Cleaned up and removed legacy configuration surfaces
This commit is contained in:
@@ -1,40 +0,0 @@
|
||||
package analyzer
|
||||
|
||||
import "context"
|
||||
|
||||
// NoopRunner is a deterministic no-op analyzer adapter.
|
||||
type NoopRunner struct{}
|
||||
|
||||
// Run returns the requested output path with placeholder metadata.
|
||||
func (n *NoopRunner) Run(ctx context.Context, req AnalyzeRequest) (AnalyzeResult, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return AnalyzeResult{}, err
|
||||
}
|
||||
return AnalyzeResult{ArtifactPath: req.OutputPath, Metadata: map[string]any{"placeholder": true}}, nil
|
||||
}
|
||||
|
||||
// FakeRunner captures analyze requests and returns deterministic responses.
|
||||
type FakeRunner struct {
|
||||
Requests []AnalyzeRequest
|
||||
Err error
|
||||
Result AnalyzeResult
|
||||
}
|
||||
|
||||
// Run records request and returns configured response.
|
||||
func (f *FakeRunner) Run(ctx context.Context, req AnalyzeRequest) (AnalyzeResult, error) {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return AnalyzeResult{}, err
|
||||
}
|
||||
f.Requests = append(f.Requests, req)
|
||||
if f.Err != nil {
|
||||
return AnalyzeResult{}, f.Err
|
||||
}
|
||||
res := f.Result
|
||||
if res.ArtifactPath == "" {
|
||||
res.ArtifactPath = req.OutputPath
|
||||
}
|
||||
if res.Metadata == nil {
|
||||
res.Metadata = map[string]any{"fake": true}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFakeRunnerCapturesRequestAndReturnsPath(t *testing.T) {
|
||||
fake := &FakeRunner{}
|
||||
req := AnalyzeRequest{ArtifactType: "session-log", OutputPath: "artifacts/session-log.md"}
|
||||
|
||||
res, err := fake.Run(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
if len(fake.Requests) != 1 || fake.Requests[0].ArtifactType != "session-log" {
|
||||
t.Fatalf("requests = %#v, want captured request", fake.Requests)
|
||||
}
|
||||
if res.ArtifactPath != req.OutputPath {
|
||||
t.Fatalf("artifact path = %q, want %q", res.ArtifactPath, req.OutputPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeRunnerError(t *testing.T) {
|
||||
fake := &FakeRunner{Err: errors.New("boom")}
|
||||
_, err := fake.Run(context.Background(), AnalyzeRequest{})
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// Package analyzer declares the adapter contract for artifact analysis generation.
|
||||
package analyzer
|
||||
|
||||
import "context"
|
||||
|
||||
// TODO: implement analyzer integration once the analyzer contract is finalized.
|
||||
|
||||
// Runner is the adapter boundary for analyzer invocations.
|
||||
type Runner interface {
|
||||
Run(ctx context.Context, req AnalyzeRequest) (AnalyzeResult, error)
|
||||
}
|
||||
|
||||
// AnalyzeRequest describes one analyzer artifact generation request.
|
||||
type AnalyzeRequest struct {
|
||||
ArtifactType string
|
||||
ProcessedTranscriptPath string
|
||||
ContextReferences []string
|
||||
OutputPath string
|
||||
GeneratedConfigPath string
|
||||
StdoutLogPath string
|
||||
StderrLogPath string
|
||||
}
|
||||
|
||||
// AnalyzeResult describes analyzer output.
|
||||
type AnalyzeResult struct {
|
||||
ArtifactPath string
|
||||
Metadata map[string]any
|
||||
}
|
||||
@@ -203,8 +203,6 @@ seriatim:
|
||||
audita:
|
||||
binary: ` + auditaBinary + `
|
||||
llm_api_key_env: OPENROUTER_API_KEY
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`
|
||||
@@ -268,8 +266,6 @@ seriatim:
|
||||
binary: seriatim
|
||||
audita:
|
||||
binary: audita
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`
|
||||
@@ -435,10 +431,6 @@ seriatim:
|
||||
report: true
|
||||
audita:
|
||||
binary: ` + auditaBinary + `
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
artifacts:
|
||||
output_dir: artifacts
|
||||
notification:
|
||||
timeout: 10s
|
||||
`
|
||||
|
||||
@@ -108,8 +108,6 @@ seriatim:
|
||||
binary: seriatim
|
||||
audita:
|
||||
binary: audita
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`
|
||||
|
||||
@@ -1067,8 +1067,6 @@ func TestBuildDefaultRunnersWithOmittedToolSections(t *testing.T) {
|
||||
root: ` + t.TempDir() + `
|
||||
whisperx:
|
||||
transcribe_url: https://example.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`
|
||||
|
||||
@@ -270,7 +270,7 @@ func ResolvePreviousSessionArtifactWithCatalog(
|
||||
ID: source,
|
||||
Path: candidate,
|
||||
ProducerStage: "prepare",
|
||||
OutputKind: "previous_session_artifact",
|
||||
OutputKind: "previous_session_cache",
|
||||
Provenance: ArtifactProvenancePreviousCacheManifestInput,
|
||||
}, nil
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func ResolvePreviousSessionArtifactWithCatalog(
|
||||
ID: source,
|
||||
Path: fallback,
|
||||
ProducerStage: "prepare",
|
||||
OutputKind: "previous_session_artifact",
|
||||
OutputKind: "previous_session_cache",
|
||||
Provenance: ArtifactProvenancePreviousCacheFilesystem,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ func TestCacheDefaults(t *testing.T) {
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
transcribe_url: https://example.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`, `session_id: 2026-05-03
|
||||
|
||||
@@ -130,7 +130,7 @@ func writeCampaignConfigTestFiles(t *testing.T, campaignYAML, sessionYAML string
|
||||
campaignPath := filepath.Join(dir, "campaign.yml")
|
||||
sessionPath := filepath.Join(dir, "session.yml")
|
||||
|
||||
pipelineYAML := "workspace:\n root: " + filepath.ToSlash(filepath.Join(dir, "work")) + "\nwhisperx:\n transcribe_url: https://example.com/transcribe\nanalyzer:\n timeout: 20m\nnotification:\n timeout: 10s\n"
|
||||
pipelineYAML := "workspace:\n root: " + filepath.ToSlash(filepath.Join(dir, "work")) + "\nwhisperx:\n transcribe_url: https://example.com/transcribe\nnotification:\n timeout: 10s\n"
|
||||
if err := os.WriteFile(pipelinePath, []byte(pipelineYAML), 0o644); err != nil {
|
||||
t.Fatalf("write pipeline.yml: %v", err)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ type PipelineConfig struct {
|
||||
Normalize *NormalizeConfig `yaml:"normalize"`
|
||||
Trim *TrimConfig `yaml:"trim"`
|
||||
Scriptorium *ScriptoriumConfig `yaml:"scriptorium"`
|
||||
Analyzer AnalyzerConfig `yaml:"analyzer"`
|
||||
Notification NotificationConfig `yaml:"notification"`
|
||||
}
|
||||
|
||||
@@ -70,8 +69,6 @@ type SecretsConfig struct {
|
||||
// StorageConfig configures storage backends and related parameters.
|
||||
type StorageConfig struct {
|
||||
Backend string `yaml:"backend"`
|
||||
Bucket string `yaml:"bucket"`
|
||||
Prefix string `yaml:"prefix"`
|
||||
S3 *StorageS3Config `yaml:"s3"`
|
||||
}
|
||||
|
||||
@@ -235,13 +232,6 @@ type ScriptoriumInputConfig struct {
|
||||
Required bool `yaml:"required"`
|
||||
}
|
||||
|
||||
// AnalyzerConfig configures analyzer adapter settings.
|
||||
type AnalyzerConfig struct {
|
||||
BinaryPath string `yaml:"binary_path"`
|
||||
Timeout string `yaml:"timeout"`
|
||||
Artifacts ArtifactSettings `yaml:"artifacts"`
|
||||
}
|
||||
|
||||
// NotificationConfig configures notification backend settings.
|
||||
type NotificationConfig struct {
|
||||
Backend string `yaml:"backend"`
|
||||
@@ -249,12 +239,6 @@ type NotificationConfig struct {
|
||||
Timeout string `yaml:"timeout"`
|
||||
}
|
||||
|
||||
// ArtifactSettings configures generated artifact selection and paths.
|
||||
type ArtifactSettings struct {
|
||||
OutputDir string `yaml:"output_dir"`
|
||||
Types []string `yaml:"types"`
|
||||
}
|
||||
|
||||
// SessionInputsConfig contains per-session input references.
|
||||
type SessionInputsConfig struct {
|
||||
AudioDir string `yaml:"audio_dir"`
|
||||
|
||||
@@ -27,8 +27,6 @@ seriatim:
|
||||
binary: seriatim
|
||||
audita:
|
||||
binary: audita
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 15s
|
||||
`,
|
||||
@@ -48,8 +46,6 @@ inputs:
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
transcribe_url: https://transcription.ai.rakestrawhome.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 15s
|
||||
`,
|
||||
@@ -67,8 +63,6 @@ inputs:
|
||||
name: "workspace root defaults when omitted",
|
||||
pipelineYAML: `whisperx:
|
||||
transcribe_url: https://transcription.ai.rakestrawhome.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 15s
|
||||
`,
|
||||
@@ -97,6 +91,24 @@ inputs:
|
||||
`,
|
||||
wantLoadErr: "pipeline file",
|
||||
},
|
||||
{
|
||||
name: "legacy analyzer section fails strict decode",
|
||||
pipelineYAML: `workspace:
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
transcribe_url: https://transcription.ai.rakestrawhome.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
`,
|
||||
sessionYAML: `session_id: 2026-05-03
|
||||
inputs:
|
||||
audio_dir: ./audio
|
||||
speakers_file: ./speakers.yml
|
||||
autocorrect_file: ./autocorrect.yml
|
||||
glossary_file: ./glossary.yml
|
||||
`,
|
||||
wantLoadErr: "strict decode failed",
|
||||
},
|
||||
{
|
||||
name: "unknown whisperx field fails",
|
||||
pipelineYAML: `workspace:
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
legacyPreviousSource := "previous_session_" + "artifact"
|
||||
tests := []struct {
|
||||
name string
|
||||
scriptoriumYAML string
|
||||
@@ -94,7 +95,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
wantValidateErr: "pipeline.scriptorium.timeout must be a valid duration",
|
||||
},
|
||||
{
|
||||
name: "optional previous recap input is accepted",
|
||||
name: "legacy previous session artifact source fails validation",
|
||||
scriptoriumYAML: `scriptorium:
|
||||
binary: scriptorium
|
||||
artifacts:
|
||||
@@ -107,7 +108,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.transcript.polished
|
||||
required: true
|
||||
previous_recap:
|
||||
source: previous_session_artifact
|
||||
source: ` + legacyPreviousSource + `
|
||||
artifact: session_recap
|
||||
path: ""
|
||||
required: false
|
||||
@@ -115,6 +116,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
session_id: true
|
||||
output_kind: session_recap
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source "` + legacyPreviousSource + `" is unsupported`,
|
||||
},
|
||||
{
|
||||
name: "canonical previous-session source is accepted",
|
||||
|
||||
@@ -39,6 +39,49 @@ storage:
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageLegacyTopLevelFieldsFailStrictDecode(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
storageYAML string
|
||||
wantField string
|
||||
}{
|
||||
{
|
||||
name: "bucket",
|
||||
storageYAML: `
|
||||
storage:
|
||||
bucket: my-dnd-archive
|
||||
`,
|
||||
wantField: "bucket",
|
||||
},
|
||||
{
|
||||
name: "prefix",
|
||||
storageYAML: `
|
||||
storage:
|
||||
prefix: dnd
|
||||
`,
|
||||
wantField: "prefix",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pipelineYAML := testPipelineBaseYAML + tt.storageYAML
|
||||
pipelinePath, _ := writeConfigFiles(t, pipelineYAML, testSessionBaseYAML)
|
||||
|
||||
_, err := LoadPipeline(pipelinePath)
|
||||
if err == nil {
|
||||
t.Fatal("LoadPipeline() error = nil, want strict decode error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "strict decode failed") {
|
||||
t.Fatalf("LoadPipeline() error = %v, want strict decode failed", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantField) {
|
||||
t.Fatalf("LoadPipeline() error = %v, want field %q", err, tt.wantField)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorageS3CredentialEnvNamesLoadAndValidate(t *testing.T) {
|
||||
pipelineYAML := testPipelineBaseYAML + `
|
||||
storage:
|
||||
|
||||
@@ -87,9 +87,6 @@ func validatePipeline(cfg *PipelineConfig) error {
|
||||
if err := validateScriptorium(cfg.Scriptorium); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateDuration("pipeline.analyzer.timeout", cfg.Analyzer.Timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateDuration("pipeline.notification.timeout", cfg.Notification.Timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -720,8 +717,6 @@ func validateScriptoriumInputSource(artifactName, inputName, source string, conf
|
||||
|
||||
func isStaticSupportedScriptoriumInputSource(source string) bool {
|
||||
switch source {
|
||||
case "previous_session_artifact":
|
||||
return true
|
||||
case "narratio.transcript.merged":
|
||||
return true
|
||||
case "narratio.transcript.polished":
|
||||
|
||||
@@ -649,15 +649,6 @@ func resolveScriptoriumInput(
|
||||
return "", false, nil, err
|
||||
}
|
||||
switch source {
|
||||
case "previous_session_artifact":
|
||||
if strings.TrimSpace(inputCfg.Path) == "" {
|
||||
return "", false, nil, nil
|
||||
}
|
||||
resolved := resolveInputPathForRead(paths, sessionDir, inputCfg.Path)
|
||||
if err := requireFile(resolved, "scriptorium input "+inputName); err != nil {
|
||||
return "", false, nil, nil
|
||||
}
|
||||
return resolved, true, nil, nil
|
||||
default:
|
||||
resolved, err := artifacts.ResolveSessionArtifactWithCatalog(paths, m, source, runtimeCatalog)
|
||||
if err == nil {
|
||||
|
||||
@@ -219,7 +219,7 @@ func TestAnalyzeRunStillSucceedsWhenRenderSucceeds(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeOmitsOptionalPreviousRecapWhenUnavailable(t *testing.T) {
|
||||
func TestAnalyzeOmitsOptionalCanonicalPreviousRecapWhenUnavailable(t *testing.T) {
|
||||
env, m, fake := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "processed.json"), `{"segments":[]}`)
|
||||
@@ -236,9 +236,7 @@ func TestAnalyzeOmitsOptionalPreviousRecapWhenUnavailable(t *testing.T) {
|
||||
Required: true,
|
||||
},
|
||||
"previous_recap": {
|
||||
Source: "previous_session_artifact",
|
||||
Artifact: "session_recap",
|
||||
Path: "",
|
||||
Source: "narratio.previous_session.artifact.session_recap",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
@@ -343,12 +341,12 @@ func (r *orderedScriptoriumRunner) RunArtifact(_ context.Context, req scriptoriu
|
||||
}, nil
|
||||
}
|
||||
|
||||
func TestAnalyzeIncludesPreviousRecapWhenConfiguredAndAvailable(t *testing.T) {
|
||||
func TestAnalyzeIncludesCanonicalPreviousRecapWhenPreparedCacheExists(t *testing.T) {
|
||||
env, m, fake := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "processed.json"), `{"segments":[]}`)
|
||||
|
||||
previousRecapPath := filepath.Join(filepath.Dir(env.Config.SessionPath), "previous", "session_recap.md")
|
||||
previousRecapPath := artifacts.SessionPreviousArtifactPath(paths, "artifacts/session_recap.md")
|
||||
writeAnalyzeFile(t, previousRecapPath, "previous recap\n")
|
||||
|
||||
env.Config.Pipeline.Scriptorium.Artifacts["session_recap"] = config.ScriptoriumArtifactConfig{
|
||||
@@ -363,9 +361,7 @@ func TestAnalyzeIncludesPreviousRecapWhenConfiguredAndAvailable(t *testing.T) {
|
||||
Required: true,
|
||||
},
|
||||
"previous_recap": {
|
||||
Source: "previous_session_artifact",
|
||||
Artifact: "session_recap",
|
||||
Path: "./previous/session_recap.md",
|
||||
Source: "narratio.previous_session.artifact.session_recap",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
@@ -384,7 +380,7 @@ func TestAnalyzeIncludesPreviousRecapWhenConfiguredAndAvailable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnalyzeFailsWhenRequiredPreviousRecapMissing(t *testing.T) {
|
||||
func TestAnalyzeFailsWhenRequiredCanonicalPreviousRecapMissing(t *testing.T) {
|
||||
env, m, _ := setupAnalyzeEnv(t)
|
||||
paths := sessionPathsForEnv(env, m.SessionID)
|
||||
writeAnalyzeFile(t, filepath.Join(paths.TranscriptsDir, "processed.json"), `{"segments":[]}`)
|
||||
@@ -399,8 +395,7 @@ func TestAnalyzeFailsWhenRequiredPreviousRecapMissing(t *testing.T) {
|
||||
Required: true,
|
||||
},
|
||||
"previous_recap": {
|
||||
Source: "previous_session_artifact",
|
||||
Path: "./missing/previous_recap.md",
|
||||
Source: "narratio.previous_session.artifact.session_recap",
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
@@ -410,8 +405,8 @@ func TestAnalyzeFailsWhenRequiredPreviousRecapMissing(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), `required input "previous_recap"`) {
|
||||
t.Fatalf("error = %q, want required input context", err.Error())
|
||||
if !strings.Contains(err.Error(), "run narratio run-stage --force prepare") {
|
||||
t.Fatalf("error = %q, want guidance to run force prepare", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1208,9 +1203,7 @@ func setupAnalyzeEnv(t *testing.T) (*Env, *manifest.Manifest, *scriptorium.FakeR
|
||||
Required: true,
|
||||
},
|
||||
"previous_recap": {
|
||||
Source: "previous_session_artifact",
|
||||
Artifact: "session_recap",
|
||||
Path: "",
|
||||
Source: "narratio.previous_session.artifact.session_recap",
|
||||
Required: false,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"log/slog"
|
||||
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/analyzer"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/audita"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/notify"
|
||||
"gitea.maximumdirect.net/eric/narratio/internal/adapters/scriptorium"
|
||||
@@ -28,7 +27,6 @@ type Env struct {
|
||||
Seriatim seriatim.Runner
|
||||
Audita audita.Runner
|
||||
Scriptorium scriptorium.Runner
|
||||
Analyzer analyzer.Runner
|
||||
Storage storage.Backend
|
||||
ObjectStore storage.ObjectStore
|
||||
Notifier notify.Sender
|
||||
|
||||
Reference in New Issue
Block a user