403 lines
13 KiB
Go
403 lines
13 KiB
Go
package seriatim
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/adapters/subprocess"
|
|
)
|
|
|
|
// 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":[]}`), 0o644); 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, 0o644); 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"), 0o644); 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"), 0o644); 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}`), 0o644); 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":[]}`), 0o644); 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, 0o644); 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"), 0o644); 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"), 0o644); 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":[]}`), 0o644); 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, 0o644); 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"), 0o644); 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"), 0o644); 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}`), 0o644); 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"), 0o644); 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, 0o644); 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"), 0o644); 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"), 0o644); err != nil {
|
|
return fmt.Errorf("write stderr log %q: %w", req.StderrLogPath, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|