Add Seriatim normalize adapter support

This commit is contained in:
2026-05-10 21:35:11 +00:00
parent d1eb6d77ea
commit 5e2234e418
5 changed files with 581 additions and 12 deletions

View File

@@ -77,10 +77,8 @@ func NewSubprocessRunner(cfg SubprocessRunnerConfig) (*SubprocessRunner, error)
if strings.TrimSpace(cfg.OutputSchema) == "" {
return nil, fmt.Errorf("seriatim output schema is required")
}
switch cfg.OutputSchema {
case "seriatim-minimal", "seriatim-intermediate", "seriatim-full":
default:
return nil, fmt.Errorf("seriatim output schema %q is unsupported", cfg.OutputSchema)
if err := validateOutputSchema(cfg.OutputSchema); err != nil {
return nil, err
}
if cfg.CoalesceGap != nil && *cfg.CoalesceGap < 0 {
return nil, fmt.Errorf("seriatim coalesce gap must be >= 0")
@@ -96,6 +94,15 @@ func NewSubprocessRunner(cfg SubprocessRunnerConfig) (*SubprocessRunner, error)
}, nil
}
func validateOutputSchema(schema string) error {
switch strings.TrimSpace(schema) {
case "seriatim-minimal", "seriatim-intermediate", "seriatim-full":
return nil
default:
return fmt.Errorf("seriatim output schema %q is unsupported", schema)
}
}
// Run executes Seriatim merge with deterministic flags and validates output artifacts.
func (r *SubprocessRunner) Run(ctx context.Context, req MergeRequest) (MergeResult, error) {
if r == nil {
@@ -271,6 +278,112 @@ func (r *SubprocessRunner) Trim(ctx context.Context, req TrimRequest) (TrimResul
}, nil
}
// Normalize executes Seriatim normalize with deterministic flags and validates output artifacts.
func (r *SubprocessRunner) Normalize(ctx context.Context, req NormalizeRequest) (NormalizeResult, error) {
if r == nil {
return NormalizeResult{}, fmt.Errorf("seriatim subprocess runner is nil")
}
if strings.TrimSpace(req.InputTranscriptPath) == "" {
return NormalizeResult{}, fmt.Errorf("seriatim normalize input path is required")
}
if strings.TrimSpace(req.OutputNormalizedPath) == "" {
return NormalizeResult{}, fmt.Errorf("seriatim normalize output path is required")
}
binary := r.binary
if strings.TrimSpace(req.Binary) != "" {
binary = strings.TrimSpace(req.Binary)
}
timeout := r.timeout
if req.Timeout < 0 {
return NormalizeResult{}, fmt.Errorf("seriatim normalize timeout must be >= 0")
}
if req.Timeout > 0 {
timeout = req.Timeout
}
outputSchema := strings.TrimSpace(req.OutputSchema)
if outputSchema == "" {
outputSchema = r.outputSchema
}
if err := validateOutputSchema(outputSchema); err != nil {
return NormalizeResult{}, err
}
args := buildNormalizeArgs(req, outputSchema)
if req.GeneratedConfigPath != "" {
if err := writeNormalizeInvocationConfig(req, args, binary, timeout, outputSchema); err != nil {
return NormalizeResult{}, fmt.Errorf("write seriatim normalize invocation config %q: %w", req.GeneratedConfigPath, err)
}
}
runRes, err := subprocess.Run(ctx, subprocess.RunRequest{
Executable: binary,
Args: args,
Timeout: timeout,
StdoutLogPath: req.StdoutLogPath,
StderrLogPath: req.StderrLogPath,
})
if err != nil {
return NormalizeResult{
OutputNormalizedPath: req.OutputNormalizedPath,
ReportPath: req.ReportPath,
StdoutLogPath: req.StdoutLogPath,
StderrLogPath: req.StderrLogPath,
GeneratedConfigPath: req.GeneratedConfigPath,
ExitCode: runRes.ExitCode,
Duration: runRes.Duration,
InvokedBinary: binary,
OutputSchema: outputSchema,
}, fmt.Errorf("run seriatim normalize (binary=%q): %w", binary, err)
}
if err := validateJSONFileWithSegments(req.OutputNormalizedPath); err != nil {
return NormalizeResult{
OutputNormalizedPath: req.OutputNormalizedPath,
ReportPath: req.ReportPath,
StdoutLogPath: req.StdoutLogPath,
StderrLogPath: req.StderrLogPath,
GeneratedConfigPath: req.GeneratedConfigPath,
ExitCode: runRes.ExitCode,
Duration: runRes.Duration,
InvokedBinary: binary,
OutputSchema: outputSchema,
}, fmt.Errorf("validate seriatim normalized output %q: %w", req.OutputNormalizedPath, err)
}
if strings.TrimSpace(req.ReportPath) != "" {
if err := validateJSONFile(req.ReportPath); err != nil {
return NormalizeResult{
OutputNormalizedPath: req.OutputNormalizedPath,
ReportPath: req.ReportPath,
StdoutLogPath: req.StdoutLogPath,
StderrLogPath: req.StderrLogPath,
GeneratedConfigPath: req.GeneratedConfigPath,
ExitCode: runRes.ExitCode,
Duration: runRes.Duration,
InvokedBinary: binary,
OutputSchema: outputSchema,
}, fmt.Errorf("validate seriatim normalize report output %q: %w", req.ReportPath, err)
}
}
return NormalizeResult{
OutputNormalizedPath: req.OutputNormalizedPath,
ReportPath: req.ReportPath,
StdoutLogPath: req.StdoutLogPath,
StderrLogPath: req.StderrLogPath,
GeneratedConfigPath: req.GeneratedConfigPath,
ExitCode: runRes.ExitCode,
Duration: runRes.Duration,
InvokedBinary: binary,
OutputSchema: outputSchema,
Metadata: map[string]any{
"adapter": "seriatim_subprocess",
},
}, nil
}
func (r *SubprocessRunner) buildMergeArgs(req MergeRequest) []string {
args := []string{"merge"}
@@ -354,6 +467,19 @@ func buildTrimArgs(req TrimRequest) []string {
}
}
func buildNormalizeArgs(req NormalizeRequest, outputSchema string) []string {
args := []string{
"normalize",
"--input-file", req.InputTranscriptPath,
"--output-file", req.OutputNormalizedPath,
"--output-schema", outputSchema,
}
if strings.TrimSpace(req.ReportPath) != "" {
args = append(args, "--report-file", req.ReportPath)
}
return args
}
func writeTrimInvocationConfig(req TrimRequest, args []string, binary string, timeout time.Duration) error {
payload := map[string]any{
"schema": "seriatim.generated.v1",
@@ -368,6 +494,21 @@ func writeTrimInvocationConfig(req TrimRequest, args []string, binary string, ti
return subprocess.WriteYAMLAtomic(req.GeneratedConfigPath, payload, 0o644)
}
func writeNormalizeInvocationConfig(req NormalizeRequest, args []string, binary string, timeout time.Duration, outputSchema string) error {
payload := map[string]any{
"schema": "seriatim.generated.v1",
"command": "normalize",
"binary": binary,
"args": args,
"timeout": timeout.String(),
"input_path": req.InputTranscriptPath,
"output_path": req.OutputNormalizedPath,
"output_schema": outputSchema,
"report_path": req.ReportPath,
}
return subprocess.WriteYAMLAtomic(req.GeneratedConfigPath, payload, 0o644)
}
func validateJSONFile(path string) error {
data, err := os.ReadFile(path)
if err != nil {