28 KiB
Roadmap: Runtime-Defined Scriptorium Artifacts
Status
Implementation roadmap for a pre-release hard cutover.
Purpose
Narratio currently treats artifact generation as a narrow analyze stage that supports a hard-coded session_recap artifact. This roadmap describes how to generalize artifact generation so operators can define Scriptorium-backed output artifacts at runtime through pipeline.yml.
The goal is to keep Narratio as a fixed pipeline orchestrator while making the artifact generation step configurable, composable, deterministic, and easy to regenerate selectively.
Desired Outcome
Operators should be able to define artifacts such as session recaps, player handouts, NPC summaries, quest logs, entity maps, or other campaign-specific outputs without changing Narratio code.
A configured artifact is declared under:
pipeline.scriptorium.artifacts.<name>
Each configured artifact becomes a canonical runtime artifact source ID:
narratio.artifact.<name>
For example:
scriptorium:
artifacts:
session_recap:
enabled: true
prompt_id: dnd_session.session_recap
output_path: artifacts/session_recap.md
inputs:
transcript:
source: narratio.transcript.trimmed
required: true
This artifact is addressable by later artifacts as:
narratio.artifact.session_recap
A dependent artifact can then consume it explicitly:
scriptorium:
artifacts:
player_handout:
enabled: true
depends_on:
- session_recap
prompt_id: dnd_session.player_handout
output_path: artifacts/player_handout.md
inputs:
recap:
source: narratio.artifact.session_recap
required: true
transcript:
source: narratio.transcript.trimmed
required: true
Resolved Design Decisions
The following decisions are settled for the initial implementation:
- Configured artifact outputs must live under Narratio's internal artifact output directory, initially
artifacts/. - The artifact output directory should be defined as an internal default in
internal/config/defaults.go, but no public configuration knob should be exposed yet. - Artifact
output_pathshould remain explicit in the initial implementation to avoid guessing file extensions or output formats. - A disabled artifact may still be referenced as an input if its declared output already exists on disk and passes basic validation.
- A disabled artifact is not executable during the current analyze run.
- Artifact-to-artifact references require an explicit
depends_onentry. Narratio should fail fast if the dependency declaration is missing. - The manifest remains stage-oriented:
analyzesucceeds or fails as a full stage. - Analyze-stage metadata may record per-artifact output details for provenance and later resolution, but not for intra-stage resume semantics.
--artifactsshould be added as a CLI filter for selective artifact generation.--artifactsdoes not imply--force; it only changes which configured artifacts are treated as executable whenanalyzeactually runs.- Because Narratio is still pre-release, the hard-coded
session_recapbehavior should be removed immediately rather than deprecated gradually.
Scope
This roadmap covers:
- introducing a runtime artifact catalog;
- generalizing configured Scriptorium artifact execution;
- supporting
narratio.artifact.<name>source IDs; - adding explicit artifact dependencies;
- supporting disabled-but-resolvable artifact inputs;
- adding selective artifact execution via
--artifacts; - recording generated artifacts in analyze-stage metadata and/or manifest outputs;
- removing hard-coded
session_recapbehavior; - updating tests and documentation.
Non-Goals
This feature should not turn Narratio into a general workflow engine.
The initial implementation should not add:
- arbitrary shell-command artifacts;
- arbitrary user-defined stages;
- loops or conditional branching;
- automatic archive promotion of generated artifacts;
- semantic knowledge of particular artifact types;
- per-artifact resume semantics within a successful or failed analyze stage;
- automatic dependency inference without
depends_on.
Narratio should continue to orchestrate a fixed pipeline. The configurable part is the set of Scriptorium artifact invocations performed during the analyze stage.
Current State
Narratio already has several relevant pieces in place:
pipeline.scriptorium.artifactsis modeled as a map of artifact definitions.- The Scriptorium adapter already accepts generic run/render requests.
- The artifact resolver already understands canonical artifact source IDs.
- The
analyzestage already resolves inputs, optionally runs render-debug, invokes Scriptorium, verifies output, and records metadata.
The main limitation is that analyze currently treats session_recap as the only executable artifact and rejects other enabled artifact definitions.
Target Architecture
Runtime Artifact Catalog
Introduce a per-run artifact catalog that tracks built-in artifacts and configured artifacts.
Conceptually:
ArtifactCatalog
├── built-in artifacts
│ ├── narratio.transcript.merged
│ ├── narratio.transcript.polished
│ ├── narratio.transcript.full
│ ├── narratio.transcript.trimmed
│ └── narratio.bounds.session
│
└── configured artifacts
├── narratio.artifact.session_recap
├── narratio.artifact.player_handout
└── narratio.artifact.npc_summary
The catalog should distinguish between three states:
planned valid configured or built-in artifact known to Narratio
available artifact has been produced or otherwise resolved
executable configured artifact selected for execution in this analyze run
Configured artifacts can be planned without being executable. This distinction is important for disabled artifacts and for --artifacts filtering.
Configured Artifact Source IDs
Configured artifact keys map directly to source IDs:
pipeline.scriptorium.artifacts.<name>
→ narratio.artifact.<name>
session_recap should no longer be a special built-in analyze artifact. Instead, it is just a conventional configured artifact key:
scriptorium:
artifacts:
session_recap:
enabled: true
prompt_id: dnd_session.session_recap
output_path: artifacts/session_recap.md
narratio.artifact.session_recap remains valid only because session_recap is configured.
Artifact Output Directory
Add an internal default artifact output directory, initially:
artifacts
This default should live in internal/config/defaults.go or the existing equivalent defaults location.
For the initial implementation:
- expose no public config knob for the artifact output directory;
- require each configured artifact to provide an explicit
output_path; - validate that each configured artifact
output_pathis run-relative; - validate that each configured artifact
output_pathis under the internal artifact output directory; - reject output paths that escape the run workspace or use path traversal.
This preserves future configurability without forcing Narratio to guess output extensions or formats now.
Enabled, Disabled, and Selected Artifacts
Configured artifacts should have three distinct execution states:
enabled by config artifact has enabled: true
selected for execution artifact remains executable after --artifacts filtering
disabled for execution artifact is not executable, but may be resolvable from disk
Without --artifacts, all configured artifacts with enabled: true are selected for execution.
With --artifacts, only the named artifacts are selected for execution. All other configured artifacts are treated as disabled for the current analyze invocation, regardless of their configured enabled value.
Disabled artifacts may still be resolved as inputs if their configured output_path exists on disk and passes validation.
Disabled Artifact Resolution
If artifact B references artifact A, and A is disabled for execution, Narratio should attempt to resolve A from disk.
This should succeed only when:
Ais defined inpipeline.scriptorium.artifacts;Ahas a validoutput_path;- the output path exists in the current run workspace;
- the output is non-empty, or otherwise passes any available artifact-specific validation.
The resolved provenance should make the source clear, for example:
filesystem.disabled_artifact_output
If the file does not exist or fails validation, the dependent artifact should fail before invoking Scriptorium.
Example error wording:
artifact player_handout requires narratio.artifact.session_recap, but session_recap is disabled for execution and artifacts/session_recap.md does not exist
Explicit Dependencies
Artifact-to-artifact references require explicit depends_on entries.
If artifact B has an input source of narratio.artifact.A, then B.depends_on must include A.
This should fail:
scriptorium:
artifacts:
player_handout:
enabled: true
prompt_id: dnd_session.player_handout
output_path: artifacts/player_handout.md
inputs:
recap:
source: narratio.artifact.session_recap
required: true
This should pass:
scriptorium:
artifacts:
player_handout:
enabled: true
depends_on:
- session_recap
prompt_id: dnd_session.player_handout
output_path: artifacts/player_handout.md
inputs:
recap:
source: narratio.artifact.session_recap
required: true
depends_on values refer to configured artifact keys, not full source IDs.
Dependency validation should fail on:
- references to unknown artifact keys;
- missing
depends_onentries for artifact-to-artifact input references; - self-dependencies;
- dependency cycles among executable artifacts.
Dependencies on disabled artifacts are permitted, but the disabled dependency must resolve from disk before the dependent artifact runs.
Execution Order
The analyze stage should execute selected artifacts in dependency order.
Rules:
- selected artifacts are executable;
- disabled artifacts are never executed;
- selected artifacts may depend on other selected artifacts;
- selected artifacts may depend on disabled artifacts if those disabled artifacts resolve from disk;
- independent selected artifacts run in deterministic sorted-name order.
Use topological sorting over selected artifacts, while validating dependency references across the full configured artifact set.
Input Resolution
Input resolution should use the artifact catalog and existing artifact resolver behavior.
For each configured artifact input:
- built-in sources resolve through existing resolver behavior;
previous_session_artifactpreserves existing behavior;narratio.artifact.<name>resolves through the runtime artifact catalog;- selected dependencies resolve after being produced earlier in the same analyze execution;
- disabled dependencies resolve from their configured output path on disk;
- optional missing inputs are omitted;
- required missing inputs fail before Scriptorium is invoked.
Analyze Stage Generalization
The analyze stage should become the generic Scriptorium artifact stage.
High-level flow:
- Load configured Scriptorium artifacts.
- Apply the
--artifactsfilter, if present. - If no artifacts are selected for execution, return success metadata with
skipped=true. - Build the runtime artifact catalog.
- Validate artifact names, output paths, source IDs, dependencies, selected artifacts, and required fields.
- Resolve any disabled dependencies that are required by selected artifacts.
- Sort selected artifacts by dependency order.
- For each selected artifact:
- resolve configured inputs;
- build the Scriptorium run request;
- optionally run Scriptorium render-debug;
- run Scriptorium;
- fail on validation-failed result;
- verify the output exists and is non-empty;
- record artifact output metadata;
- register
narratio.artifact.<name>as available in the catalog.
- Return aggregate analyze-stage metadata containing all generated and reused artifacts relevant to the run.
The Scriptorium adapter should remain generic. It should not decide which artifacts run, how dependencies work, or how artifacts are registered.
Manifest and Metadata
The manifest should remain stage-oriented.
This means:
analyzesucceeds or fails as a full stage;- if
analyzehas already succeeded and the user does not force it, the runner skips it as a full stage; - Narratio should not implement per-artifact resume in the first version.
However, analyze-stage metadata should still record artifact outputs for provenance and future resolution.
Recommended metadata shape:
{
"skipped": false,
"artifacts": [
{
"name": "session_recap",
"source_id": "narratio.artifact.session_recap",
"output_kind": "scriptorium_artifact",
"path": "artifacts/session_recap.md",
"prompt_id": "dnd_session.session_recap",
"profile_id": "local-gemma-31b",
"provenance": "generated.current_analyze_run"
},
{
"name": "player_handout",
"source_id": "narratio.artifact.player_handout",
"output_kind": "scriptorium_artifact",
"path": "artifacts/player_handout.md",
"prompt_id": "dnd_session.player_handout",
"profile_id": "local-gemma-31b",
"provenance": "generated.current_analyze_run"
}
],
"reused_artifacts": [
{
"name": "session_recap",
"source_id": "narratio.artifact.session_recap",
"path": "artifacts/session_recap.md",
"provenance": "filesystem.disabled_artifact_output"
}
]
}
The exact struct can differ from this example, but it should preserve:
- artifact name;
- canonical source ID;
- output path;
- prompt/profile provenance for generated artifacts;
- reused-vs-generated provenance.
Resume and Force Behavior
Keep resume behavior stage-level.
Recommended semantics:
No --force, analyze already succeeded:
runner skips analyze, regardless of --artifacts.
--force, no --artifacts:
analyze regenerates all configured artifacts with enabled: true.
--force --artifacts player_handout:
analyze treats only player_handout as executable.
all other configured artifacts are disabled for execution.
disabled dependencies may be reused from disk.
--artifacts player_handout on a not-yet-completed analyze stage:
analyze runs only player_handout.
disabled dependencies may be reused from disk.
--artifacts should not imply --force. It is an execution filter, not a resume override.
--artifacts CLI Flag
Add an --artifacts flag to commands that can execute or resume the analyze stage.
The flag should accept one or more configured artifact names. Internally, normalize values to a set of artifact keys.
Recommended behavior:
- validate all requested artifact names against
pipeline.scriptorium.artifacts; - reject unknown artifact names before running stages;
- treat requested artifacts as the only executable artifacts for the analyze stage;
- treat all other configured artifacts as disabled for execution;
- allow disabled artifacts to satisfy dependencies from disk as described above;
- if
--artifactsis used while executing a stage other thananalyze, either reject it or ignore it with a clear validation error. Prefer rejection.
The exact CLI parsing style can follow Narratio's existing conventions. Both comma-separated and repeatable values are acceptable if the CLI package supports them cleanly, but the internal representation should be a set of artifact keys.
Archive Behavior
Do not automatically archive every generated artifact.
Artifact generation and archive promotion should remain separate concerns. Operators should continue to use archive.promote_artifacts to decide which generated files should be promoted or uploaded.
Example:
archive:
promote_artifacts:
- from: artifacts/session_recap.md
to: artifacts/session_recap.md
required: true
- from: artifacts/player_handout.md
to: artifacts/player_handout.md
required: false
A later enhancement may add opt-in automatic promotion of configured artifacts, but explicit promotion should remain the default.
Implementation Plan
Phase 1: Config Model and Defaults
Add or update the configured artifact model to include:
enabled;depends_on;prompt_id;profile_id;output_path;timeout;render_debug;inputs;vars.
Add an internal default artifact output directory in internal/config/defaults.go, initially set to artifacts.
Validation rules:
- artifact names must match a conservative identifier pattern such as
^[a-z][a-z0-9_]*$; - selected/executable artifacts require
prompt_idandoutput_path; - configured artifacts that may be referenced while disabled require
output_path; - configured artifact output paths must be run-relative;
- configured artifact output paths must live under the internal artifact output directory;
- configured artifact output paths must not escape the run workspace;
narratio.artifact.<name>input sources must refer to configured artifact keys;- any
narratio.artifact.<name>input source must have a matchingdepends_onentry; depends_onentries must refer to configured artifact keys;- dependencies must not contain self-references or executable cycles;
- input names and var names must remain compatible with the Scriptorium adapter's validation rules;
- unknown YAML fields must continue to fail strict decode.
Tests:
- valid single configured artifact;
- valid multiple independent artifacts;
- valid artifact-to-artifact dependency;
- valid dependency on disabled artifact with output path;
- invalid artifact name;
- missing required fields;
- output path outside
artifacts/; - dependency on missing artifact;
- missing
depends_onfor artifact input source; - self-dependency;
- cycle detection;
- typo in
narratio.artifact.<name>source; - unknown YAML fields still fail strict decode.
Phase 2: CLI Filtering
Add the --artifacts flag and carry the selected artifact set into the run execution options.
Implementation notes:
- parse values according to existing CLI conventions;
- normalize to artifact key strings;
- validate against configured artifact definitions after config load;
- make the selected set available to the analyze stage;
- reject use with commands or stages where analyze cannot run.
Tests:
- no
--artifactsmeans all enabled artifacts are selected; - one requested artifact is selected;
- multiple requested artifacts are selected;
- unknown requested artifact fails;
--artifactsdoes not imply--force;--artifactswith already-succeeded analyze stage is skipped unless forced;--artifactson unsupported stage command fails clearly.
Phase 3: Runtime Artifact Catalog
Introduce an internal artifact catalog abstraction.
Responsibilities:
- register built-in artifact definitions;
- register configured artifact definitions;
- map configured artifact keys to
narratio.artifact.<name>IDs; - track planned, available, and executable artifact states;
- expose lookup by canonical source ID;
- record generated provenance;
- record disabled-from-disk provenance.
Keep the catalog narrow. It should not execute Scriptorium and should not understand prompt semantics.
Tests:
- built-in source lookup;
- configured source registration;
- duplicate/conflicting source handling;
- planned but unavailable artifact lookup;
- selected artifact state;
- disabled artifact state;
- registering an artifact as available after generation;
- registering a disabled artifact as available from disk;
- resolving a configured artifact from analyze metadata if that behavior is implemented.
Phase 4: Resolver Integration
Update artifact resolution so configured artifact IDs are resolved through the runtime catalog.
Resolution behavior:
- built-in sources continue using existing resolver behavior;
- configured artifact sources resolve from catalog availability/provenance;
- selected configured artifacts become available after generation;
- disabled configured artifacts may become available from disk;
- missing optional configured artifact inputs are omitted;
- missing required configured artifact inputs fail clearly.
Tests:
- configured artifact consumes a built-in transcript source;
- configured artifact consumes another configured artifact produced earlier in the same analyze run;
- configured artifact consumes a disabled artifact resolved from disk;
- required disabled artifact missing on disk fails;
- required configured artifact missing fails;
- optional missing configured artifact is omitted;
- reused artifact provenance is recorded distinctly from generated artifact provenance.
Phase 5: Analyze Stage Generalization
Refactor analyze to execute selected configured artifacts.
Implementation notes:
- remove the hard-coded
session_recapselection path; - remove the hard-coded rejection of non-
session_recapartifacts; - preserve skip behavior when Scriptorium config is absent or no artifacts are selected;
- build the runtime artifact catalog;
- apply
--artifactsfiltering; - validate selected artifacts and their dependencies;
- pre-resolve disabled dependencies from disk where required;
- compute deterministic dependency order;
- execute selected artifacts one at a time in dependency order;
- keep render-debug behavior at global and artifact levels;
- keep Scriptorium adapter invocation generic;
- after each successful run, register the artifact as available in the catalog;
- aggregate generated and reused artifact metadata.
Tests:
- no Scriptorium config skips;
- empty artifact map skips;
- no selected artifacts skips;
- disabled artifacts do not run;
- one selected artifact runs;
- multiple independent artifacts run in deterministic order;
- dependent selected artifact receives prior selected artifact as input;
- dependent selected artifact receives disabled-from-disk artifact as input;
- render-debug works for configured artifacts;
- Scriptorium validation failure fails the stage;
- missing required input fails the stage;
- successful outputs are non-empty and recorded;
- artifact filter executes only requested artifacts.
Phase 6: Manifest and Stage Metadata
Update analyze-stage metadata and manifest output recording to support dynamic configured artifacts.
Recommended behavior:
- every generated configured artifact gets
source_id: narratio.artifact.<name>; - every generated configured artifact gets a generic output kind such as
scriptorium_artifact; - reused disabled artifacts are recorded separately from generated artifacts;
- metadata is sufficient for debugging, provenance, and future resolver support;
- metadata does not create per-artifact resume semantics.
Because this is a pre-release hard cutover, do not preserve a special legacy session_recap output kind unless a current internal test or archive path still requires it temporarily. Prefer updating tests and examples to treat session_recap as an ordinary configured artifact.
Tests:
- metadata records one generated configured artifact;
- metadata records multiple generated configured artifacts;
- metadata records reused disabled artifact provenance;
session_recapis recorded as a normal configured artifact;- manifest still treats
analyzeas a single succeeded or failed stage; - runner skip behavior remains stage-level.
Phase 7: Archive and Promotion Review
Review archive behavior after dynamic artifacts are recorded.
Implementation notes:
- do not automatically promote every configured artifact;
- keep
archive.promote_artifactsexplicit; - update default or example promotion rules to use configured
session_recapoutput path; - ensure required promotion rules fail clearly when selected artifact generation did not produce a required file.
Tests:
- generated artifact can be promoted by explicit archive rule;
- required archive promotion fails if selected artifact was not generated and no file exists;
- optional archive promotion skips cleanly if file is absent;
- hard cutover does not rely on hard-coded
session_recapgeneration.
Phase 8: Documentation and Examples
Update documentation after the implementation is complete.
Recommended documentation changes:
- update
docs/config.mdwith the generalized artifact configuration model; - update
docs/internal/artifacts.mdto describe the runtime artifact catalog; - update
docs/stages/analyze.mdto describe generic Scriptorium artifact generation; - update Scriptorium integration docs only if the adapter contract changes;
- update full annotated pipeline examples;
- add at least one example with multiple artifacts and one dependency;
- document
--artifactsbehavior and its relationship to--force; - remove documentation stating that only
session_recapis supported.
Documentation should make clear that:
- configured artifact source IDs use
narratio.artifact.<name>; depends_onuses artifact keys, not full source IDs;- artifact-to-artifact source references require explicit
depends_on; - disabled artifacts can be reused from disk when required by selected artifacts;
--artifactsfilters execution but does not imply--force;- archive promotion remains explicit;
- per-artifact resume is not part of the initial implementation.
Migration Strategy
Because Narratio is pre-release, perform a hard cutover.
Required changes:
- Remove the hard-coded
session_recapanalyze behavior. - Require
session_recapto be declared underpipeline.scriptorium.artifacts.session_recapif the operator wants a session recap. - Treat
narratio.artifact.session_recapas valid only whensession_recapis a configured artifact key. - Update config examples to show
session_recapas a normal configured artifact. - Update tests to stop assuming that
session_recapis a built-in analyze artifact. - Keep archive promotion explicit and path-based.
Example replacement config:
scriptorium:
binary: scriptorium
config_path: /etc/scriptorium/config.yml
timeout: 10m
render_debug: false
artifacts:
session_recap:
enabled: true
prompt_id: dnd_session.session_recap
profile_id: local-gemma-31b
output_path: artifacts/session_recap.md
timeout: 20m
inputs:
transcript:
source: narratio.transcript.trimmed
required: true
prior_recap:
source: previous_session_artifact
artifact: artifacts/session_recap.md
required: false
vars:
artifact_title: Session Recap
Acceptance Criteria
The feature is complete when:
- operators can define more than one enabled Scriptorium artifact in
pipeline.yml; - Narratio runs selected artifacts in deterministic dependency order;
- configured artifacts are addressable as
narratio.artifact.<name>; - one configured artifact can consume another configured artifact as an input;
- artifact-to-artifact input references require explicit
depends_on; - disabled artifacts can satisfy dependencies from existing on-disk outputs;
- missing required disabled artifacts fail clearly;
- optional missing inputs are omitted;
--artifactscan selectively execute valid configured artifact names;--artifactsdoes not imply--force;- render-debug behavior works for all configured artifacts;
- generated and reused artifacts are recorded in analyze-stage metadata;
session_recapis no longer hard-coded and works as a normal configured artifact;- archive promotion remains explicit;
- tests cover config validation, dependency sorting, disabled artifact resolution, resolver behavior, CLI filtering, analyze execution, archive interactions, and metadata.
Suggested Implementation Order
- Config model, defaults, and validation.
- CLI parsing and propagation of
--artifactsselection. - Runtime artifact catalog.
- Resolver integration for configured artifacts.
- Analyze stage generalization.
- Stage metadata and manifest output recording.
- Archive behavior review.
- Documentation and examples.
This order keeps the most static pieces first, then moves into execution behavior once the configuration contract is explicit and well tested.