Record chunking metadata in process runs

This commit is contained in:
2026-05-11 12:47:31 +00:00
parent 12fd541669
commit b997e7c97c
6 changed files with 598 additions and 35 deletions

View File

@@ -413,6 +413,274 @@ func TestRunProcessFailedRunRetainedWithNeverRetention(t *testing.T) {
}
}
func TestRunProcessReportJSONIncludesChunkingSummary(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
transcriptPath := writeFile(t, "transcript.json", `[
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello world this is a test"},
{"id":2,"speaker":"Bob","start":2.0,"end":3.0,"text":"Another segment here with more text"},
{"id":3,"speaker":"Alice","start":4.0,"end":5.0,"text":"Final segment text"}
]`)
reportPath := filepath.Join(t.TempDir(), "report.json")
outputPath := filepath.Join(t.TempDir(), "normalized.json")
exitCode := Run([]string{
"process",
transcriptPath,
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--output",
outputPath,
"--report-json",
reportPath,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
if stdout.Len() != 0 {
t.Fatalf("expected empty stdout with --output, got %q", stdout.String())
}
report := readProcessReport(t, reportPath)
if report.Chunking == nil {
t.Fatalf("expected chunking summary in report")
}
if report.Chunking.ChunkCount == 0 {
t.Errorf("expected non-zero chunk count")
}
if report.Chunking.MaxSectionTokens == 0 {
t.Errorf("expected max_section_tokens in report")
}
if report.Phase != "phase3-chunking" {
t.Errorf("expected phase 'phase3-chunking', got %q", report.Phase)
}
}
func TestRunProcessChunkingSummaryArtifactWritten(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
workDir := t.TempDir()
transcriptPath := writeFile(t, "transcript.json", `[
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello"},
{"id":2,"speaker":"Bob","start":2.0,"end":3.0,"text":"World"}
]`)
exitCode := Run([]string{
"process",
transcriptPath,
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--work-dir",
workDir,
"--work-dir-retention",
"always",
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
runPath := onlyRunDir(t, workDir)
// Verify chunking-summary.json exists
chunkingSummaryPath := filepath.Join(runPath, "chunking-summary.json")
if _, err := os.Stat(chunkingSummaryPath); err != nil {
t.Fatalf("expected chunking-summary.json artifact: %v", err)
}
// Verify it contains valid JSON with expected fields
content := readFile(t, chunkingSummaryPath)
var summary struct {
ChunkCount int `json:"chunk_count"`
MinEstimatedTokens int `json:"min_estimated_chunk_tokens"`
MaxEstimatedTokens int `json:"max_estimated_chunk_tokens"`
TotalEstimatedTokens int `json:"total_estimated_transcript_tokens"`
Chunks []struct {
Index int `json:"index"`
StartSegmentID int `json:"start_segment_id"`
EndSegmentID int `json:"end_segment_id"`
EstimatedTokens int `json:"estimated_tokens"`
SegmentCount int `json:"segment_count"`
} `json:"chunks"`
}
if err := json.Unmarshal(content, &summary); err != nil {
t.Fatalf("failed to parse chunking summary: %v", err)
}
if summary.ChunkCount == 0 {
t.Errorf("expected non-zero chunk count in summary")
}
if len(summary.Chunks) != summary.ChunkCount {
t.Errorf("expected %d chunks, got %d", summary.ChunkCount, len(summary.Chunks))
}
}
func TestRunProcessTargetSectionsThroughCLI(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
transcriptPath := writeFile(t, "transcript.json", `[
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello world"},
{"id":2,"speaker":"Bob","start":2.0,"end":3.0,"text":"Another test"},
{"id":3,"speaker":"Charlie","start":4.0,"end":5.0,"text":"Third segment"},
{"id":4,"speaker":"Alice","start":6.0,"end":7.0,"text":"Final segment"}
]`)
reportPath := filepath.Join(t.TempDir(), "report.json")
exitCode := Run([]string{
"process",
transcriptPath,
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--target-sections",
"2",
"--report-json",
reportPath,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
report := readProcessReport(t, reportPath)
if report.Chunking == nil {
t.Fatalf("expected chunking summary in report")
}
if report.Chunking.ChunkCount != 2 {
t.Errorf("expected 2 chunks with --target-sections 2, got %d", report.Chunking.ChunkCount)
}
if report.Chunking.TargetSections == nil || *report.Chunking.TargetSections != 2 {
t.Errorf("expected target_sections=2 in report")
}
}
func TestRunProcessImpossibleTargetSectionsFailsCleanly(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
// Create transcript with 2 segments
transcriptPath := writeFile(t, "transcript.json", `[
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello"},
{"id":2,"speaker":"Bob","start":2.0,"end":3.0,"text":"World"}
]`)
reportPath := filepath.Join(t.TempDir(), "report.json")
workDir := t.TempDir()
exitCode := Run([]string{
"process",
transcriptPath,
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--target-sections",
"5",
"--report-json",
reportPath,
"--work-dir",
workDir,
"--work-dir-retention",
"always",
}, &stdout, &stderr)
if exitCode == 0 {
t.Fatalf("expected nonzero exit code for impossible target-sections")
}
if stdout.Len() != 0 {
t.Fatalf("expected empty stdout on failure, got %q", stdout.String())
}
if !strings.Contains(stderr.String(), "target_sections") {
t.Errorf("expected target_sections error in stderr, got %q", stderr.String())
}
// Verify report was written with failure info
report := readProcessReport(t, reportPath)
if report.Status != "failed" {
t.Errorf("expected failed status, got %q", report.Status)
}
if report.ErrorPhase != "chunking" {
t.Errorf("expected error phase 'chunking', got %q", report.ErrorPhase)
}
// Verify run dir was retained with error.log
runPath := onlyRunDir(t, workDir)
if _, err := os.Stat(filepath.Join(runPath, "error.log")); err != nil {
t.Errorf("expected error.log in retained failed run: %v", err)
}
}
func TestRunProcessOutputTranscriptStillNormalizedOnly(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
transcriptPath := writeFile(t, "transcript.json", `[
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello"},
{"id":2,"speaker":"Alice","start":1.5,"end":2.5,"text":"world"}
]`)
outputPath := filepath.Join(t.TempDir(), "normalized.json")
exitCode := Run([]string{
"process",
transcriptPath,
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--output",
outputPath,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
if stdout.Len() != 0 {
t.Fatalf("expected empty stdout with --output, got %q", stdout.String())
}
// Verify output is the normalized transcript (same as before)
output := readFile(t, outputPath)
transcript, err := schema.ParseTranscriptJSON(output)
if err != nil {
t.Fatalf("expected valid transcript JSON: %v", err)
}
if len(transcript.Segments) != 1 {
t.Errorf("expected 1 merged segment, got %d", len(transcript.Segments))
}
// Text should be normalized (merged with space)
if transcript.Segments[0].Text != "Hello world" {
t.Errorf("expected merged text 'Hello world', got %q", transcript.Segments[0].Text)
}
}
func TestRunProcessReportJSONNotPrintedToStdout(t *testing.T) {
var stdout bytes.Buffer
var stderr bytes.Buffer
transcriptPath := writeFile(t, "transcript.json", `[
{"id":1,"speaker":"Alice","start":0.0,"end":1.0,"text":"Hello"}
]`)
reportPath := filepath.Join(t.TempDir(), "report.json")
exitCode := Run([]string{
"process",
transcriptPath,
"--glossary",
fixturePath("tiny_glossary.yaml"),
"--report-json",
reportPath,
}, &stdout, &stderr)
if exitCode != 0 {
t.Fatalf("expected exit code 0, got %d with stderr %q", exitCode, stderr.String())
}
// Verify stdout contains only transcript JSON, not report JSON
stdoutStr := stdout.String()
if strings.Contains(stdoutStr, "chunk_count") {
t.Errorf("stdout should not contain report JSON fields, got: %s", stdoutStr)
}
if strings.Contains(stdoutStr, "normalization_merges") {
t.Errorf("stdout should not contain report JSON fields, got: %s", stdoutStr)
}
// Verify transcript is valid JSON
if _, err := schema.ParseTranscriptJSON(stdout.Bytes()); err != nil {
t.Errorf("stdout should contain valid transcript JSON: %v", err)
}
}
func fixturePath(name string) string {
return filepath.Join("testdata", name)
}