package seriatim import ( "context" "fmt" "gitea.maximumdirect.net/eric/narratio/internal/adapters/subprocess" "gitea.maximumdirect.net/eric/narratio/internal/fileops" ) // NoopRunner is a deterministic no-op seriatim adapter. type NoopRunner struct{} // Run returns the requested output path with placeholder metadata. func (n *NoopRunner) Run(ctx context.Context, req MergeRequest) (MergeResult, error) { if err := ctx.Err(); err != nil { return MergeResult{}, err } if err := materializePlaceholders(req); err != nil { return MergeResult{}, err } return MergeResult{ MergedTranscriptPath: req.OutputMergedTranscriptPath, ReportPath: req.ReportPath, StdoutLogPath: req.StdoutLogPath, StderrLogPath: req.StderrLogPath, GeneratedConfigPath: req.GeneratedConfigPath, InvokedBinary: "noop", Metadata: map[string]any{"placeholder": true}, }, nil } // Trim returns the requested output path with placeholder metadata. func (n *NoopRunner) Trim(ctx context.Context, req TrimRequest) (TrimResult, error) { if err := ctx.Err(); err != nil { return TrimResult{}, err } if err := materializeTrimPlaceholders(req); err != nil { return TrimResult{}, err } return TrimResult{ OutputTrimmedPath: req.OutputTrimmedPath, StdoutLogPath: req.StdoutLogPath, StderrLogPath: req.StderrLogPath, GeneratedConfigPath: req.GeneratedConfigPath, InvokedBinary: "noop", KeepSelector: req.KeepSelector, Metadata: map[string]any{"placeholder": true}, }, nil } // Normalize returns the requested output path with placeholder metadata. func (n *NoopRunner) Normalize(ctx context.Context, req NormalizeRequest) (NormalizeResult, error) { if err := ctx.Err(); err != nil { return NormalizeResult{}, err } if err := materializeNormalizePlaceholders(req); err != nil { return NormalizeResult{}, err } return NormalizeResult{ OutputNormalizedPath: req.OutputNormalizedPath, ReportPath: req.ReportPath, StdoutLogPath: req.StdoutLogPath, StderrLogPath: req.StderrLogPath, GeneratedConfigPath: req.GeneratedConfigPath, InvokedBinary: "noop", OutputSchema: req.OutputSchema, Metadata: map[string]any{"placeholder": true}, }, nil } // Render returns the requested output path with placeholder metadata. func (n *NoopRunner) Render(ctx context.Context, req RenderRequest) (RenderResult, error) { if err := ctx.Err(); err != nil { return RenderResult{}, err } if err := materializeRenderPlaceholders(req); err != nil { return RenderResult{}, err } return RenderResult{ OutputRenderedPath: req.OutputRenderedPath, StdoutLogPath: req.StdoutLogPath, StderrLogPath: req.StderrLogPath, GeneratedConfigPath: req.GeneratedConfigPath, InvokedBinary: "noop", Format: req.Format, Title: req.Title, Metadata: map[string]any{"placeholder": true}, }, nil } // FakeRunner captures merge requests and returns deterministic responses. type FakeRunner struct { Requests []MergeRequest Err error Result MergeResult NormalizeRequests []NormalizeRequest NormalizeErr error NormalizeResult NormalizeResult TrimRequests []TrimRequest TrimErr error TrimResult TrimResult RenderRequests []RenderRequest RenderErr error RenderResult RenderResult } // Run records request and returns configured response. func (f *FakeRunner) Run(ctx context.Context, req MergeRequest) (MergeResult, error) { if err := ctx.Err(); err != nil { return MergeResult{}, err } f.Requests = append(f.Requests, req) if f.Err != nil { return MergeResult{}, f.Err } if err := materializePlaceholders(req); err != nil { return MergeResult{}, err } res := f.Result if res.MergedTranscriptPath == "" { res.MergedTranscriptPath = req.OutputMergedTranscriptPath } if res.ReportPath == "" { res.ReportPath = req.ReportPath } if res.StdoutLogPath == "" { res.StdoutLogPath = req.StdoutLogPath } if res.StderrLogPath == "" { res.StderrLogPath = req.StderrLogPath } if res.GeneratedConfigPath == "" { res.GeneratedConfigPath = req.GeneratedConfigPath } if res.InvokedBinary == "" { res.InvokedBinary = "fake" } if res.Metadata == nil { res.Metadata = map[string]any{"fake": true} } return res, nil } // Trim records request and returns configured response. func (f *FakeRunner) Trim(ctx context.Context, req TrimRequest) (TrimResult, error) { if err := ctx.Err(); err != nil { return TrimResult{}, err } f.TrimRequests = append(f.TrimRequests, req) if f.TrimErr != nil { return TrimResult{}, f.TrimErr } if err := materializeTrimPlaceholders(req); err != nil { return TrimResult{}, err } res := f.TrimResult if res.OutputTrimmedPath == "" { res.OutputTrimmedPath = req.OutputTrimmedPath } if res.StdoutLogPath == "" { res.StdoutLogPath = req.StdoutLogPath } if res.StderrLogPath == "" { res.StderrLogPath = req.StderrLogPath } if res.GeneratedConfigPath == "" { res.GeneratedConfigPath = req.GeneratedConfigPath } if res.InvokedBinary == "" { res.InvokedBinary = "fake" } if res.KeepSelector == "" { res.KeepSelector = req.KeepSelector } if res.Metadata == nil { res.Metadata = map[string]any{"fake": true} } return res, nil } // Normalize records request and returns configured response. func (f *FakeRunner) Normalize(ctx context.Context, req NormalizeRequest) (NormalizeResult, error) { if err := ctx.Err(); err != nil { return NormalizeResult{}, err } f.NormalizeRequests = append(f.NormalizeRequests, req) if f.NormalizeErr != nil { return NormalizeResult{}, f.NormalizeErr } if err := materializeNormalizePlaceholders(req); err != nil { return NormalizeResult{}, err } res := f.NormalizeResult if res.OutputNormalizedPath == "" { res.OutputNormalizedPath = req.OutputNormalizedPath } if res.ReportPath == "" { res.ReportPath = req.ReportPath } if res.StdoutLogPath == "" { res.StdoutLogPath = req.StdoutLogPath } if res.StderrLogPath == "" { res.StderrLogPath = req.StderrLogPath } if res.GeneratedConfigPath == "" { res.GeneratedConfigPath = req.GeneratedConfigPath } if res.InvokedBinary == "" { res.InvokedBinary = "fake" } if res.OutputSchema == "" { res.OutputSchema = req.OutputSchema } if res.Metadata == nil { res.Metadata = map[string]any{"fake": true} } return res, nil } // Render records request and returns configured response. func (f *FakeRunner) Render(ctx context.Context, req RenderRequest) (RenderResult, error) { if err := ctx.Err(); err != nil { return RenderResult{}, err } f.RenderRequests = append(f.RenderRequests, req) if f.RenderErr != nil { return RenderResult{}, f.RenderErr } if err := materializeRenderPlaceholders(req); err != nil { return RenderResult{}, err } res := f.RenderResult if res.OutputRenderedPath == "" { res.OutputRenderedPath = req.OutputRenderedPath } if res.StdoutLogPath == "" { res.StdoutLogPath = req.StdoutLogPath } if res.StderrLogPath == "" { res.StderrLogPath = req.StderrLogPath } if res.GeneratedConfigPath == "" { res.GeneratedConfigPath = req.GeneratedConfigPath } if res.InvokedBinary == "" { res.InvokedBinary = "fake" } if res.Format == "" { res.Format = req.Format } if res.Title == "" { res.Title = req.Title } if res.Metadata == nil { res.Metadata = map[string]any{"fake": true} } return res, nil } func materializePlaceholders(req MergeRequest) error { if req.OutputMergedTranscriptPath != "" { if err := subprocess.WriteFileAtomic(req.OutputMergedTranscriptPath, []byte(`{"schema":"seriatim.intermediate.v1","segments":[]}`), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write merged transcript %q: %w", req.OutputMergedTranscriptPath, err) } } if req.GeneratedConfigPath != "" { payload := map[string]any{ "schema": "seriatim.generated.v1", "placeholder": true, "input_transcript_paths": req.InputTranscriptPaths, "output_path": req.OutputMergedTranscriptPath, } if err := subprocess.WriteYAMLAtomic(req.GeneratedConfigPath, payload, fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write generated config %q: %w", req.GeneratedConfigPath, err) } } if req.StdoutLogPath != "" { if err := subprocess.WriteFileAtomic(req.StdoutLogPath, []byte("seriatim noop/fake stdout placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stdout log %q: %w", req.StdoutLogPath, err) } } if req.StderrLogPath != "" { if err := subprocess.WriteFileAtomic(req.StderrLogPath, []byte("seriatim noop/fake stderr placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stderr log %q: %w", req.StderrLogPath, err) } } if req.ReportPath != "" { if err := subprocess.WriteFileAtomic(req.ReportPath, []byte(`{"schema":"seriatim.report.v1","placeholder":true}`), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write report %q: %w", req.ReportPath, err) } } return nil } func materializeTrimPlaceholders(req TrimRequest) error { if req.OutputTrimmedPath != "" { if err := subprocess.WriteFileAtomic(req.OutputTrimmedPath, []byte(`{"schema":"seriatim.intermediate.v1","segments":[]}`), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write trimmed transcript %q: %w", req.OutputTrimmedPath, err) } } if req.GeneratedConfigPath != "" { payload := map[string]any{ "schema": "seriatim.generated.v1", "placeholder": true, "command": "trim", "input_path": req.InputTranscriptPath, "output_path": req.OutputTrimmedPath, "keep_selector": req.KeepSelector, } if err := subprocess.WriteYAMLAtomic(req.GeneratedConfigPath, payload, fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write generated config %q: %w", req.GeneratedConfigPath, err) } } if req.StdoutLogPath != "" { if err := subprocess.WriteFileAtomic(req.StdoutLogPath, []byte("seriatim noop/fake trim stdout placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stdout log %q: %w", req.StdoutLogPath, err) } } if req.StderrLogPath != "" { if err := subprocess.WriteFileAtomic(req.StderrLogPath, []byte("seriatim noop/fake trim stderr placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stderr log %q: %w", req.StderrLogPath, err) } } return nil } func materializeNormalizePlaceholders(req NormalizeRequest) error { if req.OutputNormalizedPath != "" { if err := subprocess.WriteFileAtomic(req.OutputNormalizedPath, []byte(`{"schema":"seriatim.intermediate.v1","segments":[]}`), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write normalized transcript %q: %w", req.OutputNormalizedPath, err) } } if req.GeneratedConfigPath != "" { payload := map[string]any{ "schema": "seriatim.generated.v1", "placeholder": true, "command": "normalize", "input_path": req.InputTranscriptPath, "output_path": req.OutputNormalizedPath, "output_schema": req.OutputSchema, } if req.ReportPath != "" { payload["report_path"] = req.ReportPath } if err := subprocess.WriteYAMLAtomic(req.GeneratedConfigPath, payload, fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write generated config %q: %w", req.GeneratedConfigPath, err) } } if req.StdoutLogPath != "" { if err := subprocess.WriteFileAtomic(req.StdoutLogPath, []byte("seriatim noop/fake normalize stdout placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stdout log %q: %w", req.StdoutLogPath, err) } } if req.StderrLogPath != "" { if err := subprocess.WriteFileAtomic(req.StderrLogPath, []byte("seriatim noop/fake normalize stderr placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stderr log %q: %w", req.StderrLogPath, err) } } if req.ReportPath != "" { if err := subprocess.WriteFileAtomic(req.ReportPath, []byte(`{"schema":"seriatim.report.v1","placeholder":true}`), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write report %q: %w", req.ReportPath, err) } } return nil } func materializeRenderPlaceholders(req RenderRequest) error { if req.OutputRenderedPath != "" { if err := subprocess.WriteFileAtomic(req.OutputRenderedPath, []byte("# Transcript\n\nRendered markdown placeholder.\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write rendered transcript %q: %w", req.OutputRenderedPath, err) } } if req.GeneratedConfigPath != "" { payload := map[string]any{ "schema": "seriatim.generated.v1", "placeholder": true, "command": "render", "input_path": req.InputTranscriptPath, "output_path": req.OutputRenderedPath, "format": req.Format, "title": req.Title, "include_timestamps": req.IncludeTimestamps, "include_segment_ids": req.IncludeSegmentIDs, "include_metadata": req.IncludeMetadata, } if err := subprocess.WriteYAMLAtomic(req.GeneratedConfigPath, payload, fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write generated config %q: %w", req.GeneratedConfigPath, err) } } if req.StdoutLogPath != "" { if err := subprocess.WriteFileAtomic(req.StdoutLogPath, []byte("seriatim noop/fake render stdout placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stdout log %q: %w", req.StdoutLogPath, err) } } if req.StderrLogPath != "" { if err := subprocess.WriteFileAtomic(req.StderrLogPath, []byte("seriatim noop/fake render stderr placeholder\n"), fileops.WorkspaceFileMode); err != nil { return fmt.Errorf("write stderr log %q: %w", req.StderrLogPath, err) } } return nil }