diff --git a/docs/integrations/dnd-spells.md b/docs/integrations/dnd-spells.md index cd67169..abf441e 100644 --- a/docs/integrations/dnd-spells.md +++ b/docs/integrations/dnd-spells.md @@ -105,8 +105,8 @@ artifacts: ## Limits -This checkpoint implements only D&D spell-cast extraction. It does not -implement: +The current implementation covers only D&D spell-cast extraction. It does not +yet implement: - item extraction; - NPC extraction; diff --git a/docs/roadmap/1-core-contracts-and-skeleton.md b/docs/roadmap/1-core-contracts-and-skeleton.md deleted file mode 100644 index 4b4ff45..0000000 --- a/docs/roadmap/1-core-contracts-and-skeleton.md +++ /dev/null @@ -1,70 +0,0 @@ -# Checkpoint 1: Core Contracts And Skeleton - -## Status - -This document describes planned work, not implemented behavior. - -## Goal - -Define the stable vocabulary and core interfaces that adapters, extractors, -validators, and runners will build against. - -This checkpoint should produce a compileable Go repository with a minimal CLI -shell and contract-level tests. It does not need to process real input or -produce useful artifacts. - -## Scope - -In scope: - -- Go module bootstrap; -- executable entrypoint; -- minimal CLI package; -- core source, artifact, manifest, and contract types; -- fake implementation tests proving the interfaces are usable. - -Out of scope: - -- real input modules; -- real extract modules; -- LLM provider calls; -- prompt or response-schema assets; -- diagnostics run directory; -- production config loading. - -## Target End State - -The repository should contain a compileable Go application shell and stable core -contract packages: - -- `cmd/notarius` provides the executable entrypoint. -- `internal/cli` provides a minimal CLI shell. -- `internal/core/source` defines generic source documents, source units, source - references, and source validation helpers. -- `internal/core/artifacts` defines extractor-neutral artifact candidate, - approved artifact, rejected artifact, and run manifest types. -- `internal/framework/contracts` defines the adapter, extractor, validator, and - structured LLM interfaces used by later checkpoints. - -The contracts should be proven with fake implementations in tests. Those tests -should demonstrate composition without real input modules, real extract modules, -LLM provider calls, prompt assets, or diagnostics infrastructure. - -Implementation staging belongs in -[`implementation.md`](implementation.md). - -## Done Criteria - -- `go test ./...` passes. -- `go build ./cmd/notarius` passes. -- Core types and contracts exist in stable package locations. -- Tests prove fake implementations can compose at the type-contract level. -- No real Seriatim, D&D, LLM, or Audita-specific behavior has been added yet. - -## Review Questions - -- Are the interfaces small enough? -- Are source-format details absent from core packages? -- Are D&D concepts absent from framework and core packages? -- Is the shell compileable without placeholder behavior that will be hard to - unwind? diff --git a/docs/roadmap/2-framework-composition.md b/docs/roadmap/2-framework-composition.md deleted file mode 100644 index 85a4acc..0000000 --- a/docs/roadmap/2-framework-composition.md +++ /dev/null @@ -1,75 +0,0 @@ -# Checkpoint 2: Framework Composition - -## Status - -This document describes planned work, not implemented behavior. - -## Goal - -Prove the core contracts compose before adding real stage modules or portable -Audita infrastructure. - -This checkpoint should produce a minimal runner that can execute fake registered -components from source input to artifact output in tests. - -## Scope - -In scope: - -- input adapter registry; -- extractor registry; -- validator decision model; -- decision-cardinality checks; -- minimal runner; -- fake-component runner tests. - -Out of scope: - -- real input parsing; -- real input modules; -- real extract modules; -- pipeline-profile config loading; -- module capability validation; -- real LLM calls; -- prompt assets; -- response schema assets; -- diagnostics run directory; -- real D&D artifact schemas. - -## Target End State - -The repository should contain a minimal framework composition layer: - -- `internal/framework/pipeline` registers and builds input adapter constructors - and extractor constructors by stable key. -- `internal/framework/validate` provides shared validator decision helpers and - cardinality checks. -- `internal/framework/pipeline` executes configured extractors against a - `SourceDocument`, applies validator chains, and returns approved and rejected - artifacts. - -The runner should operate on already parsed source documents in this checkpoint. -Raw input parsing and concrete input module behavior remain deferred to the -Seriatim input module checkpoint. - -Pipeline-profile resolution, module metadata, and capability validation are -deferred to checkpoint 3. This checkpoint only needs constructor registries and -minimal runner composition. - -Implementation staging belongs in -[`implementation.md`](implementation.md). - -## Done Criteria - -- `go test ./...` passes. -- Fake adapter/extractor/validator registrations work in tests. -- The runner operates on `SourceDocument`, not transcript-specific structures. -- The runner does not import concrete D&D extract module packages. - -## Review Questions - -- Does the runner know only about sources, extractors, validators, and artifacts? -- Are registries simple enough to evolve? -- Are validation decisions expressive enough for deterministic and LLM-backed - validators? -- Is any domain-specific behavior creeping into framework packages? diff --git a/docs/roadmap/3-pipeline-stages-chunking-merge-normalize.md b/docs/roadmap/3-pipeline-stages-chunking-merge-normalize.md deleted file mode 100644 index 78c05f7..0000000 --- a/docs/roadmap/3-pipeline-stages-chunking-merge-normalize.md +++ /dev/null @@ -1,181 +0,0 @@ -# Checkpoint 3: Pipeline Stages, Chunking, Merge, And Normalize - -## Status - -This document describes planned work, not implemented behavior. - -## Goal - -Make Notarius's application workflow first-class before adding real input -modules or extract modules. - -The workflow should be: - -```text -input -> chunk -> extract -> merge -> normalize -> output -``` - -Checkpoint 3 should define the contracts and minimal fake-tested framework -behavior for chunking, per-chunk extraction, merging, and normalization. It -should also introduce a fixture-driven walking skeleton that exercises the full -stage sequence with fake modules and a fake LLM client. It should not add real -input modules, real domain extract modules, LLM provider code, or production -output modules. - -## Scope - -In scope: - -- source chunk model; -- chunker contract; -- extract-stage contract for extractors operating on chunks; -- merge-stage contract; -- normalize-stage contract; -- output-stage contract and fake output encoder for pipeline completeness; -- resolved pipeline definition types for a fixed-shape pipeline template; -- module binding and module metadata types, including flat capability strings; -- default application for `chunk`, lane `merge`, lane `normalize`, `output`, - and `llm_profile`; -- lane selection behavior equivalent to future `--only`; -- runner/pipeline updates that exercise these stages with fake components; -- generic append/chronological merge behavior for artifact candidates when - appropriate; -- fixture-driven walking skeleton test for - `input -> chunk -> extract -> merge -> normalize -> output`; -- fake `StructuredLLMClient` wired through a trivial extractor. - -Out of scope: - -- Seriatim parsing; -- D&D spell extraction; -- LLM provider calls; -- prompt assets; -- response schema assets; -- diagnostics run directory; -- production config file loading; -- real CLI command behavior; -- production output serialization or durable output writing. - -## Target End State - -The repository should contain explicit pipeline-stage contracts: - -- `InputAdapter`: external source input to `SourceDocument`. -- `Chunker`: `SourceDocument` to ordered `SourceChunk` values. -- `Extractor`: `SourceChunk` to artifact candidates. -- `Merger`: per-chunk candidates to merged candidates. -- `Normalizer`: merged candidates to normalized candidates. -- `OutputEncoder`: final artifact bundle to bytes. - -The repository should also contain a resolved pipeline model that represents: - -- `pipeline_id`; -- shared input binding; -- shared chunk binding; -- selected artifact lanes; -- lane extract, merge, normalize, and validator bindings; -- output binding; -- resolved defaults; -- resolved pipeline digest input. - -The runner should orchestrate fake implementations through chunk, extract, -merge, normalize, and approval/validation behavior in tests. - -The checkpoint should include a fixture-driven walking skeleton that starts from -fixture input bytes and ends at encoded output bytes. The walking skeleton should -exercise the stage contracts, resolved pipeline model, module metadata, -capability validation, defaults, lane selection, and fake LLM client wiring. It -is contract coverage, not useful user-facing behavior. - -## Design Intent - -Chunking is a core application concern because many source documents, especially -transcripts, will be too large for a single LLM extraction pass. - -Chunk processing may be serial or parallel depending on extractor needs. The -architecture should support both, while checkpoint 3 may execute -deterministically in series until a later checkpoint introduces concurrency. - -Merge and normalize are separate stages: - -- merge combines per-chunk extracted candidates into one stream or collection; -- normalize reconciles the merged output by checking duplicates, consistency, - ordering, identity resolution, or other cross-chunk concerns. - -For some artifact types, merge may be generic append-in-source-order behavior. -For other artifact types, merge may be domain-specific. Normalization is where -deduplication and consistency checks should live. - -## Processing Modes - -The architecture should leave room for extractor-level processing modes: - -- whole-document processing; -- serial chunk processing; -- parallel chunk processing. - -Checkpoint 3 may execute chunks serially for deterministic behavior. -The contracts should not bake in a single-pass assumption or prevent later -parallel execution. - -## Generic Merge Behavior - -A generic merger should be able to concatenate candidates in deterministic -chunk order and candidate order. This is likely sufficient for early spell-cast -extraction, where chronological serialization is useful. - -Domain-specific mergers may be added later when generic ordering is not enough. - -## Generic Normalize Behavior - -A no-op normalizer should be available as the default. - -Domain-specific normalizers may later: - -- deduplicate repeated extracted facts; -- resolve aliases; -- reconcile conflicting candidate fields; -- enforce chronological or source-reference consistency; -- attach normalization warnings. - -## Walking Skeleton - -The fixture-driven skeleton should prove the staged architecture continuously as -new contracts are added. It should remain deliberately small and use fake modules -only. It should validate module keys and flat capability requirements before -execution, using registry metadata rather than constructing modules. Capability -values should remain simple strings. - -Implementation staging for the walking skeleton belongs in -[`implementation.md`](implementation.md). - -## Done Criteria - -- `go test ./...` passes. -- Pipeline-stage contracts are explicit and source/domain agnostic. -- A resolved pipeline profile model exists for fixed-shape pipeline templates. -- Pipeline defaults and lane selection are covered by fake tests. -- Module capability validation is covered by fake tests. -- Fake tests prove input source documents can be chunked, extracted, merged, and - normalized. -- A fixture-driven walking skeleton proves fake input, chunk, extract, merge, - normalize, and output modules compose end to end with a fake LLM client. -- Merge and normalize are distinct concepts in code and tests. -- The runner no longer implies whole-document-only extraction as the core - application model. -- No concrete input module, domain extract module, LLM provider, prompt, - response schema, diagnostics, config, or D&D artifact code is added. - -## Review Questions - -- Is the workflow clearly represented as input, chunk, extract, merge, - normalize, and output? -- Are merge and normalize cleanly separated? -- Does the resolved pipeline model avoid becoming a general-purpose workflow - engine? -- Are module capabilities simple flat strings? -- Does lane selection avoid creating ad hoc pipelines? -- Can a generic merger handle simple chronological artifact streams? -- Can a later domain-specific normalizer handle duplicates and consistency - without changing core runner contracts? -- Does the design allow serial and parallel chunk processing later? diff --git a/docs/roadmap/4-portable-audita-infrastructure.md b/docs/roadmap/4-portable-audita-infrastructure.md deleted file mode 100644 index 595fa57..0000000 --- a/docs/roadmap/4-portable-audita-infrastructure.md +++ /dev/null @@ -1,114 +0,0 @@ -# Checkpoint 4: Portable Audita Infrastructure - -## Status - -This document describes planned work, not implemented behavior. - -## Goal - -Port or adapt reusable Audita infrastructure that directly supports Notarius -contracts while avoiding Audita's transcript-correction model. - -This checkpoint should add reusable runtime plumbing, not real extraction -behavior. - -## Scope - -In scope: - -- structured LLM client interface implementation; -- LLM scheduler; -- prompt registry pattern; -- response-schema registry pattern; -- diagnostics run directory pattern; -- config loading and validation for named pipeline profiles and implemented - runtime pieces. - -Out of scope: - -- correction proposals; -- replacement policies; -- transcript mutation; -- correction ledger terminology; -- Audita module or validator behavior; -- real D&D prompts or schemas; -- domain-specific prompt or schema assets; -- embedded built-in pipeline profiles. - -## Target End State - -The repository should contain reusable runtime infrastructure adapted from -Audita where it directly supports Notarius contracts: - -- an OpenAI-compatible structured-output LLM client behind the existing - `StructuredLLMClient` interface; -- an LLM scheduler for bounded concurrency; -- an embedded response-schema registry pattern; -- an embedded prompt registry pattern; -- a diagnostics run directory pattern using extraction-oriented artifact names; -- config structs, loading, defaults, redaction, and validation for named - pipeline profiles. - -Framework code should remain source-agnostic and domain-agnostic. Provider HTTP -details should stay inside the LLM runtime package. Prompt and schema registries -should use placeholder/test assets until real extractor prompts and schemas are -introduced by later checkpoints. - -Config should support: - -- `llm_profiles`; -- `pipelines..input`; -- `pipelines..chunk`; -- `pipelines..artifacts..extract`; -- lane `merge`, `normalize`, and validator settings; -- output module selection; -- inline module-binding object form and string shorthand; -- default `chunk`, `merge`, `normalize`, `output`, and `llm_profile`; -- selected pipeline ID and lane filtering for runtime use; -- concurrency; -- work directory; -- diagnostics retention. - -Config loading should support the standard precedence model: - -1. built-in defaults -2. configuration file -3. environment variables -4. CLI flags - -Structural module selection should come from pipeline config. CLI flags may -override operational settings and artifact lane selection, but should not offer -ad hoc `--extractor` or `--chunker` wiring. - -Config validation should fail fast for unknown pipeline IDs, unknown module -keys, missing required slots, missing capabilities, unknown LLM profiles, empty -artifact-lane sets, and invalid lane selections. - -If the CLI shell is ready, the checkpoint should expose discovery/validation -commands for config and pipeline profiles: - -```sh -notarius config validate -notarius pipelines list -``` - -Implementation staging belongs in [`implementation.md`](implementation.md). - -## Done Criteria - -- `go test ./...` passes. -- Audita runtime pieces are adapted to Notarius package names and contracts. -- No correction proposal, replacement policy, transcript mutation, or correction - ledger code has been copied. -- Runtime tests cover secret redaction, schema registry lookup, prompt metadata, - and scheduler behavior where applicable. -- Config tests cover named pipeline profiles, defaults, lane selection, - capability validation, and resolved pipeline digesting. - -## Review Questions - -- Did we copy only reusable infrastructure? -- Do provider-specific types stay behind adapter/runtime boundaries? -- Are diagnostics names and report concepts extraction-oriented? -- Is config limited to named pipeline profiles and implemented behavior? -- Are structural pipeline changes kept out of ad hoc CLI flags? diff --git a/docs/roadmap/5-seriatim-input-module.md b/docs/roadmap/5-seriatim-input-module.md deleted file mode 100644 index 44e048e..0000000 --- a/docs/roadmap/5-seriatim-input-module.md +++ /dev/null @@ -1,107 +0,0 @@ -# Checkpoint 5: Seriatim Input Module - -## Status - -This document describes planned work, not implemented behavior. - -## Goal - -Add the first real input source while keeping transcript-specific behavior -isolated inside an input-stage module. - -This checkpoint should allow Seriatim minimal transcript JSON to become a -generic `SourceDocument`. - -## Scope - -In scope: - -- `internal/modules/input/seriatim`; -- parser for Seriatim minimal output JSON; -- mapping into `SourceDocument` and `SourceUnit`; -- source-document validation; -- input adapter registry wiring; -- module metadata/capabilities for pipeline validation; -- fixtures and tests; -- config compatibility through named pipeline profiles. - -Out of scope: - -- D&D extraction; -- LLM extraction calls; -- transcript-specific behavior in runner/core packages; -- support for every possible Seriatim schema variant. - -## Target End State - -The repository should contain a real Seriatim input-stage module that translates -Seriatim minimal transcript JSON into the generic source model. - -The Seriatim module should be registered under the stable input adapter key -`seriatim`. It should be selectable through the existing input adapter registry -and through pipeline-profile resolution when a profile binds `input: seriatim`. - -The module should accept the Seriatim minimal output shape: - -- top-level `metadata`; -- top-level `segments`; -- segment `id`; -- segment `start`; -- segment `end`; -- segment `speaker`; -- segment `text`. - -The module should map Seriatim data into generic source values: - -- segment `id` becomes `SourceUnit.ID`; -- segment `text` becomes `SourceUnit.Text`; -- the document and unit kind strings identify transcript-like source material - without adding transcript-specific fields or types to core packages; -- `speaker`, `start`, and `end` become source-unit metadata; -- top-level Seriatim metadata becomes source-document metadata; -- the resulting source document passes core source validation. - -The module should reject invalid Seriatim input with clear module-specific -errors. Validation should cover: - -- valid JSON; -- required top-level metadata and segments; -- required segment fields; -- unique segment IDs; -- non-empty segment text; -- valid start and end values. - -The module should declare flat capabilities for pipeline validation. Initial -capabilities should describe transcript-oriented source properties preserved by -the adapter, including speaker and timestamp metadata. - -Implementation staging belongs in -[`implementation.md`](implementation.md). - -## Fixtures And Tests - -The checkpoint should add synthetic fixtures and focused tests for: - -- valid Seriatim minimal transcript; -- malformed JSON; -- missing metadata; -- missing or duplicate segment IDs; -- empty segment text; -- source-reference compatibility with generated unit IDs. - -## Done Criteria - -- `go test ./...` passes. -- Seriatim minimal transcript JSON maps into `SourceDocument`. -- Transcript fields do not appear in core runner contracts. -- The input module is selectable through the registry and pipeline-profile - configuration when config support exists. -- The input module declares capabilities needed for pipeline validation. -- Tests prove transcript-specific assumptions are isolated to the input module. - -## Review Questions - -- Are segment, speaker, and timestamp assumptions contained inside the input module? -- Are unit IDs stable and suitable for source references? -- Does the input module preserve enough metadata for transcript-oriented output later? -- Should the input module accept only Seriatim minimal output for now? diff --git a/docs/roadmap/6-dnd-spells-extractor.md b/docs/roadmap/6-dnd-spells-extractor.md deleted file mode 100644 index 17dc60c..0000000 --- a/docs/roadmap/6-dnd-spells-extractor.md +++ /dev/null @@ -1,120 +0,0 @@ -# Checkpoint 6: D&D Spells Extractor - -## Status - -This document records the target scope for checkpoint 6. The implemented -integration contract is documented in -[`docs/integrations/dnd-spells.md`](../integrations/dnd-spells.md). - -## Goal - -Implement the first useful extract-stage module: D&D spell casts from a -Seriatim transcript source document. - -This checkpoint should produce the first meaningful vertical slice from real -source input to validated artifact output. - -## Scope - -In scope: - -- D&D spell artifact schema and Go structs; -- structured response schema asset; -- prompt assets; -- `internal/modules/extract/dnd/spells`; -- module metadata/capability requirements for pipeline validation; -- source-reference and schema validators in the extractor chain; -- fake LLM tests; -- runner-level integration tests with fake infrastructure. - -Out of scope: - -- D&D item extraction; -- NPC extraction; -- combat extraction; -- cross-slice deduplication beyond simple deterministic merging; -- broad D&D rules validation; -- a `notarius run` command. - -## Target End State - -The repository should contain a real D&D spells extract-stage module at -`internal/modules/extract/dnd/spells`. - -The spells module should be registered under the stable extractor key -`dnd/spells`. It should be selectable as a named artifact lane in pipeline -configuration, for example a lane named `spells` whose extractor module is -`dnd/spells`. - -The module should translate generic source chunks into spell-cast artifact -candidates with this spell payload: - -- `caster`; -- `spell`; -- `effect`; -- `narrative_description`. - -The LLM structured response should also include source references for each -spell cast. The extractor should copy those references into the generic -artifact envelope rather than duplicating source references inside the durable -spell payload. - -The caster is the in-world character or creature casting the spell, not the -table speaker. Speaker metadata from transcript source units may be used as -optional prompt context when present, but it should not be a required or durable -field in the spell payload. - -Prompts should: - -- describe the generic source-unit input format; -- explain that source references must use source-unit IDs exactly; -- avoid relying on transcript-specific fields except as optional metadata; -- request only D&D spell-cast artifacts. - -The extractor should attach deterministic validators by default. Validation -should cover: - -- spell payload shape; -- non-empty required spell fields; -- at least one source reference per spell cast; -- source references that validate against the source document. - -The module should declare flat capabilities for pipeline validation. Initial -capabilities should require chunked transcript source material and provide a -D&D spell-cast artifact capability. - -Implementation staging belongs in -[`implementation.md`](implementation.md). - -## Fixtures And Tests - -The checkpoint should add synthetic fixtures and focused tests for: - -- successful spell extraction with a fake structured LLM client; -- empty spell-cast results; -- malformed structured output handling; -- invalid source-reference rejection; -- stable output ordering; -- pipeline-profile selection of a `spells` artifact lane; -- runner integration from Seriatim input through the spells extractor using - fake chunk and output modules. - -## Done Criteria - -- `go test ./...` passes. -- Seriatim input can flow through the runner into the D&D spells extractor. -- Spell artifacts include valid source references. -- D&D concepts are contained in extract module/artifact packages and docs. -- The spells module can be selected as a named artifact lane in pipeline - configuration. -- The first meaningful vertical slice is available through tests. -- No CLI `run` behavior is documented or implemented until the CLI path exists. - -## Review Questions - -- Is the spell extract module domain-specific without making the framework - D&D-specific? -- Are source references valid and useful for downstream validation? -- Is prompt/schema ownership clear? -- Does this vertical slice reveal contract changes needed before adding items, - NPCs, or combat? diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index e73a2e5..e89f3be 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,604 +1,888 @@ -# Implementation Plan: Checkpoint 6 D&D Spells Extractor +# Implementation Plan: MVP ## Status -This is a staged implementation plan for -[`6-dnd-spells-extractor.md`](6-dnd-spells-extractor.md). It is intended for an -LLM coding agent to follow stage by stage. +This is the staged implementation plan for the active MVP roadmap: +[`mvp.md`](mvp.md). -This plan implements only checkpoint 6. Do not add D&D item, NPC, combat, or -encounter extraction; do not add broad D&D rules validation; do not add a -`notarius run` command; and do not make core framework packages D&D-specific. +The target audience is an LLM coding agent. Implement the stages in order. +Each stage should leave the repository compiling and tested. Do not skip ahead +to later stages unless the current stage's done criteria are satisfied. ## Policy Context Follow: -- [`docs/policy/architecture.md`](../policy/architecture.md) -- [`docs/policy/documentation.md`](../policy/documentation.md) +- [`../policy/architecture.md`](../policy/architecture.md) +- [`../policy/documentation.md`](../policy/documentation.md) +- [`mvp.md`](mvp.md) +- [`initial-architecture.md`](initial-architecture.md) Required boundaries: -- keep D&D-specific artifact semantics, prompt data shaping, and response - interpretation inside `internal/modules/extract/dnd/spells`; -- keep source-format details inside input modules and do not depend on concrete - Seriatim package types from the spells extractor; -- keep core source, runner, pipeline, LLM, prompt, config, and validator - packages source-agnostic and domain-agnostic; -- use embedded prompt and schema assets instead of inline prompt/schema strings; -- register the extractor through the existing extractor registry; -- use flat capability strings in module metadata; -- keep future or planned behavior in `docs/roadmap/` until implemented. +- framework packages must remain source-agnostic and domain-agnostic; +- source-format behavior belongs in input modules; +- D&D spell behavior, prompt assets, response schema assets, and stable + prompt/schema identifiers belong in `internal/modules/extract/dnd/spells`; +- stage business logic belongs under `internal/modules//...` unless it + is genuinely tiny shared framework plumbing; +- structural pipeline selection must remain config-driven; +- `--only` may select artifact lanes but must not alter pipeline structure; +- output-stage warnings are out-of-band from artifact payloads and must be + available to CLI/diagnostics; +- keep planned documentation in `docs/roadmap/` until MVP behavior exists. ## Global Implementation Decisions -- Add no new third-party dependency. -- Use `dnd/spells` as the stable extractor module key. -- Put concrete extractor code under `internal/modules/extract/dnd/spells`. -- Use `dnd.spell_cast` as the artifact type and `v1` as the schema version. -- Use `dnd.spell_casts` as the extractor-provided capability. -- The extractor module spec must require `chunks` and `source.transcript`. - It must not require `transcript.speaker` or `transcript.timestamps`; those - metadata values may be included in prompts when present, but extraction must - not depend on Seriatim-specific helper APIs. -- Keep durable artifact payloads focused on spell data: +- Add no new third-party dependencies. +- Keep YAML config version `1` unless a user-visible config syntax change is + unavoidable. New module options can use existing binding `options`. +- Use the existing six-stage workflow: + `input -> chunk -> extract -> merge -> normalize -> output`. +- Use production CLI wiring in `internal/cli` for the MVP instead of adding a + new app package. The CLI may compose modules, but it must not own module + business logic. +- Keep `notarius run` serial over chunks for the MVP. The contracts and LLM + scheduler should still permit later parallel execution. +- Use one effective LLM profile per MVP run. The current runner accepts one + `StructuredLLMClient`, so a selected pipeline with multiple distinct effective + LLM profile IDs should fail clearly until multi-client runtime support is + intentionally added. +- Use the existing OpenAI-compatible client for real runs. +- Add a scheduled LLM client wrapper so every structured completion passes + through the configured scheduler. +- Use the existing `seriatim` input module and `dnd/spells` extractor module. +- Implement production default modules with these keys: + - `generic` chunker; + - `appendorder` merger; + - `noop` normalizer; + - `json` output encoder. +- Put production default modules under: + - `internal/modules/chunk/generic`; + - `internal/modules/merge/appendorder`; + - `internal/modules/normalize/noop`; + - `internal/modules/output/json`. +- The output encoder should return logical output files; the CLI/application + layer should write those files to disk. Encoders should not own filesystem + side effects. +- Use an output directory per run. The MVP default output root should be + `./notarius-output`, overrideable by `--output-dir`. +- File writes for durable output should be atomic where practical: write to a + temporary file in the target directory, then rename. +- Use synthetic fixtures only. Do not add private campaign transcript content, + real API keys, or private infrastructure values. -```go -type SpellCast struct { - Caster string `json:"caster"` - Spell string `json:"spell"` - Effect string `json:"effect"` - NarrativeDescription string `json:"narrative_description"` -} -``` - -- The LLM response model must include source references so the extractor can - populate `artifacts.ArtifactCandidate.SourceRefs`, but source references must - live in the generic artifact envelope for durable pipeline output rather than - being duplicated inside the `SpellCast` payload. -- `caster` means the in-world character or creature casting the spell. Do not - use table speaker/player as the durable caster field. Source-unit `speaker` - metadata may be rendered as optional prompt context when present. -- Use `source.SourceRef` JSON field names exactly as the core type defines - them: `source_id`, `start_unit_id`, and `end_unit_id`. -- Trim text fields before marshaling candidate payloads. Do not silently trim - source-reference IDs; invalid source refs must be rejected by validation. -- `Extractor.Validators()` must return module-owned deterministic validators by - default. Do not require pipeline profiles to configure validators explicitly - for the default checkpoint behavior. -- Treat an empty `spell_casts` response array as a successful empty extraction. - Treat a missing or null `spell_casts` field as malformed structured output. -- Use synthetic D&D transcript fixture text only. Do not include private - campaign transcript content. - -## Stage 1: Domain Model, Module Skeleton, And Registry Metadata +## Stage 1: Move D&D Prompt And Schema Assets Into The Spells Module ### Goal -Create the D&D spells extractor package, define the artifact and response -models, and make the extractor discoverable through the existing extractor -registry without calling an LLM yet. +Restore the intended framework/domain boundary before building additional MVP +functionality. -### Files To Add Or Update +`internal/framework/llm` and `internal/framework/prompt` should provide generic +asset loading, metadata, and rendering primitives. They must not define +D&D-specific prompt IDs, response schema keys, asset paths, or tests. -- `internal/modules/extract/dnd/spells/extractor.go` -- `internal/modules/extract/dnd/spells/model.go` -- `internal/modules/extract/dnd/spells/registry_test.go` +### Files To Update Or Move -### Required API +Expected files: -Add: - -```go -package spells - -const Key = "dnd/spells" -const ArtifactType = "dnd.spell_cast" -const SchemaVersion = "v1" - -type SpellCast struct { - Caster string `json:"caster"` - Spell string `json:"spell"` - Effect string `json:"effect"` - NarrativeDescription string `json:"narrative_description"` -} - -type Extractor struct{} - -func New() *Extractor -func (e *Extractor) Key() string -func (e *Extractor) ArtifactType() string -func (e *Extractor) SchemaVersion() string -func (e *Extractor) Validators() []contracts.Validator -func (e *Extractor) Extract(ctx context.Context, req contracts.ExtractionRequest) (contracts.ExtractionResult, error) -func ModuleSpec() pipeline.ModuleSpec -func Register(registry *pipeline.ExtractorRegistry) error -``` - -Add unexported response structs in the same package: - -```go -type extractionResponse struct { - SpellCasts []spellCastResponse `json:"spell_casts"` -} - -type spellCastResponse struct { - Caster string `json:"caster"` - Spell string `json:"spell"` - Effect string `json:"effect"` - NarrativeDescription string `json:"narrative_description"` - SourceRefs []source.SourceRef `json:"source_refs"` -} -``` - -### Required Behavior - -- `New()` returns a non-nil extractor. -- `ModuleSpec()` returns defensive slices with stage `pipeline.StageExtract`, - key `dnd/spells`, requires `chunks` and `source.transcript`, and provides - `dnd.spell_casts`. -- `Register()` calls `ExtractorRegistry.RegisterWithSpec(ModuleSpec(), ...)`. -- `Register(nil)` returns an error from the registry path rather than panicking. -- `Extract()` may return a clear not-yet-implemented error in this stage only. -- `Validators()` returns nil in this stage only; later stages must replace it - with the real validator chain. - -### Required Tests - -- `New()` returns an extractor whose key, artifact type, and schema version - match the constants. -- `ModuleSpec()` uses extract stage and declares the required capabilities. -- Caller mutation of `ModuleSpec().Provides` or `ModuleSpec().Requires` does not - affect later calls. -- `Register()` makes the extractor buildable from an `ExtractorRegistry`. -- Registry lookup returns the spells module spec. -- `Register(nil)` returns an error containing extractor registry context. - -### Validation - -Run: - -```sh -gofmt -w internal/modules/extract/dnd/spells -go test ./internal/modules/extract/dnd/spells -go test ./... -``` - -## Stage 2: Structured Response Schema Asset - -### Goal - -Add the structured response schema used by the D&D spells extractor and register -it through the existing LLM schema registry. - -### Files To Add Or Update - -- `internal/framework/llm/assets/schemas/dnd_spells.v1.json` - `internal/framework/llm/schema_registry.go` - `internal/framework/llm/schema_registry_test.go` +- `internal/framework/llm/assets/schemas/dnd_spells.v1.json` +- `internal/framework/prompt/registry.go` +- `internal/framework/prompt/render.go` +- `internal/framework/prompt/render_test.go` +- `internal/framework/prompt/assets/dnd/spells/system.md` +- `internal/framework/prompt/assets/dnd/spells/user.md` +- `internal/modules/extract/dnd/spells/extractor.go` +- `internal/modules/extract/dnd/spells/prompt.go` +- `internal/modules/extract/dnd/spells/prompt_test.go` - `internal/modules/extract/dnd/spells/schema_test.go` +- `docs/integrations/dnd-spells.md`, only if prompt/schema ownership text needs + to be corrected. -### Schema Decisions +### Required Design -Register: +Refactor `internal/framework/llm` so it can load schemas from caller-owned +embedded files. -- response schema key: `dnd_spells` -- schema ID: `notarius.dnd.spells` -- schema version: `v1` -- response schema name: `notarius_dnd_spells_v1` -- asset path: `assets/schemas/dnd_spells.v1.json` +Add or expose a generic constructor similar to: -The JSON schema must require a top-level object: +```go +func LoadResponseSchema(fsys fs.FS, def ResponseSchemaDefinition) (ResponseSchema, error) +``` -```json -{ - "spell_casts": [ - { - "caster": "Aria", - "spell": "Cure Wounds", - "effect": "Heals an injured ally.", - "narrative_description": "Aria casts Cure Wounds after the fight.", - "source_refs": [ - { - "source_id": "session-alpha", - "start_unit_id": "seg-001", - "end_unit_id": "seg-002" - } - ] - } - ] +where `ResponseSchemaDefinition` carries: + +- key; +- ID; +- version; +- name; +- asset path. + +The existing framework registry may keep test schemas, but it must not include +`DNDSpellsSchemaKey` or `dnd_spells.v1.json`. + +Refactor `internal/framework/prompt` so it can compile/render prompt pairs from +caller-owned embedded files. + +Add or expose a generic constructor/rendering type similar to: + +```go +type Bundle struct { ... } +func LoadBundle(fsys fs.FS, def Definition) (*Bundle, error) +func (b *Bundle) RenderUserSystem(data any) (system string, user string, metadata Metadata, err error) +``` + +The framework prompt package may continue to own shared hardening text if that +is useful, but it must not include `DNDSpellsPromptID` or D&D prompt paths. + +Move D&D prompt and schema assets under `internal/modules/extract/dnd/spells`. +Recommended paths: + +```text +internal/modules/extract/dnd/spells/assets/prompts/system.md +internal/modules/extract/dnd/spells/assets/prompts/user.md +internal/modules/extract/dnd/spells/assets/schemas/dnd_spells.v1.json +``` + +The spells package should define its own stable identifiers: + +```go +const PromptID = "dnd.spells" +const ResponseSchemaKey = "dnd_spells" +const ResponseSchemaID = "notarius.dnd.spells" +const ResponseSchemaName = "notarius_dnd_spells_v1" +``` + +The spells extractor must call module-owned prompt/schema helpers and pass only +generic framework values into the LLM client. + +### Required Tests + +- Framework LLM schema tests prove test schemas still load, sort, clone, and + omit raw schema content from diagnostics. +- Framework LLM schema tests prove looking up `dnd_spells` in the framework + registry fails. +- Framework prompt tests prove test prompts still render and missing template + data still errors. +- Framework prompt tests contain no D&D prompt assertions. +- Spells package schema tests load the module-owned D&D schema and verify: + - key; + - ID; + - version; + - response schema name; + - valid JSON; + - clone/mutation safety; + - diagnostics omit raw schema content. +- Spells package prompt tests render the module-owned prompt and verify + hardening text and prompt metadata. +- Existing spells extractor tests still pass without importing framework-owned + D&D constants. + +### Validation + +Run: + +```sh +gofmt -w internal/framework/llm internal/framework/prompt internal/modules/extract/dnd/spells +go test ./internal/framework/llm ./internal/framework/prompt ./internal/modules/extract/dnd/spells +go test ./... +``` + +## Stage 2: Add MVP Manifest And Logical Output File Contracts + +### Goal + +Make output and manifest contracts capable of representing the MVP's durable +run output before implementing the production JSON encoder or CLI writing. + +### Files To Update + +Expected files: + +- `internal/framework/contracts/contracts.go` +- `internal/core/artifacts/*.go` +- `internal/framework/pipeline/runner.go` +- `internal/framework/pipeline/runner_test.go` +- `internal/framework/contracts/contracts_test.go` +- `docs/policy/architecture.md`, only if the implemented output contract + requires clarifying policy text. + +### Required Design + +Extend the output contract to support logical files: + +```go +type OutputFile struct { + Name string `json:"name"` + ContentType string `json:"content_type,omitempty"` + Bytes []byte `json:"-"` +} + +type OutputResult struct { + Files []OutputFile `json:"files,omitempty"` + Warnings []Warning `json:"warnings,omitempty"` } ``` -Required schema constraints: +Remove or stop using the single `OutputResult.Bytes` / `ContentType` path once +all tests are updated. If keeping those fields temporarily reduces churn, mark +them as legacy in comments and make the runner prefer `Files`. -- `additionalProperties: false` at every object level. -- top-level `spell_casts` is required and must be an array. -- `spell_casts` may be empty. -- each spell cast requires non-empty `caster`, `spell`, `effect`, and - `narrative_description`. -- each spell cast requires `source_refs` with `minItems: 1`. -- each source ref requires non-empty `source_id`, `start_unit_id`, and - `end_unit_id`. +Add matching fields to `pipeline.RunOutput`: + +```go +OutputFiles []contracts.OutputFile `json:"-"` +``` + +The runner should collect output-stage warnings exactly as it does now, after +calling the output encoder. + +Define safe logical file names: + +- names are slash-separated relative paths; +- names must not be empty, absolute, contain `..`, or contain `\`; +- names are validated before the runner returns them; +- file names are sorted deterministically by the encoder that creates them. + +Extend manifest data enough for MVP provenance: + +- add `RunManifest.LLMProfiles []LLMProfileManifest`; +- add `ArtifactLaneManifest.Metadata map[string]any`; +- add `RunManifest.StartedAt`, `CompletedAt`, and `RunID` population support + in the runner input/output path. + +Recommended structs: + +```go +type LLMProfileManifest struct { + ID string `json:"id"` + Provider string `json:"provider,omitempty"` + Model string `json:"model,omitempty"` +} +``` + +Add an optional metadata interface for modules: + +```go +type ManifestMetadataProvider interface { + ManifestMetadata() map[string]any +} +``` + +When a stage module implements the interface, the runner should include that +metadata in the appropriate manifest area. For the MVP, the D&D spells extractor +will use this to report prompt and response schema identifiers/hashes on its +artifact lane. + +Update `pipeline.RunInput` to accept: + +- `RunID string`; +- `StartedAt time.Time`; +- `LLMProfiles []artifacts.LLMProfileManifest`. + +The runner should set `CompletedAt` when the run finishes or fails after a +manifest has been initialized. ### Required Tests -- `llm.LookupResponseSchema(llm.DNDSpellsSchemaKey)` succeeds. -- Registered schema list remains sorted and now includes the D&D spells schema. -- The schema content is valid JSON and mutation-safe through registry lookups. -- The spells package can look up the schema key it will use during extraction. -- The schema diagnostics map omits raw schema content. +- `contracts.OutputFile` JSON shape omits bytes and includes name/content type. +- Runner rejects unsafe output file names returned by an encoder. +- Runner preserves output warnings out-of-band. +- Runner output contains logical files returned by the encoder. +- Manifest includes run ID, started/completed timestamps when supplied or + generated. +- Manifest includes LLM profile metadata supplied in `RunInput`. +- Manifest includes extractor-provided lane metadata when the extractor + implements `ManifestMetadataProvider`. ### Validation Run: ```sh -gofmt -w internal/framework/llm internal/modules/extract/dnd/spells -go test ./internal/framework/llm -go test ./internal/modules/extract/dnd/spells +gofmt -w internal/framework/contracts internal/core/artifacts internal/framework/pipeline +go test ./internal/framework/contracts ./internal/core/artifacts ./internal/framework/pipeline go test ./... ``` -## Stage 3: Prompt Assets And Prompt Rendering +## Stage 3: Implement Production Default Stage Modules ### Goal -Add embedded prompt assets for D&D spell extraction and render deterministic -system/user messages from generic source chunks. +Make pipeline defaults real production modules instead of test-only fakes or +framework-only helpers. ### Files To Add Or Update -- `internal/framework/prompt/assets/dnd/spells/system.md` -- `internal/framework/prompt/assets/dnd/spells/user.md` -- `internal/framework/prompt/registry.go` -- `internal/framework/prompt/registry_test.go` -- `internal/framework/prompt/render_test.go` -- `internal/modules/extract/dnd/spells/prompt.go` -- `internal/modules/extract/dnd/spells/prompt_test.go` +Expected packages: -### Prompt Decisions +- `internal/modules/chunk/generic` +- `internal/modules/merge/appendorder` +- `internal/modules/normalize/noop` +- `internal/modules/output/json` -Register: +Expected framework cleanup: -- prompt ID: `dnd.spells` -- prompt constant: `DNDSpellsPromptID` -- prompt version: `v1` -- embedded directory: `assets/dnd/spells` +- `internal/framework/pipeline/generic_stages.go` +- pipeline tests that currently instantiate framework `AppendOrderMerger` or + `NoopNormalizer`. -The system prompt must: +### Required Design -- include the shared prompt hardening text via `{{ hardening }}`; -- identify the task as extracting D&D spell casts only; -- state that the model must not infer spells not supported by the provided - source text; -- state that source references must use source-unit IDs exactly as provided. +#### `generic` chunker -The user prompt must render: +Package: `internal/modules/chunk/generic` -- source document ID; -- chunk ID and chunk index when a chunk is present; -- source units in their existing order; -- each source unit's ID and text; -- selected metadata only as optional context, using generic labels. Include - `speaker`, `start`, and `end` when present without importing Seriatim helper - APIs. +Key: `generic` -Add a module-local prompt data builder that accepts -`contracts.ExtractionRequest` and returns template data. It must not mutate the -request, source document, chunk, units, or metadata maps. +Module spec: + +- stage: `chunk`; +- requires: `source.transcript` is **not** required; +- provides: `chunks`. + +Behavior: + +- accepts any valid `SourceDocument`; +- preserves source-unit order; +- returns stable chunk IDs: `chunk-000001`, `chunk-000002`, and so on; +- copies source units defensively; +- adds chunk metadata: + - `start_unit_id`; + - `end_unit_id`; + - `unit_count`. + +Options: + +- `max_units`: positive integer, default `50`; +- `overlap_units`: non-negative integer, default `0`, must be less than + `max_units`. + +If the source has no units, return a clear error. If options have the wrong type +or invalid values, return a clear module-specific error. + +#### `appendorder` merger + +Package: `internal/modules/merge/appendorder` + +Key: `appendorder` + +Module spec: + +- stage: `merge`; +- requires: no artifact-type-specific capability; +- provides: `merged`. + +Behavior: + +- preserves chunk order as provided by the runner; +- preserves candidate order within each chunk; +- defensively copies candidates, payloads, source refs, and metadata; +- does not merge, deduplicate, or rewrite source references. + +#### `noop` normalizer + +Package: `internal/modules/normalize/noop` + +Key: `noop` + +Module spec: + +- stage: `normalize`; +- requires: `merged`; +- provides: `normalized`. + +Behavior: + +- defensively copies candidates; +- does not deduplicate, rewrite, or validate domain content. + +#### `json` output encoder + +Package: `internal/modules/output/json` + +Key: `json` + +Module spec: + +- stage: `output`; +- requires: `normalized`; +- provides: `encoded`. + +Behavior: + +- returns logical output files: + - `index.json`; + - `manifest.json`; + - `artifacts/.json` for each approved artifact type; + - `rejected.json`; + - `warnings.json`. +- groups approved artifacts by `Artifact.ArtifactType`; +- sorts artifact-type file names by artifact type; +- preserves artifact order within each artifact type according to runner order; +- pretty-prints JSON with two-space indentation and trailing newline; +- uses content type `application/json`; +- includes rejected artifacts and warnings even when the arrays are empty; +- does not include output warnings inside artifact payloads. + +File-name safety: + +- artifact type may contain dots and hyphens; +- replace any character outside `[A-Za-z0-9._-]` with `_` for artifact file + names; +- if sanitization produces an empty name, return an error. ### Required Tests -- Prompt metadata lookup succeeds for `dnd.spells`. -- Registered prompt metadata remains sorted. -- Rendering includes hardening text. -- Rendering includes source ID, chunk ID, unit IDs, unit text, and optional - speaker/timestamp metadata when present. -- Rendering fails clearly if required template data is missing. -- Module-local prompt data construction works with a generic source chunk and - does not depend on concrete Seriatim package helpers. +- Generic chunker tests cover defaults, exact chunk boundaries, overlap, + invalid options, empty source, defensive copies, and stable IDs. +- Append-order merge tests cover ordering and defensive copies. +- Noop normalizer tests cover pass-through behavior and defensive copies. +- JSON output tests cover all logical files, grouping, sorted filenames, + rejected/warnings presence, pretty JSON, unsafe artifact type sanitization, + and no mutation of inputs. +- Pipeline config tests using defaults resolve when these module specs are + registered. ### Validation Run: ```sh -gofmt -w internal/framework/prompt internal/modules/extract/dnd/spells -go test ./internal/framework/prompt -go test ./internal/modules/extract/dnd/spells -go test ./... -``` - -## Stage 4: Extractor LLM Call And Candidate Mapping - -### Goal - -Implement `Extractor.Extract` so a source chunk can flow through prompt -rendering, structured LLM completion, and candidate creation. - -### Files To Add Or Update - -- `internal/modules/extract/dnd/spells/extractor.go` -- `internal/modules/extract/dnd/spells/model.go` -- `internal/modules/extract/dnd/spells/extractor_test.go` - -### Required Behavior - -`Extract()` must: - -- reject nil extractor, nil context, canceled context, nil source, nil chunk, - empty chunk units, and nil LLM client with clear `dnd spells` error context; -- render the `dnd.spells` prompt using the stage 3 prompt data builder; -- look up the stage 2 response schema; -- call `req.LLMClient.CompleteStructured` with: - - `StageName: Key`; - - two messages, system then user; - - `ResponseSchemaName: schema.Name`; - - `ResponseSchema: schema.JSONSchema`; -- decode into `extractionResponse`; -- reject a nil `SpellCasts` slice as malformed structured output; -- return no candidates for an empty `SpellCasts` slice; -- preserve response order when creating candidates; -- trim `caster`, `spell`, `effect`, and `narrative_description` before - marshaling the `SpellCast` payload; -- copy response source refs into `ArtifactCandidate.SourceRefs`; -- leave `ExtractorKey`, `ArtifactType`, `SchemaVersion`, and `Index` empty or - zero so the runner's existing candidate normalization remains authoritative; -- wrap LLM, prompt, schema lookup, response validation, and JSON marshal errors - with useful `dnd spells` context. - -Do not call validators from `Extract()`. Validation belongs to the runner's -validator phase. - -### Required Tests - -Use a fake `contracts.StructuredLLMClient`. - -- Successful extraction returns one candidate with expected payload fields, - source refs, response schema name, schema JSON, prompt messages, and stage - name. -- Empty `spell_casts: []` returns no candidates and no error. -- Missing or null `spell_casts` is rejected as malformed structured output. -- LLM client errors are wrapped with D&D spells context. -- Nil source, nil chunk, empty chunk units, nil LLM client, nil context, and - canceled context are rejected. -- Multiple response spell casts produce candidates in response order. -- Candidate source refs are copied so later mutation of the fake response does - not mutate returned candidates. - -### Validation - -Run: - -```sh -gofmt -w internal/modules/extract/dnd/spells -go test ./internal/modules/extract/dnd/spells -go test ./... -``` - -## Stage 5: Deterministic Validator Chain - -### Goal - -Add module-owned deterministic validators for spell payload shape, required -fields, and source-reference grounding. - -### Files To Add Or Update - -- `internal/modules/extract/dnd/spells/validator.go` -- `internal/modules/extract/dnd/spells/validator_test.go` -- `internal/modules/extract/dnd/spells/extractor.go` - -### Validator Decisions - -Add two validators: - -1. `ShapeValidator` - - `Name()` returns `dnd/spells/shape`. - - Rejects malformed JSON payloads with reason code `invalid_payload`. - - Rejects blank `caster`, `spell`, `effect`, or `narrative_description` - with reason code `missing_required_field`. - - Approves candidates with valid payload shape and required fields. - -2. `SourceRefValidator` - - `Name()` returns `dnd/spells/source_refs`. - - Rejects candidates with no source refs using reason code - `missing_source_ref`. - - Rejects any invalid source ref using reason code `invalid_source_ref` and - the `source.ValidateRef` error message. - - Approves candidates whose source refs all validate against the request - source document. - -`Extractor.Validators()` must return `ShapeValidator{}` followed by -`SourceRefValidator{}`. Return a fresh slice each time. - -Both validators must: - -- satisfy `contracts.Validator`; -- return one decision for every candidate; -- preserve candidate indexes in decisions; -- return an error, not rejection decisions, when called with nil source only if - source-reference validation cannot run. Shape validation does not require a - source document. - -### Required Tests - -- `Extractor.Validators()` returns the two validators in the required order and - is mutation-safe. -- Each validator approves a valid candidate. -- Shape validator rejects malformed payload JSON. -- Shape validator rejects each blank required field. -- Source ref validator rejects missing refs. -- Source ref validator rejects unknown source IDs, unknown unit IDs, and - reversed unit ranges. -- Both validators return one decision per candidate and preserve indexes. -- Approved decisions use `validate.Approved`; rejected decisions use the reason - codes listed above. - -### Validation - -Run: - -```sh -gofmt -w internal/modules/extract/dnd/spells -go test ./internal/modules/extract/dnd/spells -go test ./... -``` - -## Stage 6: Pipeline Resolution And Runner Integration - -### Goal - -Prove Seriatim input can flow through the existing runner into the real D&D -spells extractor, fake downstream infrastructure, and the extractor-owned -validators. - -### Files To Add Or Update - -- `internal/modules/extract/dnd/spells/config_test.go` -- `internal/modules/extract/dnd/spells/runner_test.go` -- `internal/modules/extract/dnd/spells/testdata/seriatim_spell_session.json` -- `internal/modules/extract/dnd/spells/testdata/pipeline.yml` - -### Required Test Catalog - -Build test-only catalogs and registries with: - -- Seriatim input registered through `seriatim.Register`; -- a fake chunker registered as `fake/chunk`, requiring `source.transcript` and - providing `chunks`; -- D&D spells extractor registered through `spells.Register`; -- `pipeline.AppendOrderMerger` registered as `appendorder`, requiring - `dnd.spell_casts`; -- `pipeline.NoopNormalizer` registered as `noop`; -- a fake `json` output encoder registered as output stage. - -Do not add real chunk or output modules for this checkpoint. - -### YAML Fixture - -Use a synthetic pipeline fixture: - -```yaml -version: 1 -pipelines: - dnd-spells-fixture: - input: seriatim - chunk: fake/chunk - artifacts: - spells: - extract: dnd/spells - merge: appendorder - normalize: noop - output: json -``` - -### Required Tests - -- `config.ParseFileConfigYAML` and `Config.ApplyFileConfig` load the fixture. -- `Config.Resolve` succeeds with pipeline ID `dnd-spells-fixture` and the - test-only catalog. -- The resolved artifact lane ID is `spells` and extractor module is - `dnd/spells`. -- The resolved pipeline digest is non-empty and stable across repeated - resolution. -- Removing `source.transcript` from the Seriatim module spec causes resolution - to fail with a missing capability error for `dnd/spells`. -- Removing `dnd.spell_casts` from the extractor spec causes resolution to fail - with a missing capability error for `appendorder`. -- Selecting an unknown `--only` lane still fails through existing resolution - behavior. -- Runner success path: - - parse the Seriatim fixture; - - use a fake LLM that returns at least two valid spell casts; - - assert approved artifacts preserve response order; - - assert approved payloads contain spell data; - - assert approved artifact source refs validate with `source.ValidateRef`; - - assert the manifest records input module `seriatim`, extractor - `dnd/spells`, lane `spells`, and validation status `approved`. -- Runner rejection path: - - fake LLM returns a spell cast with an invalid source ref; - - runner completes with rejected artifacts and validation status `rejected`; - - rejection contains validator `dnd/spells/source_refs` and reason code - `invalid_source_ref`. -- Runner malformed-output path: - - fake LLM returns or reports malformed structured output; - - runner returns an extraction error with D&D spells context and failed - validation status. - -### Validation - -Run: - -```sh -gofmt -w internal/modules/extract/dnd/spells -go test ./internal/modules/extract/dnd/spells -go test ./internal/modules/input/seriatim +gofmt -w internal/modules/chunk/generic internal/modules/merge/appendorder internal/modules/normalize/noop internal/modules/output/json internal/framework/pipeline +go test ./internal/modules/chunk/generic ./internal/modules/merge/appendorder ./internal/modules/normalize/noop ./internal/modules/output/json go test ./internal/framework/pipeline go test ./... ``` -## Stage 7: Documentation And Final Verification +## Stage 4: Add Production CLI Catalog And Runtime Wiring ### Goal -Document the implemented D&D spells extraction contract without describing -unimplemented extractors or a `notarius run` command. +Make implemented modules selectable by real CLI commands without test-injected +catalogs. ### Files To Add Or Update -- `docs/integrations/dnd-spells.md` -- `docs/roadmap/6-dnd-spells-extractor.md` +Expected files: -### Required Documentation +- `internal/cli/run.go` +- new `internal/cli/catalog.go` or equivalent; +- `internal/cli/run_test.go`; +- module registry tests as needed. -Create `docs/integrations/dnd-spells.md` as implemented-behavior documentation -with: +### Required Design -- module key, artifact type, schema version, prompt ID, and response schema key; -- accepted source expectations: generic source document/chunk with transcript - capability supplied by pipeline resolution; -- spell payload fields; -- source-reference behavior: LLM response includes refs, durable artifact - output carries refs in the generic artifact envelope; -- default deterministic validators and rejection reason codes; -- declared required/provided capabilities; -- note that item, NPC, combat, encounter, broad D&D rules, and CLI `run` - workflows are not implemented by this checkpoint. +Add production wiring in `internal/cli`: -Update `docs/roadmap/6-dnd-spells-extractor.md` only if implementation reveals -a real scope or policy correction. Keep implementation staging in this file, -not in the feature roadmap. +```go +func productionRegistries() (pipeline.Registries, error) +func productionCatalog() (pipeline.ModuleCatalog, error) +``` -### Final Validation +The production wiring must register: + +- input: `seriatim`; +- chunk: `generic`; +- extract: `dnd/spells`; +- merge: `appendorder`; +- normalize: `noop`; +- output: `json`. + +Keep all business logic in module packages. `internal/cli` should only compose +registries/catalogs and command behavior. + +Update `cli.Options` so tests may inject registries/catalog/runtime without +disabling production defaults unintentionally. + +Recommended option fields: + +```go +type Options struct { + Catalog pipeline.ModuleCatalog + Registries pipeline.Registries + LLMClientFactory LLMClientFactory + LookupEnv func(string) (string, bool) + Now func() time.Time +} +``` + +If `Catalog` or `Registries` is empty in normal `Run`, use production wiring. +If tests provide either, use the provided value. + +Define `LLMClientFactory` in `internal/cli` or a small local file: + +```go +type LLMClientFactory func(ctx context.Context, cfg config.Config, profileID string) (contracts.StructuredLLMClient, []artifacts.LLMProfileManifest, error) +``` + +The production factory should: + +- read the selected LLM profile from effective config; +- construct `llm.OpenAICompatibleClient`; +- construct `llm.Scheduler` using the most specific configured concurrency: + profile `max_concurrency` if set, otherwise global `concurrency.total_llm`, + otherwise `1`; +- wrap the client in a scheduled client so every completion acquires/releases a + scheduler permit; +- return manifest-safe LLM metadata with profile ID, provider, and model. + +Add a scheduled client wrapper in `internal/framework/llm` if it does not +already exist: + +```go +func NewScheduledClient(client contracts.StructuredLLMClient, scheduler *Scheduler) contracts.StructuredLLMClient +``` + +### Required CLI Behavior + +- `notarius config validate --config --pipeline ` uses the + production catalog by default. +- `notarius pipelines list --config ` still lists configured pipeline + IDs and validates config shape. +- `notarius pipelines list --config --json` remains stable. + +Do not implement `notarius run` in this stage. + +### Required Tests + +- Production catalog includes the six MVP modules and their module specs. +- `config validate --pipeline` succeeds for a real MVP config fixture using no + injected catalog. +- Unknown module keys still fail with stage/pipeline context. +- Production LLM client factory rejects missing/invalid LLM profiles with clear + errors. +- Scheduled client wrapper enforces scheduler use and propagates errors. +- Existing CLI tests using injected catalogs still pass. + +### Validation Run: ```sh -gofmt -w internal/modules/extract/dnd/spells internal/framework/llm internal/framework/prompt +gofmt -w internal/cli internal/framework/llm +go test ./internal/cli ./internal/framework/llm go test ./... +go vet ./... go build ./cmd/notarius -rm -f ./notarius ``` -### Done Criteria +## Stage 5: Implement `notarius run` Without Durable File Writing -- `go test ./...` passes. -- `go build ./cmd/notarius` passes. -- The D&D spells extractor is registered through the extractor registry. -- Pipeline-profile resolution can select a `spells` artifact lane using - extractor module `dnd/spells`. -- Seriatim minimal transcript JSON can flow through the runner into the D&D - spells extractor in tests. -- Spell artifacts include valid generic source references in the artifact - envelope. -- Invalid source references are rejected by the extractor-owned validator chain. -- D&D concepts do not appear in core runner, source, pipeline, config, prompt, - LLM, or validator contracts. -- No CLI `run` behavior is documented or implemented in this checkpoint. +### Goal + +Add the user-facing run command and prove it can drive the configured pipeline +with injected fake runtime pieces. This stage should return/run data in memory +or through test buffers, but durable file writing may be completed in Stage 6. + +### Files To Update + +Expected files: + +- `internal/cli/run.go` +- `internal/cli/run_test.go` +- `cmd/notarius/main.go`, only if command wiring requires it. + +### Required Command Shape + +Support: + +```sh +notarius run --input path/to/source.json +notarius run --input path/to/source.json --only spells +``` + +Supported flags: + +- `--config path`; +- `--input path`, required; +- `--only lane-a,lane-b`; +- `--output-dir path`, parsed and passed through metadata for Stage 6; +- `--diagnostics-dir path`, overrides config diagnostics work dir for this run; +- `--llm-profile profile-id`, operational override for MVP runs. + +Do not add flags for structural module selection, such as `--extractor`, +`--chunker`, `--merge`, or `--output`. + +### Required Behavior + +- Missing pipeline ID returns exit code `2`. +- Missing `--input` returns exit code `2`. +- Unknown flags return exit code `2`. +- Config/load/resolve/runtime failures return exit code `1`. +- Successful runs return exit code `0`. +- `--only` uses existing lane selection behavior. +- Extend `config.ResolveInput` with `LLMProfileOverride string` or an + equivalent option. When `--llm-profile` is provided, apply it to every + resolved module binding before the resolved pipeline digest is computed. This + keeps the override operational rather than structural while still making the + effective pipeline digest truthful. +- After resolution, collect the distinct effective LLM profile IDs used by the + selected pipeline. For the MVP, require exactly one distinct profile ID and + fail clearly if more than one is present. +- Input file is read as raw bytes and passed to the runner with path metadata. +- Source ID defaults to the input path or basename only if the input adapter + needs one; do not invent transcript-specific source IDs in the CLI. +- The command resolves the selected pipeline with the production catalog. +- The command constructs the LLM client through `LLMClientFactory`. +- The command invokes `pipeline.New(registries).Run(...)`. +- The command prints a concise success message that includes at least: + - pipeline ID; + - approved artifact count; + - rejected artifact count. +- If warnings exist, print a concise warning count to stderr. + +### Required Tests + +Use fake LLM/runtime injection; do not call external services. + +- Missing pipeline ID. +- Missing input flag. +- Unknown pipeline. +- Unknown `--only` lane. +- Invalid input file path. +- Successful run invokes runner path through real registries and fake LLM. +- `--only spells` runs only the selected lane. +- LLM factory failure is reported clearly. +- Validation rejection produces a failed/non-zero or successful-with-rejections + behavior according to current runner semantics. For MVP, keep runner + semantics: a run with rejected artifacts completes successfully with + `ValidationStatus` set to `rejected`, unless an error occurs. + +### Validation + +Run: + +```sh +gofmt -w internal/cli +go test ./internal/cli +go test ./... +go vet ./... +go build ./cmd/notarius +``` + +## Stage 6: Write Durable Output And Diagnostics For `notarius run` + +### Goal + +Complete the MVP run workflow by writing output files and diagnostics. + +### Files To Update + +Expected files: + +- `internal/cli/run.go` +- `internal/cli/run_test.go` +- `internal/core/diagnostics/*.go`, only if helper methods are needed. + +### Required Design + +Output directory behavior: + +- default root: `./notarius-output`; +- override: `--output-dir`; +- each run writes to `//`; +- run ID comes from diagnostics run directory when available or from a + generated UTC nanosecond timestamp using the same style as diagnostics; +- create directories with `0755`; +- write files with `0644`; +- write each file atomically where practical. + +Logical output files from `pipeline.RunOutput.OutputFiles` should be written +under the run output directory. Reject unsafe logical file names before writing: + +- empty; +- absolute; +- contains `..`; +- contains backslash; +- escapes the run output directory after path cleaning. + +Diagnostics behavior: + +- create a diagnostics run directory at command start unless retention is + `never` and the implementation can still reliably capture failures; simplest + MVP behavior is to create it and then apply retention at the end; +- write invocation metadata; +- write redacted effective config; +- write resolved pipeline; +- write run manifest; +- write warnings; +- write run report containing output path, counts, and validation status; +- write error log on failure; +- apply retention with existing diagnostics policy. + +`--diagnostics-dir` should override `Config.Diagnostics.WorkDir` after file and +environment config have been applied, without changing structural pipeline +definition or pipeline digest. + +Success output: + +- stdout includes the durable output run directory path; +- stderr includes warning count when warnings are present; +- no raw prompt text, raw API keys, or large source payloads should be printed. + +### Required Tests + +- Successful `notarius run` writes output files under a temp output directory. +- Output write rejects unsafe logical file names from a fake encoder. +- Writes are atomic enough that no temporary files remain after success. +- Diagnostics artifacts are written on success. +- Error log is written on failure after diagnostics directory creation. +- Retention `never` removes successful warning-free diagnostics directories. +- Warnings are present in diagnostics and are reported to stderr. +- `--diagnostics-dir` overrides config diagnostics directory. + +### Validation + +Run: + +```sh +gofmt -w internal/cli internal/core/diagnostics +go test ./internal/cli ./internal/core/diagnostics +go test ./... +go vet ./... +go build ./cmd/notarius +``` + +## Stage 7: Add MVP Fixtures And End-To-End Acceptance Coverage + +### Goal + +Make the MVP path continuously testable without network access. + +### Files To Add Or Update + +Expected fixtures: + +- `examples/seriatim-minimal-transcript.json`, if the example can be kept + accurate before the deferred documentation pass; +- `examples/dnd-spells.config.yml`, if config examples are tested in this + stage; +- or equivalent `internal/cli/testdata/...` fixtures if examples are deferred. + +Expected tests: + +- `internal/cli/run_test.go` +- `internal/modules/extract/dnd/spells/runner_test.go` +- config tests as needed. + +### Required Design + +Add a maintained MVP config fixture: + +```yaml +version: 1 +llm_profiles: + default: + provider: openai-compatible + base_url: http://127.0.0.1:1 + model: fake-model +pipelines: + dnd-session: + input: seriatim + chunk: + module: generic + options: + max_units: 50 + artifacts: + spells: + extract: dnd/spells +``` + +The fixture may use a fake base URL because tests should inject a fake LLM +client factory. Do not require a real network call. + +Acceptance tests should execute the public CLI entry path with: + +```sh +notarius run dnd-session --config --input --output-dir +notarius run dnd-session --config --input --only spells --output-dir +notarius config validate --config --pipeline dnd-session +notarius pipelines list --config +``` + +The fake LLM should return deterministic D&D spell output with valid source +references. The resulting output files should be parsed as JSON and checked for: + +- manifest pipeline ID and digest; +- spell artifact payload; +- source references; +- prompt/schema metadata in manifest or artifact metadata; +- validation status; +- warning behavior. + +### Required Failure Coverage + +Add fixture-driven tests for: + +- missing config; +- unknown pipeline; +- invalid Seriatim input; +- invalid `--only` lane; +- fake LLM failure; +- malformed LLM response; +- invalid source reference rejection. + +### Validation + +Run: + +```sh +gofmt -w internal/cli internal/modules/extract/dnd/spells +go test ./internal/cli ./internal/modules/extract/dnd/spells +go test ./... +go vet ./... +go build ./cmd/notarius +``` + +## Stage 8: MVP Final Review And Roadmap Cleanup + +### Goal + +Confirm the MVP is complete enough to trigger the deferred documentation pass. + +### Required Review + +Perform a code review against: + +- [`mvp.md`](mvp.md); +- [`../policy/architecture.md`](../policy/architecture.md); +- [`../policy/documentation.md`](../policy/documentation.md). + +Check specifically: + +- no D&D prompt/schema assets or constants remain in framework packages; +- production CLI commands use production wiring by default; +- `config validate --pipeline` works with the MVP fixture; +- `pipelines list` works with the MVP fixture; +- `notarius run` writes durable output and diagnostics; +- default modules resolve without test-only registration; +- output warnings remain out-of-band from artifact payloads; +- no private data or secrets appear in fixtures; +- docs outside `docs/roadmap/` describe only implemented behavior. + +### Required Validation + +Run: + +```sh +go test ./... +go vet ./... +go build ./cmd/notarius +``` + +### Required Roadmap Update + +After the MVP is implemented and reviewed: + +- update [`mvp.md`](mvp.md) to mark MVP functionality complete or reduce it to + remaining release/documentation work; +- keep the full documentation pass deferred until this review passes; +- do not tag alpha `0.1.0` until the documentation pass is complete. ## Open Questions -None. This plan chooses the checkpoint-6 behavior needed to implement the -feature without requiring additional product decisions. +None. The plan above makes the required MVP implementation choices explicitly. diff --git a/docs/roadmap/initial-architecture.md b/docs/roadmap/initial-architecture.md index 7a95705..754de0f 100644 --- a/docs/roadmap/initial-architecture.md +++ b/docs/roadmap/initial-architecture.md @@ -546,10 +546,10 @@ Reuse these architectural patterns: - validator decision cardinality and deterministic validator ordering; - CLI tests and fixture-driven integration tests. -The fixture-driven integration-test pattern should begin at checkpoint 3 with a -walking skeleton over fake modules and a fake LLM client. Later checkpoints -should replace fake pieces with real Seriatim, runtime, and D&D modules without -losing that end-to-end contract coverage. +The fixture-driven integration-test pattern should remain part of the codebase: +walking skeleton tests over fake modules and fake LLM clients should be +preserved as real Seriatim, runtime, and D&D modules are added, so the +end-to-end contract coverage is not lost. Avoid copying these Audita concepts directly: @@ -565,23 +565,13 @@ extraction-report concepts. ## Checkpoint Roadmap -The initial implementation should proceed through six coherent checkpoints. -Each checkpoint should leave the repository in a reviewable state, with the code -compiling and targeted tests covering the newly introduced contracts or behavior. +The initial six checkpoint roadmap has been implemented and retired. The +checkpoint files have been removed from `docs/roadmap/` so the active roadmap +does not compete with completed implementation history. -1. [Core Contracts And Skeleton](1-core-contracts-and-skeleton.md) -2. [Framework Composition](2-framework-composition.md) -3. [Pipeline Stages, Chunking, Merge, And Normalize](3-pipeline-stages-chunking-merge-normalize.md) -4. [Portable Audita Infrastructure](4-portable-audita-infrastructure.md) -5. [Seriatim Input Module](5-seriatim-input-module.md) -6. [D&D Spells Extractor](6-dnd-spells-extractor.md) - -The first contract-level walking skeleton should arrive at checkpoint 3: fixture -input through fake input, chunk, extract, merge, normalize, and output modules -with a fake LLM client. The first useful vertical slice should arrive at -checkpoint 6: Seriatim transcript input to validated D&D spell artifact output. -Earlier checkpoints remain contract-first and may not produce useful user output -yet. +The remaining work needed to reach the first functional MVP is tracked in +[`mvp.md`](mvp.md). Future staged implementation plans should be written to +[`implementation.md`](implementation.md) from that active MVP roadmap. ## Architecture Decisions diff --git a/docs/roadmap/mvp.md b/docs/roadmap/mvp.md new file mode 100644 index 0000000..8c937d6 --- /dev/null +++ b/docs/roadmap/mvp.md @@ -0,0 +1,249 @@ +# MVP Roadmap + +## Status + +This is the active roadmap for reaching the first functional Notarius MVP. + +The previous numbered checkpoint roadmaps have been implemented and retired. +This document captures the remaining work needed to turn the implemented +architecture into a usable MVP, with the current architectural review findings +folded in as first-class work. + +Implementation staging belongs in [`implementation.md`](implementation.md). + +## MVP Goal + +The MVP should let a user run Notarius against a Seriatim minimal transcript +JSON file, select a configured pipeline profile, extract D&D spell-cast +artifacts with an LLM-backed extractor, validate those artifacts, and write +durable JSON output plus diagnostics. + +The intended command shape is: + +```sh +notarius run dnd-session --input session-014.json +notarius run dnd-session --input session-014.json --only spells +``` + +The MVP remains intentionally narrow: + +- one production input module: `seriatim`; +- one production extract module: `dnd/spells`; +- one generic chunk module sufficient for transcript-scale processing; +- generic append-order merge; +- generic noop normalization; +- generic JSON output; +- config-driven pipeline profiles; +- OpenAI-compatible structured LLM execution through the existing LLM client. + +## MVP Work Areas + +### Framework/Domain Asset Boundaries + +This is the highest-priority remaining architecture correction. + +Framework packages must remain source-agnostic and domain-agnostic. D&D spell +prompt assets, response schema assets, prompt IDs, response schema keys, and +domain-specific prompt/schema tests should not live in `internal/framework/llm` +or `internal/framework/prompt`. + +Target state: + +- `internal/framework/llm` provides generic structured-output client, + scheduler, schema metadata, schema loading, and schema lookup/registration + primitives. +- `internal/framework/prompt` provides generic prompt metadata, prompt loading, + rendering, hardening, and lookup/registration primitives. +- `internal/modules/extract/dnd/spells` owns the D&D spell prompt assets, + response schema assets, stable prompt ID, stable response schema key, and + module-specific prompt/schema tests. +- Framework tests use placeholder/test assets only. +- The D&D spells extractor depends on generic framework APIs, not + framework-owned D&D constants. + +This work should not change the external artifact shape or module key. It is an +ownership and package-boundary correction. + +### Production Application Catalog Wiring + +The implemented modules and registries are currently exercised mostly through +tests that inject catalogs. The MVP needs a production assembly point that +builds the catalog and stage registries used by real CLI commands. + +Target state: + +- a small app-level package or CLI wiring function constructs the production + `pipeline.ModuleCatalog`; +- production wiring registers `seriatim`; +- production wiring registers `dnd/spells`; +- production wiring registers the default `generic`, `appendorder`, `noop`, and + `json` modules; +- `notarius config validate --pipeline ...` validates real configured + pipelines without test-only catalog injection; +- `notarius pipelines list` reports production-registered modules where useful + for discoverability. + +The production wiring should not move domain behavior into the CLI. The CLI may +compose modules, but module packages should continue to own their own behavior +and metadata. + +### Default Production Stage Modules + +Pipeline defaults are already part of the architecture: + +- `chunk: generic`; +- `merge: appendorder`; +- `normalize: noop`; +- `output: json`. + +The MVP should make those defaults real production modules rather than +test-only conveniences. + +Target state: + +- `generic` chunking creates ordered chunks over generic source units and is + configurable enough for transcript MVP use; +- `appendorder` merge serializes artifact candidates in deterministic source + and chunk order; +- `noop` normalize passes merged artifacts through unchanged while preserving + diagnostics; +- `json` output encodes approved artifacts, rejected artifacts, warnings, + manifest data, and relevant run metadata in a durable JSON shape; +- each default module declares module specs and capabilities compatible with + pipeline validation; +- default modules are registered by production app wiring. + +If a default module remains implemented in `internal/framework/pipeline`, its +production registration still needs to be explicit and discoverable. If its +logic grows beyond a small generic helper, move it under `internal/modules`. + +### `notarius run` + +The MVP needs a functional run command that drives the already-implemented +pipeline runner. + +Target state: + +- command shape: + +```sh +notarius run --input path/to/source.json +notarius run --input path/to/source.json --only spells +``` + +- required flags and arguments produce clear usage errors; +- `--config` selects the config file; +- `--only` filters artifact lanes without changing structural pipeline config; +- operational overrides may cover output directory, work directory, + concurrency, and LLM profile/model settings where already supported by config; +- structural stage selection remains config-driven; +- the command parses input through the configured input adapter; +- the command constructs the configured LLM client and scheduler; +- the command invokes the pipeline runner; +- the command writes durable output and diagnostics; +- failures return stable non-zero exit codes and useful error messages. + +The command should be covered by fixture-driven CLI tests with fake LLM behavior +where network calls would otherwise be required. + +### MVP Output And Diagnostics Behavior + +The MVP should produce inspectable files that are stable enough for downstream +experiments, without pretending to be a final public artifact contract. + +Target state: + +- output path behavior is deterministic and documented in code/tests; +- JSON output includes approved artifacts grouped or ordered predictably; +- each artifact includes its generic source references; +- rejected artifacts and validation decisions remain inspectable; +- output-stage warnings remain out-of-band from the durable artifact payload but + are captured for CLI reporting and diagnostics; +- run manifest data includes source digest, resolved pipeline digest, relevant + model/profile information, prompt/schema identifiers, and validation status; +- diagnostics redact secrets and include the resolved effective configuration + needed to debug a run. + +### MVP Fixtures And Acceptance Tests + +The MVP should be continuously testable without external services. + +Target state: + +- maintained Seriatim transcript fixture for the D&D spells MVP; +- maintained minimal config fixture for the MVP pipeline; +- fake LLM path for deterministic CLI and runner tests; +- config validation tests using the production catalog; +- `notarius run` fixture test from input file to output JSON; +- failure tests for missing config, unknown pipeline, invalid input, + invalid lane selection, LLM failure, and validation rejection; +- `go test ./...` is sufficient to exercise the MVP path without network + access. + +### Documentation Pass Preparation + +The full documentation pass is intentionally deferred until MVP functionality +exists. It should happen before tagging alpha `0.1.0`. + +The MVP implementation should still leave clear hooks for the documentation +rewrite: + +- command behavior should be stable enough to document in `docs/cli.md`; +- config behavior should be stable enough to document in `docs/config.md`; +- output behavior should be stable enough to document in integration docs; +- examples should be generated from or validated against maintained fixtures + where practical. + +## Out Of Scope For MVP + +- D&D item extraction; +- NPC extraction; +- combat extraction; +- D&D rules validation beyond the spell extractor's deterministic checks; +- Markdown or Obsidian input; +- cross-lane entity normalization; +- cross-chunk semantic deduplication beyond whatever a simple normalizer can + safely support; +- a general DAG or workflow engine; +- ad hoc CLI flags for structural module selection; +- release-quality documentation before the MVP behavior is implemented. + +## MVP Done Criteria + +- D&D prompt and response schema assets are owned by the D&D spells module, not + by framework packages. +- Production CLI commands use a real app catalog rather than test-injected + module catalogs. +- A config profile can bind `input: seriatim` and an artifact lane with + `extract: dnd/spells`. +- Default `generic`, `appendorder`, `noop`, and `json` modules resolve through + production wiring. +- `notarius config validate --config --pipeline ` works with the + MVP config. +- `notarius pipelines list --config ` works with the MVP config. +- `notarius run --input ` reads a Seriatim transcript, + extracts D&D spell artifacts, validates them, and writes JSON output. +- `notarius run --input --only spells` runs only the + selected artifact lane. +- The run manifest records source digest, resolved pipeline digest, LLM profile + and model, prompt/schema identifiers, and validation status. +- Output warnings are available to CLI/diagnostics without becoming artifact + payload fields. +- MVP fixture tests cover the full path without network access. +- `go test ./...`, `go vet ./...`, and `go build ./cmd/notarius` pass. + +## Deferred Documentation Pass + +After MVP behavior is implemented and before alpha `0.1.0`, complete a full +documentation pass/rewrite. That pass should move implemented behavior out of +roadmap documents and into canonical docs required by +[`../policy/documentation.md`](../policy/documentation.md), including at least: + +- `README.md`; +- `docs/cli.md`; +- `docs/config.md`; +- `docs/operations.md`, if diagnostics/run recovery behavior warrants it; +- `docs/internal/` architecture and package-boundary docs; +- `docs/integrations/` updates for Seriatim input, D&D spell artifacts, and + JSON output; +- maintained `examples/` files.