8.0 KiB
Narratio Architecture
1. Purpose
narratio is a Go orchestrator for D&D session processing. It runs a stage-based local pipeline from audio input through transcript processing and artifact generation, with manifest-based skip/force/resume behavior.
Narratio integrates with Scriptorium through the public CLI (scriptorium run and scriptorium render) via synchronous subprocess execution.
2. Current Status
Implemented:
- strict
pipeline.yml+session.ymlloading with strict YAML field checking (KnownFields(true)) - local workspace/session layout, lock file handling, artifact path helpers, checksums, and atomic writes
- manifest store and stage status transitions for resumable runs
- real
prepare,transcribe,merge, andpolishstages - real WhisperX HTTP adapter
- real Seriatim subprocess adapter
- real Audita subprocess adapter
- real Scriptorium subprocess adapter
- real
analyzestage for initialsession_recapgeneration - optional Scriptorium render diagnostics (
render_debug) before production run
Still placeholder/future:
trimstage behaviorarchivestage behaviornotifystage behavior- additional Scriptorium artifact types beyond
session_recap - artifact-to-artifact workflows beyond the initial single-artifact implementation
- generic stale detection based on input/config checksums
3. Pipeline and Stage Boundaries
Canonical stage order:
preparetranscribemergepolishtrimanalyzearchivenotify
Boundary rules:
- orchestration logic lives in
internal/app - stage business logic lives in
internal/stage - external-tool CLI construction lives in adapter packages
- Scriptorium CLI details stay in
internal/adapters/scriptorium
4. Scriptorium Integration Model
Integration mode:
- public CLI subprocesses only (no Scriptorium internal Go packages, no HTTP API)
- production generation uses
scriptorium run - diagnostics/testing render uses
scriptorium render --format json
Run invocation shape used by adapter:
scriptorium run --prompt <prompt_id> --input name=path --out <output_path>
Optional flags passed when configured:
--config <path>--profile <profile_id>- repeated
--var name=value - repeated
--input name=path --timeout <duration>--api-key-env <ENV_NAME>when configured
Render invocation shape used by adapter:
scriptorium render --prompt <prompt_id> --input name=path --format json --out <render_output_path>
Adapter behavior:
- always passes
--out - captures stdout/stderr separately
- writes generated invocation metadata YAML (redacted, no secrets)
- treats exit code
0as success - treats exit code
1as failure - treats exit code
2as failure withvalidation_failed=trueand preserves output metadata when available - validates successful output files exist and are non-empty
- does not treat non-empty stderr as failure by itself
5. Configuration Contract
pipeline.scriptorium is optional. Existing pipelines without Scriptorium continue to work.
pipeline.trim is optional. Existing pipelines without trim config continue to work.
When pipeline.trim is present:
enabledis optional and defaults tofalsewhen omitted- relative
output_path,bounds.output_path, andbounds.render_output_pathvalues are session-workdir-relative paths - do not store secrets in trim config values
When pipeline.trim.enabled: true:
output_pathis required and non-emptybounds.prompt_idis required and non-emptybounds.transcript_input_nameis required and non-emptybounds.output_pathis required and non-emptybounds.timeoutmust parse as a Go duration when providedbounds.render_debug: truerequires non-emptybounds.render_output_pathbounds.profile_idmay be empty to use the prompt default profile- prompt IDs are config values, not hardcoded stage logic
When pipeline.scriptorium is present:
binaryis required and non-emptyconfig_pathis optional; when provided it must be non-emptytimeoutis optional; when provided it must parse as a Go duration- default
timeoutis10m - unknown YAML fields fail strict decode
Artifacts are configured as a map under pipeline.scriptorium.artifacts so multiple artifacts are possible in the config shape.
For each artifact definition:
enabled: truerequires non-emptyprompt_idenabled: truerequires non-emptyoutput_pathtimeoutmust parse as Go duration when present- optional per-artifact
render_debugmay override globalscriptorium.render_debug inputsare named and each input requires non-emptysource- inputs may be optional (
required: false) varsvalues currently supportstringandbool
Prompt IDs and profile IDs are configuration values, not hardcoded stage logic.
Trim scaffold config shape:
trim:
enabled: true
output_path: "transcripts/trimmed.json"
bounds:
prompt_id: "dnd_session.bounds"
profile_id: ""
transcript_input_name: "transcript"
output_path: "artifacts/session_bounds.json"
timeout: "10m"
render_debug: false
render_output_path: "artifacts/session_bounds.render.json"
seriatim:
report: false
6. Analyze Stage (Current Implementation)
The current real analyze implementation supports only scriptorium.artifacts.session_recap.
Behavior:
- if
pipeline.scriptoriumis missing, analyze returns a skipped result with metadata - if no Scriptorium artifacts are enabled, analyze returns a skipped result with metadata
- if enabled artifacts exist but
session_recapis not enabled, analyze fails clearly - processed transcript input is resolved from manifest (
polishoutput kindtranscript_processed) when available, otherwise fallback pathwork/<session_id>/transcripts/processed.json - processed transcript is validated as JSON with top-level
segmentsarray - configured inputs are resolved by source
- optional
previous_recapis omitted when unavailable - required
previous_recapfails before invocation when unavailable - vars are built from config + session metadata
render_debugcontrols pre-runscriptorium renderdiagnostics- render failure stops stage before production run
- render output is validated as JSON
- production call uses Scriptorium adapter
RunArtifact - successful run output must exist and be non-empty
- manifest records output refs, logs, generated config paths, and non-secret provenance metadata
7. Session Recap Paths
Current expected paths for session_recap:
- artifact output:
artifacts/session_recap.md - run stdout log:
logs/scriptorium.session_recap.stdout.log - run stderr log:
logs/scriptorium.session_recap.stderr.log - run generated invocation/config:
config/scriptorium.session_recap.generated.yml - render output (when enabled):
artifacts/session_recap.render.json - render stdout log:
logs/scriptorium.session_recap.render.stdout.log - render stderr log:
logs/scriptorium.session_recap.render.stderr.log - render generated invocation/config:
config/scriptorium.session_recap.render.generated.yml
8. Security and Privacy
- do not store secrets in pipeline YAML, generated invocation YAML, logs, or manifest metadata
- if API-key integration is configured, pass env var names only (never raw key values)
- avoid logging transcript content or rendered prompt content by default
- treat generated artifacts and logs as potentially sensitive session material
9. Roadmap
Planned next steps:
- extend analyze beyond
session_recapto additional configured artifacts - support artifact inputs that consume prior generated artifacts
- keep this composable without adding a generic DAG engine in the near term
- implement real
archivebackend behavior - implement real
notifybackend behavior - add checksum-based stale detection and stale transitions
Architectural invariants remain:
- strict config decoding/validation
- manifest-driven run control
- clear stage/adapter separation
- configuration-driven prompt/profile/input/vars/output mapping
- Scriptorium integration through public CLI subprocess contract