2 Commits

Author SHA1 Message Date
c6632d5576 Bugfix in the seriatim adapter
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
2026-05-27 08:09:22 -05:00
ffc07922c7 Cleanup following the render stage implementation and remove the completed roadmap
All checks were successful
ci/woodpecker/tag/release Pipeline was successful
2026-05-25 08:35:18 -05:00
17 changed files with 52 additions and 235 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -2,7 +2,7 @@
Narratio is a stage-driven Go orchestrator for turning D&D session audio into polished transcripts and generated artifacts. Narratio is a stage-driven Go orchestrator for turning D&D session audio into polished transcripts and generated artifacts.
It runs a deterministic workflow across `prepare`, `transcribe`, `merge`, `polish`, `normalize`, `trim`, `analyze`, and `publish`, with manifest-driven continuation and restore support. It runs a deterministic workflow across `prepare`, `transcribe`, `merge`, `polish`, `normalize`, `trim`, `render`, `analyze`, and `publish`, with manifest-driven continuation and restore support.
```bash ```bash
narratio run 2026-04-04 narratio run 2026-04-04

View File

@@ -198,7 +198,7 @@ Rules:
| `pipeline.render.format` | string | No | `markdown` (only supported value) | | `pipeline.render.format` | string | No | `markdown` (only supported value) |
| `pipeline.render.title` | string | No | empty (falls back to `session.title` when set) | | `pipeline.render.title` | string | No | empty (falls back to `session.title` when set) |
| `pipeline.render.include_timestamps` | bool | No | `true` | | `pipeline.render.include_timestamps` | bool | No | `true` |
| `pipeline.render.include_segment_ids` | bool | No | `false` | | `pipeline.render.include_segment_ids` | bool | No | `true` |
| `pipeline.render.include_metadata` | bool | No | `false` | | `pipeline.render.include_metadata` | bool | No | `false` |
| `pipeline.scriptorium.binary` | string | No | `scriptorium` | | `pipeline.scriptorium.binary` | string | No | `scriptorium` |
| `pipeline.scriptorium.config_path` | string | No | empty | | `pipeline.scriptorium.config_path` | string | No | empty |

View File

@@ -1,206 +0,0 @@
# Roadmap: Render Stage
Status: Completed
This roadmap defines a post-1.0 feature addition: a new `render` stage that uses `seriatim render` to produce human-readable Markdown versions of the final transcript artifacts.
Planned behavior belongs only in this roadmap until implementation lands. Current-behavior docs, examples, and command references must be updated only after the code is implemented and tested.
## Goal
Add a first-class stage between `trim` and `analyze`:
1. `prepare`
2. `transcribe`
3. `merge`
4. `polish`
5. `normalize`
6. `trim`
7. `render`
8. `analyze`
9. `publish`
10. `notify`
The stage renders Markdown versions of:
- `narratio.transcript.final`
- `narratio.transcript.final_trimmed`
The stage produces new built-in artifacts:
| Source ID | Canonical path | Output kind |
| --- | --- | --- |
| `narratio.transcript.final_markdown` | `transcripts/final.md` | `transcript_final_markdown` |
| `narratio.transcript.final_trimmed_markdown` | `transcripts/final.trimmed.md` | `transcript_final_trimmed_markdown` |
Default publish outputs should include:
- `narratio.transcript.final_trimmed`
- `narratio.transcript.final_markdown`
- `narratio.transcript.final_trimmed_markdown`
## Public Contract
Add `pipeline.render` with strict YAML decoding.
Fields:
| Field | Type | Default | Validation |
| --- | --- | --- | --- |
| `enabled` | bool | `true` | optional |
| `format` | string | `markdown` | only `markdown` is supported |
| `title` | string | empty | optional |
| `include_timestamps` | bool | `true` | optional |
| `include_segment_ids` | bool | `false` | optional |
| `include_metadata` | bool | `false` | optional |
Title behavior:
- if `pipeline.render.title` is non-empty, pass it as `--title`;
- otherwise, if `session.title` is non-empty, pass `session.title` as `--title`;
- otherwise, omit `--title` and let Seriatim use its default.
The initial implementation supports only Markdown. Future formats require explicit config validation and artifact naming decisions.
## Implementation Stages
### Stage 1: Artifact, Config, and Adapter Contracts
- Extend the transcript artifact model with the two Markdown built-ins.
- Register Markdown artifacts as text content in the artifact registry so they work in artifact resolution, publish outputs, locks, status, artifacts list, and Scriptorium inputs.
- Add `RenderConfig` under `PipelineConfig` and apply defaults in the config loader/defaulting path.
- Validate `pipeline.render.format` as `markdown` and keep unknown fields rejected by strict YAML decoding.
- Extend the Seriatim adapter interface with `Render(ctx, RenderRequest)`.
- Add subprocess support for `seriatim render` with:
- `--input-file`
- `--output-file`
- `--format markdown`
- optional `--title`
- explicit boolean behavior for timestamps, segment IDs, and metadata.
- Validate render output as non-empty text, not JSON.
- Write Seriatim render stdout/stderr logs and generated invocation config consistently with existing Seriatim stage calls.
### Stage 2: Render Stage Runtime
- Add `renderStage` to the stage package.
- Insert `renderStage{}` into `stage.All()` after `trimStage{}` and before `analyzeStage{}`.
- Make `run-stage render <session_id>` work through the existing stage selection path.
- If render is disabled, mark the stage succeeded with metadata and no outputs.
- If enabled, resolve inputs manifest-first using existing artifact resolution:
- final transcript from `narratio.transcript.final`;
- final trimmed transcript from `narratio.transcript.final_trimmed`.
- Render run-local Markdown outputs first, then materialize canonical outputs:
- `transcripts/final.md`
- `transcripts/final.trimmed.md`
- Record manifest outputs with the new output kinds and source IDs.
- Record metadata for input paths/provenance, canonical/run-local output paths, format, resolved title, boolean render settings, adapter duration/exit code/binary, and adapter metadata.
- On missing required JSON inputs, fail clearly with guidance to run `normalize` or `trim` as appropriate.
### Stage 3: Publish, Analyze, Docs, and Examples
- Update default publish outputs to include both Markdown artifacts in addition to final trimmed JSON.
- Ensure publish output destination derivation works for Markdown built-ins through the shared artifact policy path.
- Ensure Scriptorium input validation accepts Markdown built-ins as ordinary built-in sources.
- Update analyze missing-input guidance so required Markdown built-in inputs point operators to `run-stage render`.
- Update user/operator/internal docs only after implementation:
- `docs/cli.md`
- `docs/config.md`
- `docs/operations.md`
- `docs/internal/README.md`
- `docs/internal/artifacts.md`
- new `docs/internal/stage-render.md`
- `docs/integrations/seriatim.md`
- Update examples only where useful; defaults should work without an explicit `pipeline.render` block.
## Seriatim Adapter Contract
Add a narrow render request/result beside the existing merge, normalize, and trim contracts.
Request fields:
- binary
- input transcript path
- output Markdown path
- format
- title
- include timestamps
- include segment IDs
- include metadata
- stdout log path
- stderr log path
- generated config path
- timeout
Result fields:
- output path
- stdout log path
- stderr log path
- generated config path
- exit code
- duration
- invoked binary
- format
- title
- metadata
The adapter owns subprocess command construction and validation of the non-empty output file. Stage logic should express intent in Narratio terms and should not construct subprocess arguments directly.
## Testing Guidance
Focused tests:
- `internal/artifactmodel` and `internal/artifacts`
- new Markdown source IDs, canonical paths, output kinds, producer stage, text validation, catalog ordering;
- resolver fallback from canonical Markdown paths;
- publish destination derivation for Markdown built-ins.
- `internal/config`
- render defaults;
- strict decode rejects unknown render fields;
- invalid format fails validation;
- default publish outputs include final trimmed JSON plus both Markdown artifacts.
- `internal/adapters/seriatim`
- render command args;
- title omission vs explicit title;
- boolean flag behavior;
- generated config;
- stdout/stderr logs;
- non-empty output validation;
- failure wrapping.
- `internal/stage`
- render resolves final and final-trimmed inputs from manifest outputs before canonical fallback;
- render writes run-local outputs and materializes canonical Markdown outputs;
- render records manifest outputs, logs, generated configs, and metadata;
- disabled render succeeds without outputs;
- missing final/final-trimmed inputs fail clearly.
- `internal/app`
- full plan order includes `render`;
- `run-stage render <session_id>` works;
- force rerunning render marks analyze, publish, and notify stale;
- status and artifacts list include Markdown built-ins;
- publish defaults include Markdown outputs.
Validation commands:
- `go test ./internal/artifactmodel ./internal/artifacts -v`
- `go test ./internal/config -v`
- `go test ./internal/adapters/seriatim -v`
- `go test ./internal/stage -run Render -v`
- `go test ./internal/app -run 'Plan|RunStage|Publish|Artifacts|Status' -v`
- `go test ./...`
## Non-Goals
- Do not change existing JSON transcript source IDs, canonical paths, or output kinds.
- Do not make Markdown output paths configurable in the first implementation.
- Do not add additional render formats before the format naming and artifact naming contract is defined.
- Do not move Seriatim subprocess details into stage logic.
- Do not document the render stage as implemented outside this roadmap until implementation lands.
## Assumptions
- `render` is enabled by default.
- Markdown canonical paths are fixed built-in artifact paths.
- `format: markdown` is the only supported initial format.
- Both Markdown outputs are included in default publish outputs.
- Existing publish layout and current-state commit behavior remain unchanged.

View File

@@ -577,9 +577,9 @@ func buildRenderArgs(req RenderRequest, format string) []string {
"--input-file", req.InputTranscriptPath, "--input-file", req.InputTranscriptPath,
"--output-file", req.OutputRenderedPath, "--output-file", req.OutputRenderedPath,
"--format", format, "--format", format,
"--include-timestamps", strconv.FormatBool(req.IncludeTimestamps), "--include-timestamps=" + strconv.FormatBool(req.IncludeTimestamps),
"--include-segment-ids", strconv.FormatBool(req.IncludeSegmentIDs), "--include-segment-ids=" + strconv.FormatBool(req.IncludeSegmentIDs),
"--include-metadata", strconv.FormatBool(req.IncludeMetadata), "--include-metadata=" + strconv.FormatBool(req.IncludeMetadata),
} }
if strings.TrimSpace(req.Title) != "" { if strings.TrimSpace(req.Title) != "" {
args = append(args, "--title", req.Title) args = append(args, "--title", req.Title)

View File

@@ -628,9 +628,9 @@ func TestSubprocessRunnerRenderSuccessInvocationAndProvenance(t *testing.T) {
"--input-file", req.InputTranscriptPath, "--input-file", req.InputTranscriptPath,
"--output-file", req.OutputRenderedPath, "--output-file", req.OutputRenderedPath,
"--format", req.Format, "--format", req.Format,
"--include-timestamps", "true", "--include-timestamps=true",
"--include-segment-ids", "false", "--include-segment-ids=true",
"--include-metadata", "true", "--include-metadata=false",
"--title", req.Title, "--title", req.Title,
} }
if strings.Join(rec.Args, "\n") != strings.Join(wantArgs, "\n") { if strings.Join(rec.Args, "\n") != strings.Join(wantArgs, "\n") {
@@ -946,8 +946,8 @@ func renderReqForTest(t *testing.T) RenderRequest {
Format: "markdown", Format: "markdown",
Title: "Session 42", Title: "Session 42",
IncludeTimestamps: true, IncludeTimestamps: true,
IncludeSegmentIDs: false, IncludeSegmentIDs: true,
IncludeMetadata: true, IncludeMetadata: false,
GeneratedConfigPath: filepath.Join(dir, "seriatim.render.generated.yml"), GeneratedConfigPath: filepath.Join(dir, "seriatim.render.generated.yml"),
StdoutLogPath: filepath.Join(dir, "seriatim.render.stdout.log"), StdoutLogPath: filepath.Join(dir, "seriatim.render.stdout.log"),
StderrLogPath: filepath.Join(dir, "seriatim.render.stderr.log"), StderrLogPath: filepath.Join(dir, "seriatim.render.stderr.log"),

View File

@@ -216,7 +216,7 @@ type RenderConfig struct {
Format string `yaml:"format"` Format string `yaml:"format"`
Title string `yaml:"title"` Title string `yaml:"title"`
IncludeTimestamps *bool `yaml:"include_timestamps"` IncludeTimestamps *bool `yaml:"include_timestamps"`
IncludeSegmentIDs bool `yaml:"include_segment_ids"` IncludeSegmentIDs *bool `yaml:"include_segment_ids"`
IncludeMetadata bool `yaml:"include_metadata"` IncludeMetadata bool `yaml:"include_metadata"`
} }

View File

@@ -44,7 +44,7 @@ const (
DefaultRenderFormat = "markdown" DefaultRenderFormat = "markdown"
DefaultRenderTitle = "" DefaultRenderTitle = ""
DefaultRenderTimestamps = true DefaultRenderTimestamps = true
DefaultRenderSegmentIDs = false DefaultRenderSegmentIDs = true
DefaultRenderMetadata = false DefaultRenderMetadata = false
DefaultNormalizeOutputPath = artifactmodel.TranscriptPathFinal DefaultNormalizeOutputPath = artifactmodel.TranscriptPathFinal

View File

@@ -527,6 +527,9 @@ func applyRenderDefaults(cfg **RenderConfig) {
if (*cfg).IncludeTimestamps == nil { if (*cfg).IncludeTimestamps == nil {
(*cfg).IncludeTimestamps = boolPtr(DefaultRenderTimestamps) (*cfg).IncludeTimestamps = boolPtr(DefaultRenderTimestamps)
} }
if (*cfg).IncludeSegmentIDs == nil {
(*cfg).IncludeSegmentIDs = boolPtr(DefaultRenderSegmentIDs)
}
} }
func applyNormalizeDefaults(cfg *NormalizeConfig) { func applyNormalizeDefaults(cfg *NormalizeConfig) {

View File

@@ -30,8 +30,8 @@ func TestRenderLoadAndValidate(t *testing.T) {
if cfg.Pipeline.Render.IncludeTimestamps == nil || !*cfg.Pipeline.Render.IncludeTimestamps { if cfg.Pipeline.Render.IncludeTimestamps == nil || !*cfg.Pipeline.Render.IncludeTimestamps {
t.Fatalf("render.include_timestamps = %#v, want true", cfg.Pipeline.Render.IncludeTimestamps) t.Fatalf("render.include_timestamps = %#v, want true", cfg.Pipeline.Render.IncludeTimestamps)
} }
if cfg.Pipeline.Render.IncludeSegmentIDs { if cfg.Pipeline.Render.IncludeSegmentIDs == nil || !*cfg.Pipeline.Render.IncludeSegmentIDs {
t.Fatalf("render.include_segment_ids = true, want false") t.Fatalf("render.include_segment_ids = %#v, want true", cfg.Pipeline.Render.IncludeSegmentIDs)
} }
if cfg.Pipeline.Render.IncludeMetadata { if cfg.Pipeline.Render.IncludeMetadata {
t.Fatalf("render.include_metadata = true, want false") t.Fatalf("render.include_metadata = true, want false")
@@ -59,14 +59,26 @@ func TestRenderLoadAndValidate(t *testing.T) {
if cfg.Pipeline.Render.IncludeTimestamps == nil || *cfg.Pipeline.Render.IncludeTimestamps { if cfg.Pipeline.Render.IncludeTimestamps == nil || *cfg.Pipeline.Render.IncludeTimestamps {
t.Fatalf("render.include_timestamps = %#v, want false", cfg.Pipeline.Render.IncludeTimestamps) t.Fatalf("render.include_timestamps = %#v, want false", cfg.Pipeline.Render.IncludeTimestamps)
} }
if !cfg.Pipeline.Render.IncludeSegmentIDs { if cfg.Pipeline.Render.IncludeSegmentIDs == nil || !*cfg.Pipeline.Render.IncludeSegmentIDs {
t.Fatalf("render.include_segment_ids = false, want true") t.Fatalf("render.include_segment_ids = %#v, want true", cfg.Pipeline.Render.IncludeSegmentIDs)
} }
if !cfg.Pipeline.Render.IncludeMetadata { if !cfg.Pipeline.Render.IncludeMetadata {
t.Fatalf("render.include_metadata = false, want true") t.Fatalf("render.include_metadata = false, want true")
} }
}, },
}, },
{
name: "explicit segment ids false overrides default",
renderYAML: `render:
include_segment_ids: false
`,
assert: func(t *testing.T, cfg *Config) {
t.Helper()
if cfg.Pipeline.Render.IncludeSegmentIDs == nil || *cfg.Pipeline.Render.IncludeSegmentIDs {
t.Fatalf("render.include_segment_ids = %#v, want false", cfg.Pipeline.Render.IncludeSegmentIDs)
}
},
},
{ {
name: "invalid render format fails", name: "invalid render format fails",
renderYAML: `render: renderYAML: `render:

View File

@@ -312,6 +312,9 @@ func validateRender(cfg *RenderConfig) error {
if cfg.IncludeTimestamps == nil { if cfg.IncludeTimestamps == nil {
return fmt.Errorf("pipeline.render.include_timestamps must be set (defaults should populate this)") return fmt.Errorf("pipeline.render.include_timestamps must be set (defaults should populate this)")
} }
if cfg.IncludeSegmentIDs == nil {
return fmt.Errorf("pipeline.render.include_segment_ids must be set (defaults should populate this)")
}
format := strings.TrimSpace(cfg.Format) format := strings.TrimSpace(cfg.Format)
if format != "markdown" { if format != "markdown" {
return fmt.Errorf("pipeline.render.format must be markdown") return fmt.Errorf("pipeline.render.format must be markdown")

View File

@@ -672,7 +672,7 @@ func resolveScriptoriumInput(
return "", false, nil, fmt.Errorf("trimmed transcript input is unavailable; run trim stage first") return "", false, nil, fmt.Errorf("trimmed transcript input is unavailable; run trim stage first")
case artifacts.ArtifactTranscriptFinalMarkdown, artifacts.ArtifactTranscriptFinalTrimmedMarkdown: case artifacts.ArtifactTranscriptFinalMarkdown, artifacts.ArtifactTranscriptFinalTrimmedMarkdown:
return "", false, nil, fmt.Errorf( return "", false, nil, fmt.Errorf(
"rendered markdown transcript input is unavailable for source %q; run narratio run-stage --force render %s", "rendered markdown transcript input is unavailable for source %q; run narratio run-stage render %s --force",
descriptor.Source.ID, descriptor.Source.ID,
paths.SessionID, paths.SessionID,
) )

View File

@@ -1083,7 +1083,7 @@ func TestAnalyzeFailsWhenRenderedMarkdownTranscriptMissing(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected error, got nil") t.Fatal("expected error, got nil")
} }
if !strings.Contains(err.Error(), "run narratio run-stage --force render") { if !strings.Contains(err.Error(), "run narratio run-stage render") || !strings.Contains(err.Error(), "--force") {
t.Fatalf("error = %q, want render guidance", err.Error()) t.Fatalf("error = %q, want render guidance", err.Error())
} }
} }
@@ -1101,7 +1101,7 @@ func TestAnalyzeFailsWhenRenderedTrimmedMarkdownTranscriptMissing(t *testing.T)
if err == nil { if err == nil {
t.Fatal("expected error, got nil") t.Fatal("expected error, got nil")
} }
if !strings.Contains(err.Error(), "run narratio run-stage --force render") { if !strings.Contains(err.Error(), "run narratio run-stage render") || !strings.Contains(err.Error(), "--force") {
t.Fatalf("error = %q, want render guidance", err.Error()) t.Fatalf("error = %q, want render guidance", err.Error())
} }
} }

View File

@@ -99,7 +99,7 @@ func (normalizeStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (
stderrPath = filepath.Join(runLayout.LogsDir, "seriatim.normalize.stderr.log") stderrPath = filepath.Join(runLayout.LogsDir, "seriatim.normalize.stderr.log")
generatedConfigPath = filepath.Join(runLayout.ConfigDir, "seriatim.normalize.generated.yml") generatedConfigPath = filepath.Join(runLayout.ConfigDir, "seriatim.normalize.generated.yml")
} }
timeout, err := resolveTrimSeriatimTimeout(env.Config.Pipeline.Seriatim.Timeout) timeout, err := resolveSeriatimStageTimeout(env.Config.Pipeline.Seriatim.Timeout)
if err != nil { if err != nil {
return nil, fmt.Errorf("normalize: resolve seriatim timeout: %w", err) return nil, fmt.Errorf("normalize: resolve seriatim timeout: %w", err)
} }

View File

@@ -69,7 +69,10 @@ func (renderStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
} }
title := resolveRenderTitle(renderCfg, env.Config.Session) title := resolveRenderTitle(renderCfg, env.Config.Session)
includeTimestamps := renderCfg.IncludeTimestamps == nil || *renderCfg.IncludeTimestamps includeTimestamps := renderCfg.IncludeTimestamps == nil || *renderCfg.IncludeTimestamps
includeSegmentIDs := renderCfg.IncludeSegmentIDs includeSegmentIDs := config.DefaultRenderSegmentIDs
if renderCfg.IncludeSegmentIDs != nil {
includeSegmentIDs = *renderCfg.IncludeSegmentIDs
}
includeMetadata := renderCfg.IncludeMetadata includeMetadata := renderCfg.IncludeMetadata
meta := map[string]any{ meta := map[string]any{
@@ -131,7 +134,7 @@ func (renderStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*St
finalTrimmedGeneratedConfigPath = filepath.Join(runLayout.ConfigDir, "seriatim.render.final_trimmed.generated.yml") finalTrimmedGeneratedConfigPath = filepath.Join(runLayout.ConfigDir, "seriatim.render.final_trimmed.generated.yml")
} }
timeout, err := resolveTrimSeriatimTimeout(env.Config.Pipeline.Seriatim.Timeout) timeout, err := resolveSeriatimStageTimeout(env.Config.Pipeline.Seriatim.Timeout)
if err != nil { if err != nil {
return nil, fmt.Errorf("render: resolve seriatim timeout: %w", err) return nil, fmt.Errorf("render: resolve seriatim timeout: %w", err)
} }
@@ -237,11 +240,12 @@ func renderConfigOrDefault(cfg *config.RenderConfig) *config.RenderConfig {
} }
enabled := true enabled := true
includeTimestamps := true includeTimestamps := true
includeSegmentIDs := config.DefaultRenderSegmentIDs
return &config.RenderConfig{ return &config.RenderConfig{
Enabled: &enabled, Enabled: &enabled,
Format: config.DefaultRenderFormat, Format: config.DefaultRenderFormat,
IncludeTimestamps: &includeTimestamps, IncludeTimestamps: &includeTimestamps,
IncludeSegmentIDs: config.DefaultRenderSegmentIDs, IncludeSegmentIDs: &includeSegmentIDs,
IncludeMetadata: config.DefaultRenderMetadata, IncludeMetadata: config.DefaultRenderMetadata,
} }
} }
@@ -260,7 +264,7 @@ func wrapRenderInputResolveError(err error, sessionID, sourceID, guidanceStage s
var notFound *artifacts.SessionArtifactNotFoundError var notFound *artifacts.SessionArtifactNotFoundError
if errors.As(err, &notFound) { if errors.As(err, &notFound) {
return fmt.Errorf( return fmt.Errorf(
"render: required input %q is unavailable; run narratio run-stage --force %s %s", "render: required input %q is unavailable; run narratio run-stage %s %s --force",
sourceID, sourceID,
guidanceStage, guidanceStage,
sessionID, sessionID,

View File

@@ -103,7 +103,7 @@ func TestRenderStageFailsWhenFinalInputMissing(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected error, got nil") t.Fatal("expected error, got nil")
} }
if !strings.Contains(err.Error(), "run narratio run-stage --force normalize") { if !strings.Contains(err.Error(), "run narratio run-stage normalize") || !strings.Contains(err.Error(), "--force") {
t.Fatalf("error = %q, want normalize guidance", err.Error()) t.Fatalf("error = %q, want normalize guidance", err.Error())
} }
} }
@@ -117,7 +117,7 @@ func TestRenderStageFailsWhenFinalTrimmedInputMissing(t *testing.T) {
if err == nil { if err == nil {
t.Fatal("expected error, got nil") t.Fatal("expected error, got nil")
} }
if !strings.Contains(err.Error(), "run narratio run-stage --force trim") { if !strings.Contains(err.Error(), "run narratio run-stage trim") || !strings.Contains(err.Error(), "--force") {
t.Fatalf("error = %q, want trim guidance", err.Error()) t.Fatalf("error = %q, want trim guidance", err.Error())
} }
} }
@@ -165,6 +165,7 @@ func setupRenderEnv(t *testing.T) (*Env, *manifest.Manifest, *seriatim.FakeRunne
enabled := true enabled := true
includeTimestamps := true includeTimestamps := true
includeSegmentIDs := false
seriatimReport := false seriatimReport := false
cfg := &config.Config{ cfg := &config.Config{
PipelinePath: pipelinePath, PipelinePath: pipelinePath,
@@ -183,7 +184,7 @@ func setupRenderEnv(t *testing.T) (*Env, *manifest.Manifest, *seriatim.FakeRunne
Format: "markdown", Format: "markdown",
Title: "Pipeline Title", Title: "Pipeline Title",
IncludeTimestamps: &includeTimestamps, IncludeTimestamps: &includeTimestamps,
IncludeSegmentIDs: false, IncludeSegmentIDs: &includeSegmentIDs,
IncludeMetadata: false, IncludeMetadata: false,
}, },
}, },

View File

@@ -315,7 +315,7 @@ func (trimStage) Run(ctx context.Context, env *Env, m *manifest.Manifest) (*Stag
trimStderrLogPath = filepath.Join(runLayout.LogsDir, "seriatim.trim.stderr.log") trimStderrLogPath = filepath.Join(runLayout.LogsDir, "seriatim.trim.stderr.log")
trimGeneratedConfigPath = filepath.Join(runLayout.ConfigDir, "seriatim.trim.generated.yml") trimGeneratedConfigPath = filepath.Join(runLayout.ConfigDir, "seriatim.trim.generated.yml")
} }
trimTimeout, err := resolveTrimSeriatimTimeout(env.Config.Pipeline.Seriatim.Timeout) trimTimeout, err := resolveSeriatimStageTimeout(env.Config.Pipeline.Seriatim.Timeout)
if err != nil { if err != nil {
return nil, fmt.Errorf("trim: resolve seriatim timeout: %w", err) return nil, fmt.Errorf("trim: resolve seriatim timeout: %w", err)
} }
@@ -398,7 +398,7 @@ func copyTranscript(store artifacts.Store, src, dst string) error {
return nil return nil
} }
func resolveTrimSeriatimTimeout(raw string) (time.Duration, error) { func resolveSeriatimStageTimeout(raw string) (time.Duration, error) {
trimmed := strings.TrimSpace(raw) trimmed := strings.TrimSpace(raw)
if trimmed == "" { if trimmed == "" {
return 0, nil return 0, nil