Refactor and clean up documentation
This commit is contained in:
@@ -1,216 +1,179 @@
|
||||
# Architecture
|
||||
|
||||
This document defines Notarius development policy. It is inward-facing:
|
||||
developers and LLM coding agents should use it to preserve the project's shape,
|
||||
boundaries, and invariants as the code evolves.
|
||||
This document defines the intended high-level architecture of Notarius and the
|
||||
invariants that changes must preserve. Implemented component details belong in
|
||||
[Internal Overview](../internal/overview.md) and its linked documents. The
|
||||
reasoning behind significant architectural choices belongs in
|
||||
[ADRs](../adr/).
|
||||
|
||||
Keep this document concise. It should describe durable architectural rules, not
|
||||
CLI syntax, configuration reference material, module catalogs, or roadmap items.
|
||||
## System Shape
|
||||
|
||||
## Project Shape
|
||||
Notarius is a small, dependency-light Go application for extracting structured
|
||||
artifacts from source material. It is a general extraction platform whose
|
||||
source formats, extraction domains, validation policies, LLM providers, and
|
||||
output formats are isolated behind explicit boundaries.
|
||||
|
||||
Notarius is a small, explicit, dependency-light Go application for extracting
|
||||
structured artifacts from source material using modular pipeline stages.
|
||||
|
||||
The application is contract-first but not abstraction-heavy. Add interfaces and
|
||||
extension points when they protect a real boundary:
|
||||
|
||||
- external source formats;
|
||||
- pipeline stage modules;
|
||||
- validators;
|
||||
- LLM providers and runtime plumbing;
|
||||
- output schemas and embedded assets.
|
||||
|
||||
Avoid abstractions that only anticipate hypothetical complexity. Prefer narrow
|
||||
contracts that can be exercised by tests and real modules.
|
||||
|
||||
## Core Invariants
|
||||
|
||||
The framework must remain source-agnostic and domain-agnostic.
|
||||
|
||||
Source-format details belong in input modules. Transcript-specific concepts such
|
||||
as segments, speakers, timestamps, and transcript schemas must not spread into
|
||||
runner, extractor, validator, or LLM framework code.
|
||||
|
||||
Extraction-domain details belong in domain modules. D&D-specific concepts such
|
||||
as spells, NPCs, items, combat turns, and encounters must not spread into core
|
||||
source, runner, or LLM framework packages.
|
||||
|
||||
Extracted facts should be grounded with source references. Source references
|
||||
should point to generic source units, not transcript-only structures. Framework
|
||||
code should preserve source-reference ranges exactly and should not merge or
|
||||
rewrite overlapping ranges unless a module explicitly owns that behavior.
|
||||
|
||||
The application workflow is fixed:
|
||||
The application has one fixed pipeline shape:
|
||||
|
||||
```text
|
||||
input -> chunk -> extract -> merge -> normalize -> output
|
||||
```
|
||||
|
||||
These stages should remain explicit in the architecture. Chunking, merging, and
|
||||
normalization must not be hidden inside domain extractors when they represent
|
||||
general pipeline behavior.
|
||||
Pipelines are configured compositions of this shape. They are not arbitrary
|
||||
DAGs or a general workflow language. Every stage remains explicit; general
|
||||
chunking, merging, or normalization behavior must not be hidden inside an
|
||||
extractor.
|
||||
|
||||
Pipelines are fixed-shape templates for this workflow, not arbitrary DAGs or a
|
||||
general workflow language. Module selection should be configuration- and
|
||||
registry-driven, not scattered through conditionals.
|
||||
Input and chunking are pipeline-wide. Each selected artifact lane owns its
|
||||
extract, merge, and normalize stages, and the output stage aggregates the run's
|
||||
lane outcomes.
|
||||
|
||||
## Package Boundaries
|
||||
Notarius is contract-first without being abstraction-heavy. Interfaces and
|
||||
extension points should protect demonstrated boundaries. New abstraction is not
|
||||
itself an architectural goal.
|
||||
|
||||
Prefer fewer, larger framework packages until a boundary proves itself through
|
||||
import direction, ownership, test seams, or substantial file size.
|
||||
## Package Layout And Dependency Direction
|
||||
|
||||
Core packages should contain deterministic models and policy. Framework
|
||||
packages should contain reusable orchestration and provider plumbing. Concrete
|
||||
business logic should live under stage-oriented module packages:
|
||||
| Area | Ownership |
|
||||
| --- | --- |
|
||||
| `cmd/notarius` | Executable entry point; delegates to the CLI. |
|
||||
| `internal/cli` | Application boundary, production composition, runtime setup, durable writes, and user-facing results. |
|
||||
| `internal/core` | Generic deterministic models and policy for source material, configuration, manifests, diagnostics, and workspace identity. |
|
||||
| `internal/framework` | Reusable contracts, registries, pipeline orchestration, validation mechanics, checkpoints, debug boundaries, and LLM runtime plumbing. |
|
||||
| `internal/modules` | Concrete pipeline stage behavior. |
|
||||
| `internal/validators` | Concrete approve/reject policies. |
|
||||
|
||||
```text
|
||||
internal/modules/input/...
|
||||
internal/modules/chunk/...
|
||||
internal/modules/extract/...
|
||||
internal/modules/merge/...
|
||||
internal/modules/normalize/...
|
||||
internal/modules/output/...
|
||||
```
|
||||
The CLI is the composition root and may import concrete implementations. Core
|
||||
and framework packages cooperate as generic application layers; neither may
|
||||
depend on production modules or validators. Concrete implementations may depend
|
||||
on core models and framework contracts.
|
||||
|
||||
Use short, lowercase, idiomatic Go package names. Avoid package names that repeat
|
||||
parent-stage context.
|
||||
The following dependency boundaries are mandatory:
|
||||
|
||||
Input modules translate external source formats into the core source model.
|
||||
They may know about external schema details, source-specific metadata, and
|
||||
format-specific validation rules. They should not own extraction-domain
|
||||
decisions.
|
||||
- extractors and validators do not depend on concrete input adapters;
|
||||
- provider-specific types do not cross the LLM runtime boundary;
|
||||
- external dependency types do not leak across internal package boundaries
|
||||
unless that dependency is the package's explicit contract.
|
||||
|
||||
Extract modules own artifact semantics, prompt usage, structured response schema
|
||||
selection, and domain-specific interpretation. They should depend on framework
|
||||
contracts and core source/artifact types, not concrete input module packages.
|
||||
Production validation defaults are central catalog policy, not behavior owned by
|
||||
module packages.
|
||||
Shared helpers may support demonstrated common needs, but must not move
|
||||
source-format or extraction-domain knowledge into generic framework packages.
|
||||
External dependencies require a clear correctness, security, interoperability,
|
||||
or complexity benefit.
|
||||
|
||||
Merge modules combine extracted candidates. Normalize modules reconcile merged
|
||||
candidates for semantic consistency. Generic behavior may exist for simple
|
||||
artifact types, but domain-specific behavior belongs in modules for the relevant
|
||||
stage.
|
||||
## Source And Domain Boundaries
|
||||
|
||||
Output modules serialize final artifacts and may report warnings out of band.
|
||||
CLI, diagnostics, and reporting layers are responsible for surfacing those
|
||||
warnings.
|
||||
Input modules translate external source formats into the generic source model.
|
||||
Format-specific schemas, fields, and validation remain with the input module
|
||||
and its integration contract.
|
||||
|
||||
Framework stages operate on source documents, source units, and source
|
||||
references rather than format-specific structures. A source reference identifies
|
||||
an ordered range of generic source units. Framework code preserves those ranges
|
||||
and does not merge or rewrite them unless a stage module explicitly owns that
|
||||
behavior.
|
||||
|
||||
Extract modules own artifact semantics, prompt use, response schemas, and
|
||||
domain interpretation. Domain-specific concepts remain in the relevant module,
|
||||
validator, shared domain helper, and artifact contract.
|
||||
|
||||
Auxiliary references provide context or disambiguation. They are not source
|
||||
evidence and must not be converted into source references.
|
||||
|
||||
## Pipeline Composition And Ownership
|
||||
|
||||
Module selection is configuration- and registry-driven. The framework resolves
|
||||
named pipeline definitions, applies explicit defaults and runtime overrides,
|
||||
and verifies module availability and capabilities before execution. Structural
|
||||
pipeline choices must not be scattered through conditionals or hidden behind
|
||||
ad hoc command flags.
|
||||
|
||||
Stage ownership is explicit:
|
||||
|
||||
- input modules convert external material into the generic source model;
|
||||
- chunk modules partition source material for extraction;
|
||||
- extract modules produce domain artifacts from chunks;
|
||||
- merge modules combine accepted extraction outputs;
|
||||
- normalize modules reconcile merged output;
|
||||
- output modules encode accepted results and run outcomes into logical files.
|
||||
|
||||
The framework owns orchestration and handoff provenance. Modules return logical
|
||||
results and warnings; they do not own CLI reporting, workspace paths, durable
|
||||
file placement, checkpoints, or diagnostics.
|
||||
|
||||
## Validation
|
||||
|
||||
Validators should be independently testable and composable.
|
||||
Validation is a framework-managed boundary around raw outputs from chunk,
|
||||
extract, merge, and normalize stages. Validators receive immutable stage output
|
||||
and make an explicit whole-output decision: approve, approve with warnings, or
|
||||
reject.
|
||||
|
||||
Validators evaluate immutable module outputs returned by `chunk`, `extract`,
|
||||
`merge`, and `normalize` stages. Validator decision semantics should be
|
||||
explicit: each validator call approves, rejects, or approves with warnings for
|
||||
the whole module output it receives. Validator rejection records rejected raw
|
||||
output; validator execution errors are framework errors.
|
||||
Rejection is a recorded pipeline outcome, not a framework execution error.
|
||||
Validator execution failures are framework errors. Rejected output does not
|
||||
advance to the next stage.
|
||||
|
||||
Default validator chains belong in central production catalog mappings keyed by
|
||||
stage and module key. Pipeline configuration may override those mappings at the
|
||||
stage-local module binding. Empty chains are valid and approve by default.
|
||||
Default validator chains are production composition policy and are registered
|
||||
centrally by stage and module. Configuration may replace a stage-local default,
|
||||
including with an explicitly empty chain. Configured validator order is
|
||||
authoritative; the framework must not silently reorder it.
|
||||
|
||||
Deterministic validators should run before LLM-backed validators in production
|
||||
defaults when both are present. Configured validator order is authoritative and
|
||||
must not be silently reordered.
|
||||
## LLM Boundary
|
||||
|
||||
Shared validator runtime mechanics belong in framework code. Concrete validator
|
||||
behavior belongs in module or validator implementation packages.
|
||||
Modules and validators use transport-neutral structured completion contracts.
|
||||
Provider request and response types, authentication, transport behavior, and
|
||||
provider error adaptation remain inside the LLM runtime.
|
||||
|
||||
## LLM Runtime
|
||||
The caller of the LLM owns prompt selection, prompt inputs, response schema,
|
||||
and interpretation of structured output. Provider adapters do not own source-
|
||||
or domain-specific prompt logic.
|
||||
|
||||
LLM provider details belong behind transport-neutral framework contracts.
|
||||
LLM calls and other external operations accept cancellation and respect
|
||||
timeouts. Concurrency control belongs in shared runtime plumbing rather than in
|
||||
individual modules.
|
||||
|
||||
Provider-specific HTTP request and response types should stay inside the LLM
|
||||
runtime package. Prompt construction should stay in extractors, validators, or
|
||||
shared prompt helpers; provider adapters should not own domain prompt logic.
|
||||
## Configuration And Provenance
|
||||
|
||||
Errors, diagnostics, reports, manifests, and redacted configuration must not
|
||||
expose secrets.
|
||||
Configuration loading, precedence, defaults, environment overrides, redaction,
|
||||
and validation are centralized. Named pipeline definitions make structural
|
||||
composition explicit and discoverable. Operational overrides are permitted
|
||||
when they do not obscure the configured pipeline structure.
|
||||
|
||||
## Configuration
|
||||
Run preparation fails before stage execution when statically discoverable
|
||||
modules, capabilities, reference bindings, or explicitly selected profiles are
|
||||
invalid or incompatible.
|
||||
|
||||
Configuration should make pipeline composition explicit and discoverable.
|
||||
Run manifests record enough resolved pipeline, module, source, reference, and
|
||||
LLM provenance to make a run auditable after configuration changes. Manifests
|
||||
record identities and summaries rather than secret or large payload content.
|
||||
|
||||
Centralize configuration loading, precedence, defaults, and validation. Structural
|
||||
pipeline choices should come from named pipeline definitions, not ad hoc command
|
||||
flags. Operational overrides may be handled separately when they do not obscure
|
||||
the configured pipeline structure.
|
||||
## State, Output, And Safety
|
||||
|
||||
Module registries should expose module metadata and capabilities without
|
||||
requiring module construction. Configuration validation should fail fast when a
|
||||
pipeline binds incompatible or unknown modules.
|
||||
Durable output, diagnostics, checkpoints, and debug artifacts are separate
|
||||
surfaces with separate ownership:
|
||||
|
||||
Run manifests should record enough resolved pipeline provenance to make a run
|
||||
auditable after named configuration changes over time.
|
||||
- output modules define logical durable output; the application boundary owns
|
||||
filesystem placement;
|
||||
- diagnostics provide redacted run inspection and are not the durable output
|
||||
contract;
|
||||
- checkpoints support validated stage reuse and are not diagnostics;
|
||||
- debug artifacts are opt-in inspection data and may contain sensitive source,
|
||||
prompt, reference, and model-output content.
|
||||
|
||||
## Dependencies
|
||||
Writes of durable state are atomic where practical. Paths for writes, moves,
|
||||
overwrites, and deletion must be narrow and explicit. Cleanup that can lose data
|
||||
is opt-in.
|
||||
|
||||
Prefer the Go standard library where practical.
|
||||
Secrets must not appear in errors, logs, diagnostics, manifests,
|
||||
documentation, examples, or redacted configuration. Default logs and
|
||||
diagnostics must not include large source, prompt, reference, or artifact
|
||||
payloads.
|
||||
|
||||
Use external dependencies only when justified by correctness, security,
|
||||
interoperability, or substantial complexity reduction. Good reasons include
|
||||
widely used file formats, complex validation behavior, or secure transport
|
||||
handling.
|
||||
## Architectural Non-Goals
|
||||
|
||||
Avoid dependencies for small conveniences. Do not let external dependency types
|
||||
leak across internal package boundaries unless the dependency is itself the
|
||||
explicit contract of that package.
|
||||
Notarius does not aim to provide:
|
||||
|
||||
## State, Files, and Safety
|
||||
|
||||
If the application writes durable state, writes should be atomic where
|
||||
practical. Multi-step workflows should preserve enough diagnostics to support
|
||||
inspection after failure.
|
||||
|
||||
Code that deletes, moves, or overwrites files must use narrow, explicit paths.
|
||||
Avoid broad parent-directory operations. Cleanup that can cause data loss must
|
||||
be opt-in.
|
||||
|
||||
## Errors and Logging
|
||||
|
||||
Errors should be actionable and preserve context. Wrap errors with operation and
|
||||
path or resource context. CLI code should convert internal errors into concise
|
||||
user-facing messages.
|
||||
|
||||
Errors and logs must not expose secrets. Logs should describe operations,
|
||||
external calls, retries, and failure causes, but should not include large source
|
||||
or artifact payloads by default.
|
||||
|
||||
Long-running operations should accept `context.Context`. External calls,
|
||||
subprocesses, HTTP requests, storage operations, LLM calls, and multi-stage
|
||||
workflows should respect cancellation and timeouts.
|
||||
|
||||
## Testing
|
||||
|
||||
Core logic should be testable without real external services. Use fakes,
|
||||
fixtures, or local test doubles for input modules, extract modules, validators,
|
||||
and LLM clients where practical.
|
||||
|
||||
Contract-first work should include fake implementations that prove interfaces
|
||||
compose before real modules depend on them.
|
||||
|
||||
Maintain a fixture-driven walking skeleton that exercises the full pipeline with
|
||||
fake modules and fake external clients. This protects stage composition as real
|
||||
modules evolve.
|
||||
|
||||
Important CLI and configuration workflows should have tests. Adapter, extractor,
|
||||
validator, and stage contracts should have focused tests that do not require
|
||||
running the full application unless end-to-end coverage is intentional.
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation should follow the project documentation policy. Keep user docs
|
||||
focused on implemented behavior. Put future, planned, or aspirational work only
|
||||
under `docs/roadmap/`.
|
||||
|
||||
Core documentation should use generic terms such as source document, source
|
||||
unit, source reference, input adapter, extractor, chunker, merger, normalizer,
|
||||
artifact, validator, and run manifest.
|
||||
|
||||
Source-format details belong in input module or integration docs.
|
||||
Domain-specific extraction details belong in extract module or artifact docs.
|
||||
|
||||
When changing architecture, config, CLI behavior, stage modules, extractor
|
||||
contracts, validator contracts, LLM runtime behavior, or artifact schemas, update
|
||||
the relevant docs and examples in the same change.
|
||||
- an arbitrary workflow graph or general workflow language;
|
||||
- source-format or extraction-domain behavior in generic framework packages;
|
||||
- provider-specific contracts exposed to modules;
|
||||
- structural pipeline composition through ad hoc CLI flags;
|
||||
- implicit cross-stage behavior that bypasses the fixed pipeline;
|
||||
- abstractions introduced solely for hypothetical future complexity.
|
||||
|
||||
Reference in New Issue
Block a user