diff --git a/architecture.md b/architecture.md index c04815d..7aeaffc 100644 --- a/architecture.md +++ b/architecture.md @@ -2,28 +2,41 @@ ## 1. Purpose -`narratio` is a Go orchestrator for D&D session processing. It coordinates a stage-based pipeline from recorded audio through transcript and artifact generation, while preserving durable run state for skip/rerun/resume behavior. +`narratio` is a Go orchestrator for D&D session processing. It coordinates a stage-based pipeline from recorded audio through transcript generation, transcript polishing, and downstream artifact generation, while preserving durable run state for skip/rerun/resume behavior. -The repository currently implements the orchestration scaffold, local state model, and stage framework, including real WhisperX/Seriatim/Audita adapters plus real `prepare`/`transcribe`/`merge`/`polish` stages. It intentionally does **not** yet implement real analyzer, remote archive, or notification integrations. +The repository currently implements the orchestration scaffold, local state model, and stage framework, including real WhisperX/Seriatim/Audita adapters plus real `prepare`/`transcribe`/`merge`/`polish` stages. It intentionally does **not** yet implement real Scriptorium-backed artifact generation, remote archive, or notification integrations. + +The future `analyze` stage will use `scriptorium` as a subprocess to generate analysis artifacts such as a session recap. The initial Scriptorium-backed artifact will be a **session recap** generated from: + +1. the processed/polished transcript, and +2. the previous session recap, when available. + +The design should remain modular and composable so later artifact-generation workflows can produce intermediate artifacts, such as structured event logs or state maps, and then use those artifacts as inputs to later outputs such as session recaps, player summaries, or table-read analyses. ## 2. Design Goals - Modular boundaries between orchestration, stages, adapters, storage, config, and manifest. - Config-driven behavior with strict decoding and conservative validation. - Durable manifest-based control for resumability and idempotent reruns. -- Boring, explicit control flow (no workflow engine/DAG abstraction). +- Boring, explicit control flow; no workflow engine/DAG abstraction for v1. - Observable orchestration via structured logs plus per-tool log file paths. - Safe extension points for incremental real-stage implementation. +- Artifact generation should be composable: generated artifacts should be reusable as named inputs to later artifact generation steps. +- Prompt IDs, profile IDs, input mappings, and output destinations should be configuration-driven, not hardcoded in stage logic. ## 3. Non-Goals Current and near-term non-goals: -- Reimplementing WhisperX, Seriatim, Audita, or analyzer internals. +- Reimplementing WhisperX, Seriatim, Audita, or Scriptorium internals. +- Calling Scriptorium internal Go packages. +- Using Scriptorium's HTTP API as the initial integration path. +- Making Scriptorium responsible for Narratio stage state, artifact storage, or notification. - Building a generic distributed workflow engine. - Adding a DB dependency for v1. -- Embedding D&D prompt logic in orchestration core. -- Implementing checksum-based stale detection (planned, not implemented). +- Embedding D&D prompt text or prompt-specific business logic in the orchestration core. +- Implementing checksum-based stale detection; this is planned but not implemented. +- Implementing a full general artifact dependency DAG in the first Scriptorium integration pass. ## 4. Current Implementation Status @@ -34,22 +47,26 @@ Implemented now: - Local workdir and artifact-store abstraction with lock-file support. - Durable local JSON manifest store with atomic writes. - Stage interface, canonical stage ordering, and runner main loop. -- Real `prepare` stage (input resolution/materialization/provenance). -- Real `transcribe` stage (prepared-audio discovery, WhisperX adapter execution, JSON transcript validation, provenance metadata). -- Real `merge` stage (raw-transcript discovery, Seriatim adapter execution, merged/report JSON validation, provenance metadata). -- Real `polish` stage (merged-transcript discovery, Audita adapter execution, processed/report JSON validation, provenance metadata). -- Real WhisperX HTTP adapter implementation (multipart POST + retries + timeout + atomic output writes). -- Real Seriatim subprocess adapter implementation (deterministic CLI/env construction + output/report JSON validation). -- Real Audita subprocess adapter implementation (deterministic CLI/env construction + credential env handling + output/report JSON validation). -- Placeholder downstream stages (`normalize`, `analyze`, `archive`, `notify`) with adapter contract calls. -- Adapter interfaces and fake/no-op implementations for all external boundaries. +- Real `prepare` stage: input resolution, materialization, and provenance. +- Real `transcribe` stage: prepared-audio discovery, WhisperX adapter execution, JSON transcript validation, and provenance metadata. +- Real `merge` stage: raw-transcript discovery, Seriatim adapter execution, merged/report JSON validation, and provenance metadata. +- Real `polish` stage: merged-transcript discovery, Audita adapter execution, processed/report JSON validation, and provenance metadata. +- Real WhisperX HTTP adapter: multipart POST, retries, timeout, and atomic output writes. +- Real Seriatim subprocess adapter: deterministic CLI/env construction and output/report JSON validation. +- Real Audita subprocess adapter: deterministic CLI/env construction, credential env handling, and output/report JSON validation. +- Placeholder downstream stages: `normalize`, `analyze`, `archive`, `notify`. +- Adapter interfaces and fake/no-op implementations for external boundaries. - Reusable subprocess helper and generated YAML/config writing helper. - Test coverage across config, manifest, artifacts, planning, runner control, and adapters. Still planned/future: -- Real analyzer integration. -- Real remote archive/storage backend (S3/SFTP/etc). +- Real Scriptorium subprocess adapter. +- Real `analyze` stage backed by Scriptorium. +- Initial `session_recap` artifact generation. +- Optional previous-session artifact discovery for prior recap context. +- Future multi-artifact analysis workflows. +- Real remote archive/storage backend, such as S3/SFTP/etc. - Real notification backend. - Stale detection based on input/config checksums. @@ -72,357 +89,840 @@ Execution status: - `transcribe` is implemented and validates raw transcript JSON outputs. - `merge` is implemented and validates merged transcript/report JSON outputs. - `polish` is implemented and validates processed transcript/report JSON outputs. -- `normalize`/`analyze`/`archive`/`notify` remain placeholders. +- `normalize` remains a placeholder. +- `analyze` remains a placeholder, but is planned to become the Scriptorium-backed artifact-generation stage. +- `archive` and `notify` remain placeholders. -## 6. CLI Commands +### Future Analyze Stage Shape -`cmd/narratio/main.go` is intentionally thin and delegates to `internal/app`. +The `analyze` stage should become the home for generated artifacts. The first implemented artifact should be: + +- `session_recap` + +Initial `session_recap` inputs: + +- `transcript`: the processed transcript at `transcripts/processed.json` +- `previous_recap`: the previous session's recap, if available + +Future artifacts may include: + +- structured event log +- final state map +- glossary suggestions +- player-facing summary +- table-read analysis +- meta-analysis + +Future artifact workflows may be sequential or dependency-aware. For example: + +```text +processed transcript + ↓ +structured event log + ↓ +session recap + ↓ +player-facing summary +``` + +Do not implement a general DAG engine initially. The design should, however, avoid hardcoding assumptions that would make later artifact dependencies difficult. + +6. CLI Commands + +cmd/narratio/main.go is intentionally thin and delegates to internal/app. Current command behavior: -- `run --config --session [--force]` - - Loads + validates config, ensures workdir, acquires lock, loads/creates manifest, executes full stage plan. - - Skips already-succeeded stages unless `--force`. -- `plan --config --session [--force]` - - Loads + validates config, ensures workdir, loads manifest if present, prints `run`/`skip` action per stage and totals. -- `resume --config --session [--force]` - - Resumes from first non-succeeded stage based on manifest (or full run when forced). -- `run-stage --config --session [--force] ` - - Executes exactly one named stage; unknown stage is an error. -- `status --manifest ` - - Loads manifest and prints session ID, updated timestamp, and stage statuses. +run --config --session [--force] +Loads and validates config. +Ensures workdir. +Acquires lock. +Loads or creates manifest. +Executes full stage plan. +Skips already-succeeded stages unless --force. +plan --config --session [--force] +Loads and validates config. +Ensures workdir. +Loads manifest if present. +Prints run/skip action per stage and totals. +resume --config --session [--force] +Resumes from first non-succeeded stage based on manifest. +Runs full pipeline when forced. +run-stage --config --session [--force] +Executes exactly one named stage. +Unknown stage is an error. +status --manifest +Loads manifest and prints session ID, updated timestamp, and stage statuses. Invalid usage returns non-zero with usage/error text. -## 7. Configuration +Future Scriptorium work should not require new top-level CLI commands. scriptorium integration should be exercised through: -Package: `internal/config` +narratio run-stage --config pipeline.yml --session session.yml analyze + +and, eventually: + +narratio run --config pipeline.yml --session session.yml + +7. Configuration + +Package: internal/config Files: -- `pipeline.yml` -- `session.yml` +pipeline.yml +session.yml Key behavior: -- Strict YAML decoding via `yaml.Decoder.KnownFields(true)`. -- Unknown fields are rejected. -- Combined resolved config type keeps source paths (`PipelinePath`, `SessionPath`) for provenance/errors. -- WhisperX optional fields are defaulted during load for deterministic resolved config values. +Strict YAML decoding via yaml.Decoder.KnownFields(true). +Unknown fields are rejected. +Combined resolved config type keeps source paths (PipelinePath, SessionPath) for provenance/errors. +Optional fields are defaulted during load when deterministic resolved values are useful. +WhisperX Config -WhisperX config keys: +Current keys: -- `pipeline.whisperx.transcribe_url` (required) -- `pipeline.whisperx.language` (default: `en`) -- `pipeline.whisperx.timeout` (default: `30m`) -- `pipeline.whisperx.retries` (default: `3`) -- `pipeline.whisperx.retry_delay` (default: `2s`) -- `pipeline.whisperx.concurrency` (default: `2`) +pipeline.whisperx.transcribe_url required +pipeline.whisperx.language default: en +pipeline.whisperx.timeout default: 30m +pipeline.whisperx.retries default: 3 +pipeline.whisperx.retry_delay default: 2s +pipeline.whisperx.concurrency default: 2 +Seriatim Config -Seriatim config keys: +Current keys: -- `pipeline.seriatim.binary` (required) -- `pipeline.seriatim.timeout` (default: `10m`) -- `pipeline.seriatim.output_schema` (default: `seriatim-intermediate`; allowed: `seriatim-minimal|seriatim-intermediate|seriatim-full`) -- `pipeline.seriatim.coalesce_gap` (default: `3.0`, must be `>= 0`) -- `pipeline.seriatim.report` (default: `true`) -- optional tuning under `pipeline.seriatim.env.*` (must be `> 0` when provided) +pipeline.seriatim.binary required +pipeline.seriatim.timeout default: 10m +pipeline.seriatim.output_schema default: seriatim-intermediate +allowed: seriatim-minimal|seriatim-intermediate|seriatim-full +pipeline.seriatim.coalesce_gap default: 3.0 +must be >= 0 +pipeline.seriatim.report default: true +optional tuning under pipeline.seriatim.env.* +must be > 0 when provided +Audita Config -Audita config keys: +Current keys: -- `pipeline.audita.binary` (required) -- `pipeline.audita.timeout` (default: `3h`) -- `pipeline.audita.llm_api_key_env` (optional; no automatic default) -- `pipeline.audita.modules` (default sequence: `glossary,homophones,glossary,spoken_word,grammar,homophones,glossary`) -- `pipeline.audita.base_url` (default: `https://openrouter.ai/api/v1`) -- `pipeline.audita.model` (default: `openrouter/google/gemma-4-31b-it`) -- `pipeline.audita.llm_concurrency` (default: `1`, must be `> 0`) -- `pipeline.audita.validation_model` (default: empty string) -- `pipeline.audita.validation_llm_concurrency` (default: `1`, must be `> 0`) -- `pipeline.audita.report` (default: `true`) +pipeline.audita.binary required +pipeline.audita.timeout default: 3h +pipeline.audita.llm_api_key_env optional; no automatic default +pipeline.audita.modules +default sequence: glossary,homophones,glossary,spoken_word,grammar,homophones,glossary +pipeline.audita.base_url default: https://openrouter.ai/api/v1 +pipeline.audita.model default: openrouter/google/gemma-4-31b-it +pipeline.audita.llm_concurrency default: 1 +must be > 0 +pipeline.audita.validation_model default: empty string +pipeline.audita.validation_llm_concurrency default: 1 +must be > 0 +pipeline.audita.report default: true Audita secret-handling policy: -- `llm_api_key_env` stores only the environment variable **name**. -- API key values are read from the process environment at runtime and are not stored in `pipeline.yml`, manifest metadata, generated configs, or logs. -- If `llm_api_key_env` is configured and the named env var is missing/empty, the Audita adapter fails before invocation with a redacted error. -- If `llm_api_key_env` is empty/omitted, the Audita adapter omits `AUDITA_LLM_API_KEY` from subprocess env overrides and continues. +llm_api_key_env stores only the environment variable name. +API key values are read from the process environment at runtime. +API key values are not stored in pipeline.yml, manifest metadata, generated configs, or logs. +If llm_api_key_env is configured and the named env var is missing/empty, the Audita adapter fails before invocation with a redacted error. +If llm_api_key_env is empty/omitted, the Audita adapter omits AUDITA_LLM_API_KEY from subprocess env overrides and continues. +Planned Scriptorium Config -Validation currently enforces: +The future Scriptorium integration should add a top-level pipeline.scriptorium config section. -- `pipeline.workspace.root` is required. -- `pipeline.whisperx.transcribe_url` is required and must be a valid URL. -- `pipeline.whisperx.timeout` and `pipeline.whisperx.retry_delay` must parse as Go durations. -- `pipeline.whisperx.retries` must be `>= 0`. -- `pipeline.whisperx.concurrency` must be `> 0`. -- `pipeline.seriatim.binary` is required. -- `pipeline.seriatim.timeout` must parse as Go duration. -- `pipeline.seriatim.output_schema` must be one of `seriatim-minimal|seriatim-intermediate|seriatim-full`. -- `pipeline.seriatim.coalesce_gap` must be `>= 0`. -- optional `pipeline.seriatim.env.*` values must be `> 0` when provided. -- `pipeline.audita.binary` is required. -- `pipeline.audita.timeout` must parse as Go duration. -- `pipeline.audita.modules` must be non-empty and each entry must be one of `glossary|homophones|spoken_word|grammar`. -- `pipeline.audita.base_url` must be a valid URL when provided. -- `pipeline.audita.model` is required. -- `pipeline.audita.llm_concurrency` must be `> 0`. -- `pipeline.audita.validation_llm_concurrency` must be `> 0`. -- `session.session_id` is required. -- `session.inputs.speakers_file`, `autocorrect_file`, `glossary_file` are required. -- At least one audio source: `session.inputs.audio_dir` or non-empty `session.inputs.audio_files`. -- Configured timeout fields must parse as Go durations when present. +Illustrative shape: -Validation scope is intentionally conservative (no deep business defaults, no full schema semantics, no remote connectivity checks). +scriptorium: + binary: "scriptorium" + config_path: "/etc/scriptorium/config.yml" + timeout: "10m" + render_debug: false -## 8. Manifest and Stage Status + artifacts: + session_recap: + enabled: true + prompt_id: "dnd.session_recap" + profile_id: "local-quality" + output_path: "artifacts/session_recap.md" + timeout: "10m" -Package: `internal/manifest` + inputs: + transcript: + source: "processed_transcript" + required: true + + previous_recap: + source: "previous_session_artifact" + artifact: "session_recap" + required: false + + vars: + session_id: true + session_date: true + campaign_name: true + previous_session_id: true + output_kind: "session_recap" + +This shape is illustrative, not yet implemented. The important design points are: + +scriptorium.binary should accept either scriptorium on PATH or a full path. +scriptorium.config_path should be optional. +When omitted, Scriptorium may use its own default config behavior. +When provided, Narratio should pass --config . +Prompt IDs are configuration values, not hardcoded in the analyze stage. +Profile IDs are optional configuration values. +When omitted, Scriptorium prompt defaults may apply. +Artifact definitions should map: +artifact name +prompt ID +optional profile ID +named inputs +small metadata variables +output destination +timeout +optional render-debug behavior +Input names must match the Scriptorium prompt definition. +Large content should be passed as input files, not as --var values. +API keys should never be passed directly on the command line. +Runtime overrides such as --llm-base-url, --model, --api-key-env, --temperature, --max-tokens, --top-p, and --timeout should be used only when explicitly configured. +Validation Scope + +Validation currently enforces the implemented config sections listed above. + +Future Scriptorium validation should enforce: + +scriptorium.binary is required for real analyze. +configured artifact names are unique. +enabled artifacts have a non-empty prompt_id. +output paths are session-workdir-relative or otherwise safe. +input names are non-empty. +required configured inputs can be resolved before execution. +optional inputs may be omitted without failing the stage. +explicit config_path, when provided, exists and is a regular file. +timeout values parse as Go durations. +unknown YAML fields continue to fail strict decoding. + +Do not validate Scriptorium prompt library internals inside Narratio. Scriptorium owns prompt/profile/schema loading and validation. + +8. Manifest and Stage Status + +Package: internal/manifest Model includes: -- `Manifest` -- `StageRecord` -- `ArtifactRecord` -- `InputRecord` -- `ErrorRecord` -- `StageStatus` +Manifest +StageRecord +ArtifactRecord +InputRecord +ErrorRecord +StageStatus Supported stage statuses: -- `pending` -- `running` -- `succeeded` -- `failed` -- `skipped` -- `stale` (defined but not actively produced by runner yet) -- `interrupted` (defined for future use) +pending +running +succeeded +failed +skipped +stale +defined but not actively produced by runner yet +interrupted +defined for future use Store behavior: -- Local JSON store with create/load/save. -- Save is atomic (same-directory temp file + rename). -- `updated_at` refreshed on save. +Local JSON store with create/load/save. +Save is atomic via same-directory temp file + rename. +updated_at refreshed on save. Helpers implemented: -- `MarkStageRunning` -- `MarkStageSucceeded` -- `MarkStageFailed` -- `MarkStageSkipped` +MarkStageRunning +MarkStageSucceeded +MarkStageFailed +MarkStageSkipped -Runner currently records `running/succeeded/failed` transitions and preserves existing succeeded stage metadata when skipping. +Runner currently records running/succeeded/failed transitions and preserves existing succeeded stage metadata when skipping. Manifest versioning note: -- Current manifest model includes optional `pipeline_version`, but does **not** include an explicit manifest schema/version field. -- Before manifests become long-term compatibility-sensitive across releases/storage backends, add explicit manifest schema/versioning and migration policy. +Current manifest model includes optional pipeline_version, but does not include an explicit manifest schema/version field. +Before manifests become long-term compatibility-sensitive across releases/storage backends, add explicit manifest schema/versioning and migration policy. +Future Artifact Records for Scriptorium -## 9. Workdir and Artifact Model +The Scriptorium-backed analyze stage should record each generated artifact with enough metadata to support rerun, inspection, and future artifact dependency resolution. -Package: `internal/artifacts` +For each artifact, record non-secret metadata such as: -Canonical local paths are resolved by `SessionPaths` under: +artifact name, such as session_recap +prompt ID +profile ID, if explicitly configured +Scriptorium config path, if provided +input artifact paths +optional/missing input decisions +output path +render-debug path, if generated +stdout/stderr log paths +exit code +duration +timeout +Scriptorium command mode: run or render +validation-failed exit status when relevant +generated artifact path even if Scriptorium exits 2 and the output exists -`{workspace.root}/work/{session_id}/` +Do not record rendered prompt content by default. Transcripts and prompts may contain sensitive content. + +9. Workdir and Artifact Model + +Package: internal/artifacts + +Canonical local paths are resolved by SessionPaths under: + +{workspace.root}/work/{session_id}/ Subpaths: -- `inputs/` -- `audio/` -- `transcripts/raw/` -- `transcripts/normalized/` -- `artifacts/` -- `config/` -- `logs/` -- `manifest.json` -- `.lock` +inputs/ +audio/ +transcripts/raw/ +transcripts/normalized/ +artifacts/ +config/ +logs/ +manifest.json +.lock Implemented store capabilities: -- Ensure layout. -- Resolve session paths. -- Copy inputs. -- Existence checks. -- SHA-256 checksums. -- Atomic file writes. -- Session lock acquire/release (`.lock`, conflict-safe via `O_EXCL`). +Ensure layout. +Resolve session paths. +Copy inputs. +Existence checks. +SHA-256 checksums. +Atomic file writes. +Session lock acquire/release via .lock, conflict-safe through O_EXCL. -`Ref` supports local path info plus optional future remote key/checksum metadata. +Ref supports local path info plus optional future remote key/checksum metadata. -## 10. Stage Architecture +Future Scriptorium Artifact Paths -Package: `internal/stage` +The initial Scriptorium-backed artifact should use: + +artifacts/session_recap.md +logs/scriptorium.session_recap.stdout.log +logs/scriptorium.session_recap.stderr.log +config/scriptorium.session_recap.generated.yml + +If render debugging is enabled: + +artifacts/session_recap.render.json + +or another clearly named diagnostics path under artifacts/ or logs/. + +Future artifacts should use stable, predictable paths, for example: + +artifacts/event_log.json +artifacts/final_state_map.json +artifacts/player_summary.md +artifacts/table_read.md +artifacts/meta_analysis.md + +Names should be driven by artifact configuration rather than hardcoded in general analyzer logic. + +10. Stage Architecture + +Package: internal/stage Core contracts: -- `Stage` interface with `Name`, `Declares`, and `Run(ctx, env, manifest)`. -- `IODecl` for declared input/output artifact intent. -- `StageResult` for outputs, logs, generated configs, and metadata. +Stage interface with Name, Declares, and Run(ctx, env, manifest). +IODecl for declared input/output artifact intent. +StageResult for outputs, logs, generated configs, and metadata. -Current `Declares` role: +Current Declares role: -- `Stage.Declares` is currently contract metadata; the runner does not yet enforce declared inputs/outputs at execution time. -- Real-stage implementation work should strengthen enforcement by validating declared inputs before execution and validating declared outputs after execution. +Stage.Declares is currently contract metadata. +The runner does not yet enforce declared inputs/outputs at execution time. +Real-stage implementation work should strengthen enforcement by validating declared inputs before execution and declared outputs after execution. +Current Stage Behaviors +prepare real +Ensures layout. +Resolves session-relative input paths. +Materializes required input files into inputs/. +Writes pipeline.resolved.yml. +Resolves/copies .flac audio inputs from audio_dir or audio_files. +Computes checksums and records deterministic manifest.inputs. +Uses checksum-aware write/copy reuse for idempotency. +transcribe real +Discovers prepared .flac audio inputs from manifest.inputs with kind=audio or work/.../audio fallback. +Derives per-speaker output files at transcripts/raw/{audio_basename}.json. +Calls whisperx.Client with bounded parallelism from pipeline.whisperx.concurrency. +Validates each output file exists and is valid JSON before stage success. +Records output refs plus stage metadata. +merge real +Discovers raw transcript inputs from manifest.stages.transcribe.outputs with kind=transcript_raw or work/.../transcripts/raw fallback. +Validates input transcript files as existing regular JSON files. +Uses prepared inputs/speakers.yml and inputs/autocorrect.yml directly. +Does not translate speaker-map format. +Invokes seriatim.Runner using canonical merged/report/log/generated-config paths under the session workdir. +Validates merged transcript output JSON and report JSON before stage success. +Records merged/report output refs plus stage metadata. +polish real +Discovers merged transcript input from manifest.stages.merge.outputs with kind=transcript_merged or fallback work/.../transcripts/merged.json. +Validates merged transcript as JSON. +Requires prepared inputs/glossary.yml. +Invokes audita.Runner using canonical paths: +transcripts/processed.json +artifacts/audita.report.json +artifacts/audita-work +logs/audita.stdout.log +logs/audita.stderr.log +config/audita.generated.yml +Validates processed transcript output JSON with required top-level segments array. +Validates report JSON when enabled. +Records processed/report outputs plus non-secret provenance metadata. +normalize placeholder -### Current stage behaviors +normalize remains a placeholder. Audita and Seriatim both perform some normalization-like behavior internally. A separate real normalize stage should be implemented only if Narratio develops a clear stage-specific transformation that belongs outside Seriatim and Audita. -- `prepare` (real): - - Ensures layout. - - Resolves session-relative input paths. - - Materializes required input files into `inputs/`. - - Writes `pipeline.resolved.yml`. - - Resolves/copies `.flac` audio inputs from `audio_dir` or `audio_files`. - - Computes checksums and records deterministic `manifest.inputs`. - - Uses checksum-aware write/copy reuse for idempotency. -- `transcribe` (real): - - Discovers prepared `.flac` audio inputs from `manifest.inputs` (`kind=audio`) or `work/.../audio` fallback. - - Derives per-speaker output files at `transcripts/raw/{audio_basename}.json`. - - Calls `whisperx.Client` with bounded parallelism from `pipeline.whisperx.concurrency`. - - Validates each output file exists and is valid JSON before stage success. - - Records output refs plus stage metadata (`audio_files_count`, language/concurrency/retry settings, output paths, per-file attempts/status/duration/path). -- `merge` (real): - - Discovers raw transcript inputs from `manifest.stages.transcribe.outputs` (`kind=transcript_raw`) or `work/.../transcripts/raw` fallback. - - Validates input transcript files as existing regular JSON files. - - Uses prepared `inputs/speakers.yml` and `inputs/autocorrect.yml` directly (no speaker-map format translation). - - Invokes `seriatim.Runner` using canonical merged/report/log/generated-config paths under the session workdir. - - Validates merged transcript output JSON and (when enabled) report JSON before stage success. - - Records merged/report output refs plus stage metadata (input paths/count, Seriatim settings, output paths, adapter duration/exit metadata). -- `polish` (real): - - Discovers merged transcript input from `manifest.stages.merge.outputs` (`kind=transcript_merged`) or fallback `work/.../transcripts/merged.json`. - - Validates merged transcript as JSON and requires prepared `inputs/glossary.yml`. - - Invokes `audita.Runner` using canonical paths: - - `transcripts/processed.json` - - `artifacts/audita.report.json` (when enabled) - - `artifacts/audita-work` - - `logs/audita.stdout.log` - - `logs/audita.stderr.log` - - `config/audita.generated.yml` - - Validates processed transcript output JSON with required top-level `segments` array and validates report JSON when enabled. - - Records processed/report outputs plus non-secret provenance metadata (module/model/base-url/concurrency settings, paths, adapter duration/exit metadata, credential env-var name/presence). -- `normalize`/`analyze`/`archive`/`notify` (placeholder): - - Return placeholder metadata. - - Optionally call adapters using expected request/result contract shapes. +analyze planned -## 11. Application Environment and Adapters +analyze will become the Scriptorium-backed artifact generation stage. -`internal/app` uses `stage.Env` as the shared dependency container to avoid duplicate environment definitions. +Initial behavior should be narrowly scoped: + +Generate a session_recap artifact. +Consume transcripts/processed.json. +Include the previous session recap as previous_recap input when configured and available. +Invoke scriptorium run. +Write output to artifacts/session_recap.md. +Capture stdout/stderr logs. +Generate a redacted invocation/config file for provenance. +Validate that the output artifact file exists and is non-empty. +Record artifact refs and non-secret provenance in manifest. + +Future behavior should generalize artifact generation: + +multiple configured artifact definitions +named inputs +optional inputs +artifact-to-artifact dependencies +render diagnostics for testing/debugging +structured artifacts such as JSON event logs +sequential artifact generation where one artifact feeds another + +Do not implement a generic DAG engine in the first pass. The first Scriptorium integration should be an intentionally small, synchronous subprocess implementation. + +archive placeholder + +Future remote artifact persistence. Storage backends may include local filesystem, S3-compatible object storage, SFTP, or similar. + +notify placeholder + +Future notification backend, such as email or chat notification, once pipeline execution completes. + +11. Application Environment and Adapters + +internal/app uses stage.Env as the shared dependency container to avoid duplicate environment definitions. Contained dependencies: -- Resolved config -- Artifact store -- Manifest store -- Logger -- Adapter interfaces +Resolved config +Artifact store +Manifest store +Logger +Adapter interfaces -Adapter boundaries (`internal/adapters/*`): +Adapter boundaries currently include: -- `whisperx.Client` -- `seriatim.Runner` -- `audita.Runner` -- `analyzer.Runner` -- `storage.Backend` -- `notify.Sender` +whisperx.Client +seriatim.Runner +audita.Runner +analyzer.Runner +storage.Backend +notify.Sender -All adapters currently have fake/no-op implementations for tests/scaffold execution. WhisperX has a real HTTP adapter (`internal/adapters/whisperx/http.go`), Seriatim has a real subprocess adapter (`internal/adapters/seriatim/subprocess.go`), and Audita has a real subprocess adapter (`internal/adapters/audita/subprocess.go`). +All adapters currently have fake/no-op implementations for tests/scaffold execution. WhisperX, Seriatim, and Audita also have real implementations. -### Subprocess helper +Future Scriptorium Adapter Boundary -`internal/adapters/subprocess` provides reusable subprocess scaffolding: +The future analyzer integration should be implemented as a Scriptorium subprocess adapter. -- Context cancellation + optional timeout. -- Explicit executable/args, working dir, env overrides. -- Parent environment inheritance with override merge semantics (override values win). -- Stdout/stderr log file handling (including a shared-stream guard when both logs target the same path). -- Exit code and timing capture. -- Actionable error wrapping, including stdout/stderr log paths and a short redacted stderr tail when available. -- Atomic file/YAML writers for generated config/log scaffolding. +The existing analyzer.Runner may either: -## 12. Run Control, Locking, Logging, and Long-Running Stages +become a Scriptorium-backed implementation, or +be replaced/refined with a more explicit scriptorium.Runner adapter package. -### Run control +Either approach is acceptable if the boundary remains clear: -`internal/app` runner is sequential and manifest-driven: +internal/stage/analyze owns artifact-stage behavior. +internal/adapters/scriptorium or equivalent owns Scriptorium CLI construction and subprocess behavior. +internal/app wires the real adapter into stage.Env. +Tests can inject fake artifact-generation adapters. -- Loads/creates manifest. -- Computes per-stage action (`run` vs `skip`). -- Skip rule today: succeeded + not forced => skip. -- `--force` reruns previously succeeded stages. -- `resume` starts at first non-succeeded stage. +The Scriptorium adapter should support at least: -Stale detection is intentionally TODO (`run_control.go`) pending checksum-based invalidation logic. +RunArtifact(ctx, request) (result, error) +optional future RenderArtifact(ctx, request) (result, error) -### Locking +Request fields should include: -`run`/`resume`/`run-stage` execution paths acquire a session lock via artifact store and release it with `defer`. +binary path +optional config path +prompt ID +optional profile ID +named input paths +variable key/value pairs +output path +stdout/stderr log paths +generated config/invocation path +timeout +optional runtime overrides +API key env var name(s), if configured -### Logging +Result fields should include: -- Structured logger is initialized via `internal/logging` (`slog` text handler). -- Runner emits concise stage lifecycle logs (skip/start/success/fail + manifest save points). -- Real WhisperX/Seriatim/Audita stages already use explicit output/log/config paths; placeholder downstream subprocess adapters keep that same boundary pattern. +output path +stdout/stderr log paths +generated config path +exit code +duration +command mode +prompt ID +profile ID +validation-failed indicator for exit code 2 +non-secret metadata +Subprocess Helper -### Long-running stage expectations +internal/adapters/subprocess provides reusable subprocess scaffolding: -The architecture expects long-running stages (especially WhisperX, Seriatim, and Audita). WhisperX/Seriatim/Audita already use context timeout/cancellation in their adapters (with WhisperX retry logic); other long-running integrations are still pending. +Context cancellation + optional timeout. +Explicit executable/args, working dir, env overrides. +Parent environment inheritance with override merge semantics. +Stdout/stderr log file handling. +Shared-stream guard when both logs target the same path. +Exit code and timing capture. +Actionable error wrapping, including stdout/stderr log paths and a short redacted stderr tail when available. +Atomic file/YAML writers for generated config/log scaffolding. -## 13. Testing Strategy +The future Scriptorium adapter should reuse this helper. + +12. Scriptorium Integration Design +Integration Mode + +Initial integration is synchronous subprocess execution through the public CLI. + +Use: + +scriptorium run + +for production generation. + +Use: + +scriptorium render + +for debugging, dry-runs, and tests that validate command construction without LLM execution. + +Do not call Scriptorium internal Go packages. Do not use Scriptorium's HTTP API as the initial integration path. + +Production Invocation Shape + +The initial session recap invocation should follow this shape: + +scriptorium run \ + --prompt \ + --input transcript= \ + --input previous_recap= \ + --out + +When no previous recap is available, omit that optional input rather than passing an empty path: + +scriptorium run \ + --prompt \ + --input transcript= \ + --out + +Common optional flags: + +--config +--profile +--var name=value, repeatable +--input name=path, repeatable +--timeout +runtime model override flags only when explicitly configured +Render Invocation Shape + +For debug/test rendering: + +scriptorium render \ + --prompt \ + --input transcript= \ + --format json \ + --out + +render does not call the LLM and should not be treated as artifact generation. + +Inputs + +Inputs must be passed as repeated: + +--input name=path + +Rules: + +name must match the Scriptorium prompt definition input name. +Prefer absolute paths, or paths controlled by Narratio's session workdir. +The processed transcript is the primary transcript input. +Optional inputs may include previous recap, glossary, campaign notes, event logs, final state maps, or other generated artifacts. +Scriptorium reads files directly; Narratio should not inline large input content. +Variables + +Small metadata should be passed as repeated: + +--var name=value + +Examples: + +session_id +session_date +campaign_name +previous_session_id +output_kind + +Large content belongs in input files, not --var. + +Prompt IDs and Profiles + +Prompt IDs are configuration values. + +Narratio should not hardcode prompt IDs such as dnd.session_recap in stage logic. Prompt IDs should come from pipeline.yml artifact definitions. + +Profiles: + +May be omitted to use the prompt's default_profile. +May be provided to force a profile such as local-fast, local-quality, frontier, batch, or test. +Should be configuration values. +Runtime Overrides + +Scriptorium supports runtime override flags such as: + +--llm-base-url +--model +--api-key-env +--temperature +--max-tokens +--top-p +--timeout + +Guidance: + +Keep normal model/runtime settings in Scriptorium execution profiles. +Use runtime overrides only for explicit operator-directed exceptions or tests. +Never pass raw API keys on the command line. +--api-key-env names an environment variable; Narratio should ensure that variable is set in the subprocess environment when configured. +Output Handling + +For scriptorium run: + +Narratio should always pass --out. +Treat the output path as the generated artifact. +Capture stdout/stderr separately. +Non-empty stderr alone does not imply failure. + +For scriptorium render: + +Use --out for diagnostics. +Use --format json when tests need structured render output. +Exit Status + +Scriptorium exit status behavior: + +0: success. +1: runtime, parse, config, load, render, generation, or I/O error. +2: run completed but output validation failed. + +Narratio should treat non-zero exit codes as failed stage execution. + +However, when exit code is 2, Scriptorium may have already written an output artifact. Narratio may record that output path in failure metadata, but should not mark the artifact as successfully generated. + +13. Run Control, Locking, Logging, and Long-Running Stages +Run Control + +internal/app runner is sequential and manifest-driven: + +Loads/creates manifest. +Computes per-stage action: run vs skip. +Skip rule today: succeeded + not forced => skip. +--force reruns previously succeeded stages. +resume starts at first non-succeeded stage. + +Stale detection is intentionally TODO pending checksum-based invalidation logic. + +Locking + +run/resume/run-stage execution paths acquire a session lock via artifact store and release it with defer. + +Logging +Structured logger is initialized via internal/logging using slog. +Runner emits concise stage lifecycle logs. +Real WhisperX/Seriatim/Audita stages already use explicit output/log/config paths. +Future Scriptorium stage should use the same pattern: +logs/scriptorium..stdout.log +logs/scriptorium..stderr.log +config/scriptorium..generated.yml +Long-Running Stage Expectations + +The architecture expects long-running stages. + +WhisperX, Seriatim, and Audita already use context timeout/cancellation in their adapters. The future Scriptorium adapter should also use context timeout/cancellation through the shared subprocess helper. + +14. Previous Session Context + +The first Scriptorium-backed artifact, session_recap, may optionally consume the previous session recap. + +Narratio should support this as an optional configured input. + +Initial implementation may use a simple local path or session config reference. Future implementations may resolve previous-session artifacts from: + +current workspace +prior session workdir +manifest records +archive/storage backend +campaign/session index + +The important rule: missing optional previous recap should not fail the initial session_recap artifact generation. + +If previous recap is configured as required and cannot be resolved, analyze should fail clearly before invoking Scriptorium. + +Do not implement a full campaign history index in the initial Scriptorium pass. + +15. Testing Strategy Current tests verify scaffold behavior without real external services: -- Strict config load/validate behavior and error quality. -- Manifest store round trips and transition semantics. -- Workdir layout, atomic writes, checksums, and lock behavior. -- Plan order and stage selection. -- Run/skip/force/resume/run-stage control behavior. -- Prepare-stage input materialization/provenance/idempotency. -- Real transcribe-stage audio discovery/concurrency/failure handling/output validation/provenance. -- Real merge-stage input discovery/validation, Seriatim adapter failure handling, merged/report output validation, and provenance recording. -- Real polish-stage merged-input discovery/validation, Audita adapter failure handling, processed/report output validation, and provenance recording. -- Adapter fake behavior and error propagation. -- WhisperX HTTP adapter behavior (request shape, retry policy, timeout/cancel, JSON validation, atomic writes). -- Seriatim subprocess adapter behavior (arg/env construction, timeout/failure handling, output/report JSON validation). -- Audita subprocess adapter behavior (arg/env construction, credential handling, timeout/failure handling, output/report JSON validation). -- Subprocess helper behavior (success/failure/timeout/log capture). +Strict config load/validate behavior and error quality. +Manifest store round trips and transition semantics. +Workdir layout, atomic writes, checksums, and lock behavior. +Plan order and stage selection. +Run/skip/force/resume/run-stage control behavior. +Prepare-stage input materialization/provenance/idempotency. +Real transcribe-stage audio discovery/concurrency/failure handling/output validation/provenance. +Real merge-stage input discovery/validation, Seriatim adapter failure handling, merged/report output validation, and provenance recording. +Real polish-stage merged-input discovery/validation, Audita adapter failure handling, processed/report output validation, and provenance recording. +Adapter fake behavior and error propagation. +WhisperX HTTP adapter behavior. +Seriatim subprocess adapter behavior. +Audita subprocess adapter behavior. +Subprocess helper behavior. -Tests intentionally avoid hardcoding arbitrary operational default values. +Future Scriptorium tests should avoid real LLM calls. -## 14. Future Work / Implementation Roadmap +Recommended Scriptorium testing strategy: -Recommended implementation sequence (one focused boundary at a time): +Use fake Scriptorium adapters for stage tests. +Use helper subprocesses or scripts for adapter tests. +Use scriptorium render --format json where integration-style tests need to validate command construction without LLM execution. +Verify command construction: +run +render +--prompt +repeated --input +repeated --var +--out +optional --config +optional --profile +Verify missing required input behavior. +Verify optional previous recap behavior. +Verify output file creation. +Verify exit code 1 handling. +Verify exit code 2 handling where output may exist but validation failed. +Verify stderr capture. +Avoid real API keys in tests. +Do not log full rendered prompts by default. -1. Implement real `normalize` transcript transformation/validation. -2. Implement real `analyze` adapter integration and artifact validation. -3. Implement real `archive` remote backend behavior. -4. Implement real `notify` backend. -5. Add checksum-based stale detection and stale status transitions. -6. Add selective parallelism where architecturally safe (`transcribe` fan-out and/or downstream-safe boundaries). +Tests intentionally avoid hardcoding arbitrary operational default values unless those defaults are part of Narratio's documented config contract. + +16. Security and Privacy + +General rules: + +Never pass raw API keys on the command line. +Do not log full environment dumps. +Do not store secret values in generated configs, manifest metadata, or logs. +Treat transcripts, prompts, and generated artifacts as potentially sensitive. +Avoid logging rendered prompts by default. +Avoid logging transcript content by default. +Use session-scoped output paths. +Preserve stdout/stderr logs, but do not assume stderr means failure for tools like Scriptorium. +Runtime api_key_env values should name environment variables, not contain secrets. + +For Scriptorium specifically: + +Pass --api-key-env only when configured. +Ensure the named API-key env var is present in the subprocess environment when required. +Do not record API-key values. +Generated invocation/config files should include env var names but never secret values. +17. Future Work / Implementation Roadmap + +Recommended implementation sequence: + +Update architecture/config contracts for planned Scriptorium integration. +Add Scriptorium configuration contract. +Implement Scriptorium subprocess adapter. +Implement initial real analyze behavior for session_recap. +Support optional previous-session recap input. +Add render-debug support for Scriptorium artifact definitions. +Extend analyze to support multiple configured artifact definitions. +Add artifact-to-artifact input support for future intermediate artifacts. +Implement real archive remote backend behavior. +Implement real notify backend. +Add checksum-based stale detection and stale status transitions. +Add selective parallelism where architecturally safe. Each step must preserve existing package boundaries and manifest-based control flow. -### Real Stage Implementation Checklist +Real Stage Implementation Checklist For each stage moved from placeholder to real behavior: -- Implement and/or use the appropriate adapter behavior for that stage boundary. -- Generate and use config/log paths needed by the adapter/tool invocation. -- Validate expected stage inputs before execution. -- Write outputs atomically where practical (temp + promote/rename). -- Validate expected outputs after execution before marking success. -- Record outputs, logs, generated configs, and provenance metadata in manifest stage records. -- Preserve `skip`/`force`/`resume` semantics (do not regress run-control behavior). -- Add fake-adapter tests and at least one explicit failure-path test for stage behavior. +Implement and/or use the appropriate adapter behavior for that stage boundary. +Generate and use config/log paths needed by the adapter/tool invocation. +Validate expected stage inputs before execution. +Write outputs atomically where practical. +Validate expected outputs after execution before marking success. +Record outputs, logs, generated configs, and provenance metadata in manifest stage records. +Preserve skip/force/resume semantics. +Add fake-adapter tests and at least one explicit failure-path test. +Scriptorium Artifact Implementation Checklist -## 15. Architectural Invariants +For each configured Scriptorium artifact: + +Resolve all required inputs. +Omit missing optional inputs. +Build deterministic scriptorium run args. +Use absolute or session-workdir-controlled paths. +Pass --out. +Capture stdout/stderr separately. +Generate redacted invocation/config metadata. +Validate output file exists and is non-empty. +Record prompt ID, profile ID, input paths, vars, output path, exit code, duration, and logs. +Treat exit code 2 as failure while preserving diagnostic metadata. +Avoid logging prompt content or transcript content. +18. Architectural Invariants The following invariants are mandatory for future changes: -1. Stage orchestration logic lives in `internal/app`; stage business logic lives in `internal/stage`. -2. External tool details remain behind adapter interfaces. -3. Config loading is strict and validation-first. -4. Manifest is the source of truth for stage status and resume/skip decisions. -5. Stage outputs/provenance are recorded durably and deterministically. -6. Local workdir is the primary execution area; remote storage is an adapter concern. -7. Main entrypoint stays thin (`cmd/narratio` delegates to `internal/app`). -8. Pipeline behavior remains explicit and maintainable; no generic DAG engine introduction. -9. Every stage must consume declared artifacts, produce declared artifacts, validate them, record provenance, and remain safely skippable/rerunnable. +Stage orchestration logic lives in internal/app; stage business logic lives in internal/stage. +External tool details remain behind adapter interfaces. +Config loading is strict and validation-first. +Manifest is the source of truth for stage status and resume/skip decisions. +Stage outputs/provenance are recorded durably and deterministically. +Local workdir is the primary execution area; remote storage is an adapter concern. +Main entrypoint stays thin; cmd/narratio delegates to internal/app. +Pipeline behavior remains explicit and maintainable; no generic DAG engine introduction. +Every stage must consume declared artifacts, produce declared artifacts, validate them, record provenance, and remain safely skippable/rerunnable. +Artifact generation must remain configuration-driven; prompt IDs, profile IDs, and artifact input mappings should not be hardcoded in the orchestration core. +Scriptorium integration must use the public CLI subprocess contract until a deliberate future architecture change is made. +Secret values must never be stored in configs, generated invocation files, logs, or manifest metadata. \ No newline at end of file diff --git a/reference/audita/README.md b/docs/integrations/audita.md similarity index 100% rename from reference/audita/README.md rename to docs/integrations/audita.md diff --git a/docs/integrations/scriptorium.md b/docs/integrations/scriptorium.md new file mode 100644 index 0000000..c7d6bfb --- /dev/null +++ b/docs/integrations/scriptorium.md @@ -0,0 +1,322 @@ +# Narratio -> Scriptorium CLI Integration + +## 1. Purpose + +This document defines how Narratio should invoke Scriptorium through the **public CLI**. + +This is a **subprocess integration contract**, not an internal Go API contract. + +## 2. Assumptions + +- `scriptorium` is installed and available on `PATH`. +- Scriptorium is configured with `config.yml`. +- `config.yml` provides `prompt_dir`, `profile_dir`, and `schema_dir` as needed. +- Prompt and profile libraries are already deployed for the environment. +- Narratio provides prepared artifact files (for example polished transcript, glossary, previous recap, campaign notes). +- Initial integration is synchronous subprocess execution. +- Narratio remains the orchestrator. + +In normal operation, Narratio does not need to pass `--prompt-dir` and `--profile-dir` if they are supplied by Scriptorium config. + +Narratio may pass `--config ` when it must use a non-default Scriptorium config file. + +## 3. Core Commands Narratio May Call + +Primary commands for subprocess integration: + +- `scriptorium run` +- `scriptorium render` + +For production generation, use `scriptorium run`. + +`scriptorium render` is for debugging, dry-runs, test assertions, and validating command construction without LLM execution. + +Note: `scriptorium serve` and HTTP API exist, but they are not the initial integration path. + +## 4. Command Selection Guidance + +- Use `run` to generate an output artifact. +- Use `render` to inspect the prepared prompt and effective settings without calling the LLM. +- Use `render --format json` when Narratio/tests need structured prepare output. + +## 5. Recommended `run` Invocation Shape + +Production shape: + +```bash +scriptorium run \ + --prompt \ + --input transcript= \ + --out +``` + +Common optional additions: + +- `--config `: use a specific Scriptorium config file. +- `--profile `: override prompt default profile. +- `--var name=value` (repeatable): small metadata values. +- `--input name=path` (repeatable): additional named artifacts. +- `--timeout `: per-run timeout override. +- Runtime model override flags (`--llm-base-url`, `--model`, etc.) only for exceptional/operator-directed cases. + +## 6. Recommended `render` Invocation Shape + +Human-readable debug shape: + +```bash +scriptorium render \ + --prompt \ + --input transcript= \ + --format text +``` + +Structured debug/test shape: + +```bash +scriptorium render \ + --prompt \ + --input transcript= \ + --format json \ + --out +``` + +`render` does **not** call the LLM, does **not** validate model output, and does **not** perform repair. + +## 7. Inputs + +- Pass inputs as repeated `--input name=path` flags. +- `name` must match the Prompt Definition input name. +- Prefer absolute paths, or paths relative to a working directory controlled by Narratio. +- Pass Audita output as the primary transcript input. +- Additional inputs may include glossary, previous recap, campaign notes, event logs, final state maps, or other prompt-specific artifacts. +- Scriptorium reads input files directly; Narratio does not need to inline file content for CLI use. + +## 8. Variables + +Use repeated `--var name=value` for small metadata values. + +Typical examples: + +- `session_date` +- `session_id` +- `campaign_name` +- `previous_session_id` +- `output_kind` + +Large content belongs in input files, not `--var` values. + +## 9. Prompt IDs and Output Artifact Types + +Narratio should treat prompt IDs as configuration, not hardcoded business logic. + +Narratio config may map stage/output names to prompt IDs, for example: + +- session recap prompt +- structured event extraction prompt +- glossary suggestion prompt +- player-facing summary prompt + +Prompt IDs used by Narratio should come from the deployed Scriptorium prompt library. + +## 10. Profiles + +- Prompts may declare `default_profile`. +- Narratio may omit `--profile` to use prompt default profile. +- Narratio may pass `--profile` to force profile selection. +- This enables environment/profile selection like `local-fast`, `local-quality`, `frontier`, `batch`, or test profiles. +- Profile names should generally be Narratio configuration values. + +## 11. Runtime Overrides + +Supported runtime override flags: + +- `--llm-base-url` +- `--model` +- `--api-key-env` +- `--temperature` +- `--max-tokens` +- `--top-p` +- `--timeout` + +Guidance: + +- Keep normal model/runtime settings in Execution Profiles. +- Use runtime overrides only for explicit per-run exceptions, tests, or operator overrides. +- Never pass raw API keys on the command line. +- `--api-key-env` names an environment variable; Narratio must ensure that variable is set in subprocess environment. + +## 12. Config Behavior + +- Default config path: `/etc/scriptorium/config.yml`. +- `--config ` overrides default path. +- Missing default config is allowed by Scriptorium. +- If `--config` is provided explicitly, the file must exist and be valid. +- CLI flags override `config.yml`. +- `config.yml` overrides built-in application defaults. + +Narratio can either: + +- rely on system default config path, or +- carry an explicit config path and pass `--config`. + +## 13. Environment Handling + +Subprocess environment recommendations: + +- Pass through required API-key environment variables referenced by `api_key_env`. +- Do not pass raw API keys as CLI arguments. +- Avoid logging full environment dumps. +- Capture stdout and stderr separately. +- Use a controlled working directory. +- Prefer absolute artifact paths. + +## 14. Output Handling + +For `scriptorium run`: + +- Use `--out` when Narratio needs durable artifact files. +- Without `--out`, artifact content is written to stdout. +- Preferred orchestration pattern: always use `--out`, then treat the file as stage output artifact. +- Capture stderr for diagnostics. + +For `scriptorium render`: + +- Use `--out` to store render diagnostics. +- Use `--format json` when tests need to inspect selected profile, effective runtime settings, input hashes, prompt hash, and rendered messages. + +## 15. Exit Status and Errors + +Current CLI behavior (verified from implementation/tests): + +- `0`: success. +- `1`: runtime/parse/config/load/render/generation/IO error. +- `2`: run completed but output validation failed (`ValidationFailed`). + +Additional details: + +- On `run`, output artifact write happens before exit code selection. If validation fails, artifact may still be written and exit code is `2`. +- `stderr` carries both errors and normal run summary output; non-empty stderr alone does not imply failure. +- `render` returns `0` on success and `1` on failures. + +Narratio should treat non-zero exit codes as failed stage execution, but may record generated artifact paths if a run exited `2` and output file exists. + +## 16. Recommended Narratio Integration Pattern + +1. Build CLI args from Narratio stage configuration. +2. Use subprocess context cancellation/timeout. +3. Pass absolute input paths. +4. Pass `--out` to a session-scoped artifact path. +5. Add `--var` metadata values. +6. Optionally add `--config`. +7. Optionally add `--profile`. +8. Ensure required API-key env vars are present. +9. Run subprocess synchronously. +10. Capture stdout/stderr separately. +11. On success, store output artifact path and invocation metadata in stage artifacts. +12. On failure, store exit code and stderr diagnostics in stage status. + +## 17. Suggested Narratio Configuration Shape + +Illustrative (not required schema): + +```yaml +scriptorium: + config_path: /etc/scriptorium/config.yml + stages: + session_recap: + prompt_id: dnd.session_recap + profile_id: local-quality # optional + inputs: [transcript, glossary, previous_recap] + vars: [session_id, session_date, campaign_name] + output_path_template: artifacts/{session_id}/session_recap.md + timeout: 2m + render_debug: false +``` + +The key idea: map Narratio stage/artifact names to prompt ID, optional profile, expected inputs, and output destination. + +## 18. Testing Strategy for Narratio Integration + +- Use `scriptorium render --format json` to verify command construction without LLM calls. +- Use dedicated test prompt/profile libraries for integration tests. +- Use small fixture transcripts. +- Verify missing-input failure behavior. +- Verify prompt `default_profile` behavior. +- Verify explicit `--profile` override behavior. +- Verify `--config` behavior (default and explicit). +- Verify output file creation when `--out` is used. +- Verify stderr capture on failures. +- Avoid real API keys in tests. + +## 19. Security and Privacy Notes + +- Never pass raw API keys on command line. +- Do not log full rendered prompts by default; transcripts may contain sensitive content. +- Avoid logging prompt content unless explicit debug mode is enabled. +- Treat generated artifacts as potentially sensitive. +- Use session-scoped, access-controlled output paths. +- `api_key_env` names should come from environment management, not embedded secrets. + +## 20. Initial D&D Artifact Generation Examples + +These are examples only. Use prompt IDs from the deployed prompt library. + +Session recap: + +```bash +scriptorium run \ + --prompt dnd.session_recap \ + --input transcript=/work/session-42/transcript.polished.md \ + --input glossary=/work/session-42/glossary.yml \ + --out /work/session-42/artifacts/session_recap.md +``` + +Structured events: + +```bash +scriptorium run \ + --prompt dnd.structured_events \ + --input transcript=/work/session-42/transcript.polished.md \ + --out /work/session-42/artifacts/structured_events.json +``` + +Glossary suggestions: + +```bash +scriptorium run \ + --prompt dnd.glossary_suggestions \ + --input transcript=/work/session-42/transcript.polished.md \ + --input previous_recap=/work/session-41/artifacts/session_recap.md \ + --out /work/session-42/artifacts/glossary_suggestions.md +``` + +Player-facing summary: + +```bash +scriptorium run \ + --prompt dnd.player_summary \ + --input transcript=/work/session-42/transcript.polished.md \ + --input structured_events=/work/session-42/artifacts/structured_events.json \ + --out /work/session-42/artifacts/player_summary.md +``` + +## 21. Non-Goals + +Initial Narratio integration should not: + +- call Scriptorium internal Go packages +- use HTTP API as the primary path +- expect Scriptorium to read S3 refs directly +- make Scriptorium responsible for Narratio stage state +- make Scriptorium responsible for notification +- require Scriptorium to understand D&D workflow semantics beyond prompt definitions + +## 22. Future Extension Notes + +Possible later extensions: + +- HTTP API integration +- S3 artifact references if Scriptorium adds S3 reader support +- storing render diagnostics alongside generated artifacts +- token budgeting/prompt-size checks +- batch execution if Scriptorium later adds batch support diff --git a/reference/seriatim/README.md b/docs/integrations/seriatim.md similarity index 100% rename from reference/seriatim/README.md rename to docs/integrations/seriatim.md