diff --git a/docs/roadmap/remediation.md b/docs/roadmap/remediation.md
index 869bc72..0825b90 100644
--- a/docs/roadmap/remediation.md
+++ b/docs/roadmap/remediation.md
@@ -247,6 +247,8 @@ cell. Existing unnamed and repeated stage labels remain distinguishable.
## R7 — P3: Retire Disconnected Cutover Code and Test the Live Paths
+**Status:** Completed. Removed retired report and debug builders, utilization and timing bookkeeping, and obsolete invocation fields. Report destination tests now drive the processing command. Internal and offline subprocess suites passed.
+
**Category**
Dead production code and misleading test coverage in Stages 7,
@@ -254,11 +256,11 @@ Dead production code and misleading test coverage in Stages 7,
**Evidence**
-[The old report-directory helper](../../internal/cli/config_input.go)
+The old report-directory helper in `internal/cli/config_input.go`
has only a test caller. [Processing](../../internal/cli/process_inputs.go#L72)
implements directory selection separately. The former
-[configuration explanation](../../internal/framework/llm/safe_view.go) and
-[utilization view](../../internal/framework/processreport/llm.go#L24) also have
+configuration explanation in `internal/framework/llm/safe_view.go` and
+utilization view in `internal/framework/processreport/llm.go` also have
only test callers. The runner still collects validator timing for the latter.
The single `GlossaryPath` survives through report data and prints an empty
`Glossary input:` line after the standalone glossary loader was removed.
diff --git a/internal/cli/config_input.go b/internal/cli/config_input.go
deleted file mode 100644
index e9a5f4e..0000000
--- a/internal/cli/config_input.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package cli
-
-import (
- "fmt"
- "gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
- ck "go.maximumdirect.net/configkit"
- "path/filepath"
- "strings"
-)
-
-type selectedConfig struct {
- Path string
- Source string
- Result *ck.Result[config.Config]
-}
-
-func selectedReportDir(cfg config.Config, outputPath string, reportDirSet bool) (string, error) {
- if !cfg.Reporting.Enabled {
- return "", nil
- }
- var reportDir string
- if outputPath != "" {
- if reportDirSet {
- reportDir = cfg.Reporting.StdoutDir
- } else {
- reportDir = filepath.Dir(outputPath)
- }
- } else {
- reportDir = cfg.Reporting.StdoutDir
- }
- if strings.TrimSpace(reportDir) == "" {
- return "", fmt.Errorf("report directory is required when reporting is enabled for the selected destination")
- }
- return reportDir, nil
-}
diff --git a/internal/cli/config_input_test.go b/internal/cli/config_input_test.go
deleted file mode 100644
index 4ba5aa7..0000000
--- a/internal/cli/config_input_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package cli
-
-import (
- "gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
- "path/filepath"
- "testing"
-)
-
-func TestReportDirectorySelectionOnlyUsesActiveDestination(t *testing.T) {
- cfg := config.Config{Reporting: config.Reporting{Enabled: true, StdoutDir: "reports"}}
- if got, err := selectedReportDir(cfg, "", false); err != nil || got != "reports" {
- t.Fatalf("stdout destination: %q %v", got, err)
- }
- if got, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json"), false); err != nil || got != "out" {
- t.Fatalf("file destination ignored stdout setting: %q %v", got, err)
- }
- if got, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json"), true); err != nil || got != "reports" {
- t.Fatalf("explicit file report directory: %q %v", got, err)
- }
- cfg.Reporting.StdoutDir = ""
- if got, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json"), false); err != nil || got != "out" {
- t.Fatalf("inactive blank configured report directory rejected for file output: %q %v", got, err)
- }
- if _, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json"), true); err == nil {
- t.Fatal("active explicit blank report directory accepted for file output")
- }
- if _, err := selectedReportDir(cfg, "", false); err == nil {
- t.Fatal("enabled stdout reporting accepted blank destination")
- }
- cfg.Reporting.Enabled = false
- if got, err := selectedReportDir(cfg, filepath.Join("out", "corrected.json"), true); err != nil || got != "" {
- t.Fatalf("disabled reporting probed destination: %q %v", got, err)
- }
-}
diff --git a/internal/cli/process_command_integration_test.go b/internal/cli/process_command_integration_test.go
index 4610f1d..4ab3d5e 100644
--- a/internal/cli/process_command_integration_test.go
+++ b/internal/cli/process_command_integration_test.go
@@ -93,6 +93,42 @@ func TestProcessUsesResolvedReportingMode(t *testing.T) {
}
}
+func TestProcessChecksOnlyTheActiveReportDirectory(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ flags []string
+ wantReport bool
+ wantError bool
+ }{
+ {"file output ignores blank configured directory", nil, true, false},
+ {"explicit blank directory", []string{"--report-dir="}, false, true},
+ {"disabled reporting ignores explicit blank directory", []string{"--reporting=false", "--report-dir="}, false, false},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ dir := t.TempDir()
+ configPath := filepath.Join(dir, "config.yml")
+ if err := os.WriteFile(configPath, []byte("version: 3\npipeline: {stages: []}\nreporting: {stdout_dir: ''}\n"), 0600); err != nil {
+ t.Fatal(err)
+ }
+ transcript := filepath.Join(dir, "transcript.json")
+ if err := os.WriteFile(transcript, []byte(`[{"speaker":"Alice","start":0,"end":1,"text":"hello"}]`), 0600); err != nil {
+ t.Fatal(err)
+ }
+ output := filepath.Join(dir, "output.json")
+ args := append([]string{"process", "--config", configPath, "--output", output}, tc.flags...)
+ var stdout, stderr bytes.Buffer
+ status := Run(context.Background(), append(args, transcript), &stdout, &stderr)
+ if (status != 0) != tc.wantError || (tc.wantError && !strings.Contains(stderr.String(), "report directory is required")) {
+ t.Fatalf("status=%d stdout=%q stderr=%q", status, stdout.String(), stderr.String())
+ }
+ _, err := os.Stat(filepath.Join(dir, "errors.yml"))
+ if (tc.wantReport && err != nil) || (!tc.wantReport && !os.IsNotExist(err)) {
+ t.Fatalf("report file: %v, want present=%t", err, tc.wantReport)
+ }
+ })
+ }
+}
+
func TestProcessCollectionFlags(t *testing.T) {
dir := t.TempDir()
for name, contents := range map[string]string{
diff --git a/internal/cli/process_pipeline.go b/internal/cli/process_pipeline.go
index f1b0f2a..266ce54 100644
--- a/internal/cli/process_pipeline.go
+++ b/internal/cli/process_pipeline.go
@@ -39,11 +39,8 @@ func executePipeline(ctx context.Context, loaded *processInputs, prepared *prepa
ProposalDiagnosticsDir: debugPath, ValidationDiagnosticsDir: debugPath,
CaptureCandidates: debugPath != "", ReportDiagnosticError: warn,
})
- output.Observations = observer.Snapshot()
- output.ObservationsAvailable = true
- output.LLMSummary = llm.Summarize(output.Observations)
if debug := prepared.Prepared.Debug; debug != nil {
- warn(debug.WriteJSONArtifact(diagnostics.ArtifactLLMExecution, processreport.BuildLLMExecution(output.Observations)))
+ warn(debug.WriteJSONArtifact(diagnostics.ArtifactLLMExecution, processreport.BuildLLMExecution(observer.Snapshot())))
warn(debug.WriteJSONArtifact(diagnostics.ArtifactCorrectionLedger, processreport.BuildCorrectionLedger(processreport.CorrectionLedgerInput{RunDirectoryPath: debug.Path(), RunOutput: &output})))
}
var failures []processreport.RunFailure
diff --git a/internal/core/chunking/summary.go b/internal/core/chunking/summary.go
deleted file mode 100644
index f648c03..0000000
--- a/internal/core/chunking/summary.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package chunking
-
-// Summary provides a concise overview of chunking results for reports
-type 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"`
- TargetSections *int `json:"target_sections,omitempty"`
- MaxSectionTokens int `json:"max_section_tokens"`
- MinSectionTokens int `json:"min_section_tokens"`
-}
-
-// ChunkSummary represents a single chunk's metadata for diagnostics
-type ChunkSummary 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"`
-}
-
-// DetailedSummary provides per-chunk details for diagnostics
-type DetailedSummary struct {
- Summary `json:",inline"`
- Chunks []ChunkSummary `json:"chunks"`
-}
-
-// ComputeSummary creates a Summary from sections and config
-func ComputeSummary(sections []Section, config ChunkingConfig) Summary {
- if len(sections) == 0 {
- return Summary{
- ChunkCount: 0,
- MaxSectionTokens: config.MaxSectionTokens,
- MinSectionTokens: config.MinSectionTokens,
- TargetSections: config.TargetSections,
- }
- }
-
- minTokens := sections[0].EstimatedTokens
- maxTokens := sections[0].EstimatedTokens
- totalTokens := 0
-
- for _, sec := range sections {
- if sec.EstimatedTokens < minTokens {
- minTokens = sec.EstimatedTokens
- }
- if sec.EstimatedTokens > maxTokens {
- maxTokens = sec.EstimatedTokens
- }
- totalTokens += sec.EstimatedTokens
- }
-
- return Summary{
- ChunkCount: len(sections),
- MinEstimatedTokens: minTokens,
- MaxEstimatedTokens: maxTokens,
- TotalEstimatedTokens: totalTokens,
- TargetSections: config.TargetSections,
- MaxSectionTokens: config.MaxSectionTokens,
- MinSectionTokens: config.MinSectionTokens,
- }
-}
-
-// ComputeDetailedSummary creates a DetailedSummary from sections and config
-func ComputeDetailedSummary(sections []Section, config ChunkingConfig) DetailedSummary {
- summary := ComputeSummary(sections, config)
-
- chunks := make([]ChunkSummary, len(sections))
- for i, sec := range sections {
- chunks[i] = ChunkSummary{
- Index: sec.Index,
- StartSegmentID: sec.StartSegmentID,
- EndSegmentID: sec.EndSegmentID,
- EstimatedTokens: sec.EstimatedTokens,
- SegmentCount: len(sec.Segments),
- }
- }
-
- return DetailedSummary{
- Summary: summary,
- Chunks: chunks,
- }
-}
diff --git a/internal/core/chunking/summary_test.go b/internal/core/chunking/summary_test.go
deleted file mode 100644
index c5b8748..0000000
--- a/internal/core/chunking/summary_test.go
+++ /dev/null
@@ -1,148 +0,0 @@
-package chunking
-
-import (
- "testing"
-
- "gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
-)
-
-func TestComputeSummary(t *testing.T) {
- config := ChunkingConfig{
- MaxSectionTokens: 100,
- MinSectionTokens: 10,
- }
-
- sections := []Section{
- {Index: 0, EstimatedTokens: 30, StartSegmentID: 1, EndSegmentID: 2},
- {Index: 1, EstimatedTokens: 50, StartSegmentID: 3, EndSegmentID: 4},
- {Index: 2, EstimatedTokens: 20, StartSegmentID: 5, EndSegmentID: 5},
- }
-
- summary := ComputeSummary(sections, config)
-
- if summary.ChunkCount != 3 {
- t.Errorf("expected chunk_count=3, got %d", summary.ChunkCount)
- }
- if summary.MinEstimatedTokens != 20 {
- t.Errorf("expected min_estimated_tokens=20, got %d", summary.MinEstimatedTokens)
- }
- if summary.MaxEstimatedTokens != 50 {
- t.Errorf("expected max_estimated_tokens=50, got %d", summary.MaxEstimatedTokens)
- }
- if summary.TotalEstimatedTokens != 100 {
- t.Errorf("expected total_estimated_tokens=100, got %d", summary.TotalEstimatedTokens)
- }
- if summary.MaxSectionTokens != 100 {
- t.Errorf("expected max_section_tokens=100, got %d", summary.MaxSectionTokens)
- }
- if summary.MinSectionTokens != 10 {
- t.Errorf("expected min_section_tokens=10, got %d", summary.MinSectionTokens)
- }
- if summary.TargetSections != nil {
- t.Errorf("expected target_sections=nil, got %v", summary.TargetSections)
- }
-}
-
-func TestComputeSummaryWithTarget(t *testing.T) {
- target := 5
- config := ChunkingConfig{
- MaxSectionTokens: 100,
- MinSectionTokens: 10,
- TargetSections: &target,
- }
-
- sections := []Section{
- {Index: 0, EstimatedTokens: 30, StartSegmentID: 1, EndSegmentID: 2},
- }
-
- summary := ComputeSummary(sections, config)
-
- if summary.TargetSections == nil || *summary.TargetSections != 5 {
- t.Errorf("expected target_sections=5, got %v", summary.TargetSections)
- }
-}
-
-func TestComputeSummaryEmptySections(t *testing.T) {
- config := ChunkingConfig{
- MaxSectionTokens: 100,
- MinSectionTokens: 10,
- }
-
- sections := []Section{}
-
- summary := ComputeSummary(sections, config)
-
- if summary.ChunkCount != 0 {
- t.Errorf("expected chunk_count=0, got %d", summary.ChunkCount)
- }
- if summary.MinEstimatedTokens != 0 {
- t.Errorf("expected min_estimated_tokens=0 for empty, got %d", summary.MinEstimatedTokens)
- }
- if summary.MaxSectionTokens != 100 {
- t.Errorf("expected max_section_tokens preserved, got %d", summary.MaxSectionTokens)
- }
-}
-
-func TestComputeDetailedSummary(t *testing.T) {
- config := ChunkingConfig{
- MaxSectionTokens: 100,
- MinSectionTokens: 10,
- }
-
- sections := []Section{
- {
- Index: 0,
- EstimatedTokens: 30,
- StartSegmentID: 1,
- EndSegmentID: 2,
- Segments: make([]schema.Segment, 2), // 2 segments
- },
- {
- Index: 1,
- EstimatedTokens: 50,
- StartSegmentID: 3,
- EndSegmentID: 5,
- Segments: make([]schema.Segment, 3), // 3 segments
- },
- }
-
- detailed := ComputeDetailedSummary(sections, config)
-
- if detailed.ChunkCount != 2 {
- t.Errorf("expected chunk_count=2, got %d", detailed.ChunkCount)
- }
- if len(detailed.Chunks) != 2 {
- t.Fatalf("expected 2 chunk entries, got %d", len(detailed.Chunks))
- }
-
- // Check first chunk
- if detailed.Chunks[0].Index != 0 {
- t.Errorf("expected chunk[0].index=0, got %d", detailed.Chunks[0].Index)
- }
- if detailed.Chunks[0].StartSegmentID != 1 {
- t.Errorf("expected chunk[0].start_segment_id=1, got %d", detailed.Chunks[0].StartSegmentID)
- }
- if detailed.Chunks[0].EndSegmentID != 2 {
- t.Errorf("expected chunk[0].end_segment_id=2, got %d", detailed.Chunks[0].EndSegmentID)
- }
- if detailed.Chunks[0].EstimatedTokens != 30 {
- t.Errorf("expected chunk[0].estimated_tokens=30, got %d", detailed.Chunks[0].EstimatedTokens)
- }
- if detailed.Chunks[0].SegmentCount != 2 {
- t.Errorf("expected chunk[0].segment_count=2, got %d", detailed.Chunks[0].SegmentCount)
- }
-
- // Check second chunk
- if detailed.Chunks[1].Index != 1 {
- t.Errorf("expected chunk[1].index=1, got %d", detailed.Chunks[1].Index)
- }
- if detailed.Chunks[1].StartSegmentID != 3 {
- t.Errorf("expected chunk[1].start_segment_id=3, got %d", detailed.Chunks[1].StartSegmentID)
- }
- if detailed.Chunks[1].EndSegmentID != 5 {
- t.Errorf("expected chunk[1].end_segment_id=5, got %d", detailed.Chunks[1].EndSegmentID)
- }
- if detailed.Chunks[1].SegmentCount != 3 {
- t.Errorf("expected chunk[1].segment_count=3, got %d", detailed.Chunks[1].SegmentCount)
- }
-}
diff --git a/internal/core/diagnostics/artifacts.go b/internal/core/diagnostics/artifacts.go
index 95b1e9c..763d243 100644
--- a/internal/core/diagnostics/artifacts.go
+++ b/internal/core/diagnostics/artifacts.go
@@ -5,13 +5,8 @@ const (
ArtifactParsedSourceTranscript = "source-transcript-parsed.json"
ArtifactNormalizedTranscript = "normalized-transcript.json"
ArtifactNormalizationSummary = "normalization-summary.json"
- ArtifactChunkingSummary = "chunking-summary.json"
- ArtifactUtilizationSummary = "utilization-diagnostics.json"
ArtifactLLMExecution = "llm-execution.json"
- ArtifactSelectedProfiles = "selected-profiles.yaml"
- ArtifactConfigOrigins = "config-origins.yaml"
ArtifactCorrectionLedger = "correction-ledger.json"
ArtifactInvocationMetadata = "invocation.json"
ArtifactEffectiveConfig = "effective-config.redacted.yaml"
- ArtifactErrorLog = "error.log"
)
diff --git a/internal/core/diagnostics/run_dir.go b/internal/core/diagnostics/run_dir.go
index 383bb83..60f89ed 100644
--- a/internal/core/diagnostics/run_dir.go
+++ b/internal/core/diagnostics/run_dir.go
@@ -8,7 +8,6 @@ import (
"strings"
"time"
- "gitea.maximumdirect.net/eric/audita/v3/internal/core/chunking"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/normalization"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/redaction"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/schema"
@@ -23,18 +22,13 @@ type RunDirectory struct {
// InvocationMetadata captures non-secret invocation details for diagnostics.
type InvocationMetadata struct {
- Operation string `json:"operation"`
- TranscriptPath string `json:"transcript_path"`
- GlossaryPath string `json:"glossary_path"`
- OutputPath string `json:"output_path,omitempty"`
- ConfigPath string `json:"config_path,omitempty"`
- ConfigSource string `json:"config_source,omitempty"`
- Overlay string `json:"overlay,omitempty"`
- ConfigVersion *int `json:"config_version,omitempty"`
- TranscriptDescription string `json:"transcript_description,omitempty"`
- Modules []string `json:"modules"`
- RunID string `json:"run_id"`
- StartedAt time.Time `json:"started_at"`
+ Operation string `json:"operation"`
+ TranscriptPath string `json:"transcript_path"`
+ OutputPath string `json:"output_path,omitempty"`
+ Overlay string `json:"overlay,omitempty"`
+ ConfigVersion *int `json:"config_version,omitempty"`
+ RunID string `json:"run_id"`
+ StartedAt time.Time `json:"started_at"`
}
// NewRunDirectory creates a new run directory under the configured work dir
@@ -158,26 +152,6 @@ func (r *RunDirectory) WriteNormalizationSummary(summary *normalization.Normaliz
return nil
}
-// WriteErrorLog writes an error log on failure
-func (r *RunDirectory) WriteErrorLog(errorMessage string) error {
- errorPath := filepath.Join(r.path, ArtifactErrorLog)
- return os.WriteFile(errorPath, []byte(r.redactor.String(errorMessage)+"\n"), 0o644)
-}
-
-// WriteChunkingSummary writes the chunking summary artifact
-func (r *RunDirectory) WriteChunkingSummary(summary *chunking.DetailedSummary) error {
- summaryPath := filepath.Join(r.path, ArtifactChunkingSummary)
- bytes, err := r.redactor.JSON(summary)
- if err != nil {
- return fmt.Errorf("failed to marshal chunking summary: %w", err)
- }
- bytes = append(bytes, '\n')
- if err := os.WriteFile(summaryPath, bytes, 0o644); err != nil {
- return fmt.Errorf("failed to write chunking summary: %w", err)
- }
- return nil
-}
-
func (r *RunDirectory) WriteJSONArtifact(name string, payload any) error {
artifactPath := filepath.Join(r.path, name)
bytes, err := r.redactor.JSON(payload)
diff --git a/internal/core/reporting/llm.go b/internal/core/reporting/llm.go
index d800bcf..7ad9df8 100644
--- a/internal/core/reporting/llm.go
+++ b/internal/core/reporting/llm.go
@@ -22,37 +22,3 @@ type LLMSummary struct {
StructuralRepairs uint64 `json:"structural_repairs"`
Usage LLMUsage `json:"usage"`
}
-
-type BackendUtilization struct {
- Concurrency *int `json:"concurrency,omitempty"`
- QueueCapacity *int `json:"queue_capacity,omitempty"`
- LLMSummary
- CapacityWaitMS int64 `json:"capacity_wait_ms"`
- GenerationMS int64 `json:"generation_ms"`
- RetryDelayMS int64 `json:"retry_delay_ms"`
- LogicalWallMS int64 `json:"logical_wall_ms"`
-}
-
-type UtilizationDiagnostics struct {
- Backends map[string]BackendUtilization `json:"backends"`
- RunWallTimeMS int64 `json:"run_wall_time_ms"`
- Modules []ModuleUtilization `json:"modules,omitempty"`
- Validators []ValidatorUtilization `json:"validators,omitempty"`
-}
-
-type ModuleUtilization struct {
- ModuleKey string `json:"module_key"`
- ModuleInstance string `json:"module_instance"`
- ProposalLLMCalls uint64 `json:"proposal_llm_calls"`
- ValidationLLMCalls uint64 `json:"validation_llm_calls"`
- ModuleWallTimeMS int64 `json:"module_wall_time_ms"`
-}
-
-type ValidatorUtilization struct {
- ModuleKey string `json:"module_key"`
- ModuleInstance string `json:"module_instance"`
- ValidatorKey string `json:"validator_key"`
- LLMBacked bool `json:"llm_backed"`
- Calls int `json:"calls"`
- ElapsedMS int64 `json:"elapsed_ms"`
-}
diff --git a/internal/core/reporting/markdown.go b/internal/core/reporting/markdown.go
index c716631..9c6ed97 100644
--- a/internal/core/reporting/markdown.go
+++ b/internal/core/reporting/markdown.go
@@ -39,7 +39,6 @@ func Markdown(snapshot Snapshot, secrets ...string) []byte {
fmt.Fprintf(&b, "Proposal responses: %s; with filtering: %s\n\n",
showCount(snapshot.Summary.ProposalResponses), showCount(snapshot.Summary.ProposalResponsesWithFiltering))
fmt.Fprintf(&b, "Transcript input: %s \n", safe(snapshot.TranscriptPath))
- fmt.Fprintf(&b, "Glossary input: %s \n", safe(snapshot.GlossaryPath))
output := snapshot.OutputPath
if output == "" {
output = "stdout"
diff --git a/internal/core/reporting/markdown_test.go b/internal/core/reporting/markdown_test.go
index a0fcb2e..5947d87 100644
--- a/internal/core/reporting/markdown_test.go
+++ b/internal/core/reporting/markdown_test.go
@@ -14,7 +14,7 @@ func TestMarkdownFixedLayoutAndCounts(t *testing.T) {
one, two, three, four, zero := 1, 2, 3, 4, 0
snapshot := Snapshot{
RunID: "run-123", Status: StatusCompleted, StartedAt: time.Date(2026, 9, 24, 10, 0, 0, 0, time.UTC),
- TranscriptPath: "in|put.json", GlossaryPath: "glossary`secret`.yaml", OutputPath: "out\nfile.json",
+ TranscriptPath: "in|put`secret`.json", OutputPath: "out\nfile.json",
StageNames: []string{"grammar_1", "grammar_2"},
WorkUnits: []WorkUnit{
{StageIndex: 0, ModuleInstance: "grammar_1", ChunkIndex: 0, StartSegmentID: 1, EndSegmentID: 2, State: "completed", ErrorCount: 1, RecoveryCount: 1, CorrectionsApplied: 1},
@@ -45,7 +45,7 @@ func TestMarkdownFixedLayoutAndCounts(t *testing.T) {
if rows := markdownTableRows(text); !reflect.DeepEqual(rows, wantRows) {
t.Fatalf("matrices: got %v; want %v", rows, wantRows)
}
- for _, fragment := range []string{"in\\|put.json", "out
file.json", "glossary\\`[REDACTED]\\`.yaml", "`transport_failed`: 1", "`output_repaired`: 2", "`proposals_filtered`: 1", "`service_recovered`: 1"} {
+ for _, fragment := range []string{"in\\|put\\`[REDACTED]\\`.json", "out
file.json", "`transport_failed`: 1", "`output_repaired`: 2", "`proposals_filtered`: 1", "`service_recovered`: 1"} {
if !strings.Contains(text, fragment) {
t.Fatalf("missing %q in:\n%s", fragment, text)
}
diff --git a/internal/core/reporting/report.go b/internal/core/reporting/report.go
index c6305ce..c0d9b8e 100644
--- a/internal/core/reporting/report.go
+++ b/internal/core/reporting/report.go
@@ -136,7 +136,6 @@ type Snapshot struct {
StartedAt time.Time
CompletedAt *time.Time
TranscriptPath string
- GlossaryPath string
OutputPath string
StageNames []string
WorkUnits []WorkUnit
diff --git a/internal/framework/llm/runtime_inspection_test.go b/internal/framework/llm/runtime_inspection_test.go
index 870c954..1cf7750 100644
--- a/internal/framework/llm/runtime_inspection_test.go
+++ b/internal/framework/llm/runtime_inspection_test.go
@@ -4,9 +4,6 @@ import (
"context"
"errors"
"net/http"
- "os"
- "path/filepath"
- "strings"
"testing"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
@@ -22,38 +19,6 @@ func (r rejectingTransport) RoundTrip(*http.Request) (*http.Response, error) {
return nil, errors.New("provider access forbidden")
}
-func TestExplanationYAMLIncludesSafeInheritanceEvidence(t *testing.T) {
- data := `version: 3
-glossary_collections:
- common:
- entries:
- - id: a
- name: secret-value-not-for-explanation
- category: character
- derived:
- extends: common
-`
- file := filepath.Join(t.TempDir(), "config.yml")
- if err := os.WriteFile(file, []byte(data), 0600); err != nil {
- t.Fatal(err)
- }
- loaded, err := config.Load(ck.Input{Source: ck.File(file)}, "")
- if err != nil {
- t.Fatalf("%v: %+v", err, err.(*ck.LoadError).Diagnostics())
- }
- explanation, err := ExplanationYAML(loaded)
- if err != nil {
- t.Fatal(err)
- }
- text := string(explanation)
- if strings.Contains(text, "secret-value-not-for-explanation") {
- t.Fatal("explanation exposed an effective value")
- }
- if !strings.Contains(text, "/glossary_collections/derived/entries") || !strings.Contains(text, "inheritance:") || !strings.Contains(text, "/glossary_collections/common/entries") {
- t.Fatalf("inherited field evidence missing: %s", text)
- }
-}
-
func TestRuntimeInspectionDoesNotRequireCredentialsOrProvider(t *testing.T) {
loaded, err := config.Load(ck.Input{Source: ck.NoSource()}, "")
if err != nil {
@@ -62,7 +27,4 @@ func TestRuntimeInspectionDoesNotRequireCredentialsOrProvider(t *testing.T) {
if _, err := NewRuntime(context.Background(), loaded, &http.Client{Transport: rejectingTransport{t}}); err != nil {
t.Fatal(err)
}
- if _, err := ExplanationYAML(nil); err == nil {
- t.Fatal("nil configuration accepted")
- }
}
diff --git a/internal/framework/llm/safe_view.go b/internal/framework/llm/safe_view.go
deleted file mode 100644
index a7ef1e6..0000000
--- a/internal/framework/llm/safe_view.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package llm
-
-import (
- "fmt"
- "strings"
-
- "gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
- ck "go.maximumdirect.net/configkit"
- "gopkg.in/yaml.v3"
-)
-
-type derivationView struct {
- Operation ck.ChangeKind `yaml:"operation"`
- Reason string `yaml:"reason"`
- Dependencies []string `yaml:"dependencies,omitempty"`
-}
-type inheritanceStepView struct {
- From string `yaml:"from"`
- To string `yaml:"to"`
- Reference string `yaml:"reference"`
-}
-type explanationView struct {
- Path string `yaml:"path"`
- Source string `yaml:"source"`
- Layer ck.Layer `yaml:"layer"`
- Derivations []derivationView `yaml:"derivations,omitempty"`
- Inheritance []inheritanceStepView `yaml:"inheritance,omitempty"`
-}
-
-// ExplanationYAML reports only origins and recorded resolution operations.
-// It intentionally excludes effective and historical values, including secrets.
-func ExplanationYAML(result *ck.Result[config.Config]) ([]byte, error) {
- if result == nil {
- return nil, fmt.Errorf("configuration result is unavailable")
- }
- metadata := result.Metadata()
- sources := make(map[string]string)
- for _, source := range result.Sources() {
- sources[source.ID] = source.Label
- }
- records := make([]explanationView, 0)
- paths := metadata.HistoricalPaths()
- for i, path := range paths {
- if path.String() == "" {
- continue
- }
- if i+1 < len(paths) && strings.HasPrefix(paths[i+1].String(), path.String()+"/") {
- continue
- }
- info, ok := metadata.Lookup(path)
- if !ok {
- continue
- }
- record := explanationView{Path: path.String(), Layer: info.Origin.Layer, Source: string(info.Origin.Layer)}
- for _, step := range info.Origin.Inheritance {
- record.Inheritance = append(record.Inheritance, inheritanceStepView{
- From: step.From.String(), To: step.To.String(), Reference: step.Reference.String(),
- })
- }
- if len(info.Origin.Locations) > 0 {
- if label := sources[info.Origin.Locations[0].SourceID]; label != "" {
- record.Source = label
- }
- }
- for _, change := range metadata.History(path) {
- if change.Origin.Layer != ck.LayerResolution {
- continue
- }
- for _, reason := range change.Reasons {
- derivation := derivationView{Operation: change.Origin.Operation, Reason: reason}
- for _, dependency := range change.Dependencies {
- derivation.Dependencies = append(derivation.Dependencies, dependency.String())
- }
- record.Derivations = append(record.Derivations, derivation)
- }
- }
- records = append(records, record)
- }
- return yaml.Marshal(records)
-}
diff --git a/internal/framework/processreport/llm.go b/internal/framework/processreport/llm.go
index a3d1a9f..b91c370 100644
--- a/internal/framework/processreport/llm.go
+++ b/internal/framework/processreport/llm.go
@@ -5,9 +5,7 @@ import (
"gitea.maximumdirect.net/eric/audita/v3/internal/core/reporting"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
- "gitea.maximumdirect.net/eric/audita/v3/internal/framework/runner"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/structuredoutput"
- pk "go.maximumdirect.net/promptkit"
)
func mapLLMSummary(source llm.ExecutionSummary) reporting.LLMSummary {
@@ -21,81 +19,6 @@ func mapLLMSummary(source llm.ExecutionSummary) reporting.LLMSummary {
}
}
-// BuildUtilization groups complete logical calls by their selected backend.
-func BuildUtilization(output *runner.RunOutput, backends map[string]*pk.BackendInspection) reporting.UtilizationDiagnostics {
- result := reporting.UtilizationDiagnostics{Backends: make(map[string]reporting.BackendUtilization, len(backends))}
- if output == nil {
- return result
- }
- result.RunWallTimeMS = output.RunWallTimeMS
- groups := make(map[string]*llm.ObservationSnapshot, len(backends))
- callBackends := make(map[uint64]string, len(output.Observations.Calls))
- moduleIndices := make(map[string]int)
- moduleIndex := func(key, instance string) int {
- if i, ok := moduleIndices[instance]; ok {
- return i
- }
- i := len(result.Modules)
- moduleIndices[instance] = i
- result.Modules = append(result.Modules, reporting.ModuleUtilization{ModuleKey: key, ModuleInstance: instance})
- return i
- }
- // Seed configured order, including modules that made no LLM calls.
- for _, module := range output.ModuleResults {
- i := moduleIndex(module.ModuleKey, module.ModuleInstance)
- result.Modules[i].ModuleWallTimeMS = module.CompletedAt.Sub(module.StartedAt).Milliseconds()
- }
- for _, call := range output.Observations.Calls {
- id := call.Prompt.BackendID
- if groups[id] == nil {
- groups[id] = &llm.ObservationSnapshot{}
- }
- groups[id].Calls = append(groups[id].Calls, call)
- callBackends[call.CallID] = id
- if call.Context.ModuleInstance != "" {
- i := moduleIndex(call.Context.ModuleKey, call.Context.ModuleInstance)
- switch call.Context.Role {
- case "proposal":
- result.Modules[i].ProposalLLMCalls++
- case "validation":
- result.Modules[i].ValidationLLMCalls++
- }
- }
- }
- for _, event := range output.Observations.Events {
- if id, ok := callBackends[event.CallID]; ok {
- groups[id].Events = append(groups[id].Events, event)
- }
- }
- for id, backend := range backends {
- group := groups[id]
- if group == nil {
- group = &llm.ObservationSnapshot{}
- }
- var wait, generation, retry, wall time.Duration
- for _, call := range group.Calls {
- wall += call.WallDuration
- }
- for _, event := range group.Events {
- if event.Kind == string(pk.ExecutionFinished) {
- wait += event.CapacityWait
- generation += event.GenerationDuration
- retry += event.RetryDelay
- }
- }
- result.Backends[id] = reporting.BackendUtilization{
- Concurrency: backend.ConcurrencyLimit, QueueCapacity: backend.QueueCapacity,
- LLMSummary: mapLLMSummary(llm.Summarize(*group)),
- CapacityWaitMS: wait.Milliseconds(), GenerationMS: generation.Milliseconds(),
- RetryDelayMS: retry.Milliseconds(), LogicalWallMS: wall.Milliseconds(),
- }
- }
- for _, timing := range output.ValidatorTimings {
- result.Validators = append(result.Validators, reporting.ValidatorUtilization{ModuleKey: timing.ModuleKey, ModuleInstance: timing.ModuleInstance, ValidatorKey: timing.ValidatorName, LLMBacked: timing.LLMBacked, Calls: timing.Calls, ElapsedMS: timing.ElapsedMS})
- }
- return result
-}
-
type executionCall struct {
CallID uint64 `json:"call_id"`
Context structuredoutput.DiagnosticContext `json:"context"`
diff --git a/internal/framework/processreport/llm_test.go b/internal/framework/processreport/llm_test.go
index 530636a..0b618cb 100644
--- a/internal/framework/processreport/llm_test.go
+++ b/internal/framework/processreport/llm_test.go
@@ -8,12 +8,11 @@ import (
"time"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/llm"
- "gitea.maximumdirect.net/eric/audita/v3/internal/framework/runner"
"gitea.maximumdirect.net/eric/audita/v3/internal/framework/structuredoutput"
pk "go.maximumdirect.net/promptkit"
)
-func TestExecutionReportingGroupsPredispatchFailureAndKeepsSafeMilliseconds(t *testing.T) {
+func TestExecutionReportingKeepsSafeMilliseconds(t *testing.T) {
start := time.Now().UTC()
snapshot := llm.ObservationSnapshot{
Calls: []llm.LogicalCall{
@@ -22,14 +21,6 @@ func TestExecutionReportingGroupsPredispatchFailureAndKeepsSafeMilliseconds(t *t
},
Events: []llm.ExecutionObservation{{CallID: 1, Kind: string(pk.ExecutionFinished), BackendID: "primary", Attempts: 2, TransientRetries: 1, CapacityWait: 20 * time.Millisecond, GenerationDuration: 500 * time.Millisecond, RetryDelay: 100 * time.Millisecond}},
}
- output := &runner.RunOutput{Observations: snapshot, ObservationsAvailable: true, LLMSummary: llm.Summarize(snapshot)}
- utilization := BuildUtilization(output, map[string]*pk.BackendInspection{"primary": {ConcurrencyLimit: intPointer(2), QueueCapacity: intPointer(3)}, "secondary": {ConcurrencyLimit: intPointer(1)}})
- if utilization.Backends["primary"].LogicalCalls != 1 || utilization.Backends["primary"].ProviderAttempts != 2 || utilization.Backends["primary"].CapacityWaitMS != 20 || utilization.Backends["primary"].LogicalWallMS != 1500 {
- t.Fatalf("primary backend: %+v", utilization.Backends["primary"])
- }
- if utilization.Backends["secondary"].LogicalCalls != 1 || utilization.Backends["secondary"].ProviderAttempts != 0 || !utilization.Backends["secondary"].Usage.Total.Known {
- t.Fatalf("predispatch backend: %+v", utilization.Backends["secondary"])
- }
raw, err := json.Marshal(BuildLLMExecution(snapshot))
if err != nil {
t.Fatal(err)
@@ -57,17 +48,11 @@ func BenchmarkExecutionReports(b *testing.B) {
snapshot.Calls = append(snapshot.Calls, llm.LogicalCall{CallID: uint64(i + 1), Prompt: structuredoutput.PromptMetadata{BackendID: "local"}, Context: structuredoutput.DiagnosticContext{ModuleInstance: "grammar", Role: "proposal"}})
snapshot.Events = append(snapshot.Events, llm.ExecutionObservation{CallID: uint64(i + 1), Kind: string(pk.ExecutionFinished), Attempts: 1})
}
- output := &runner.RunOutput{Observations: snapshot}
b.Run(fmt.Sprintf("execution/%d", n), func(b *testing.B) {
for i := 0; i < b.N; i++ {
BuildLLMExecution(snapshot)
}
})
- b.Run(fmt.Sprintf("utilization/%d", n), func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- BuildUtilization(output, map[string]*pk.BackendInspection{"local": {ConcurrencyLimit: intPointer(1)}})
- }
- })
}
}
@@ -108,15 +93,4 @@ func TestIndexedReportsPreserveOrderAndPartialUsage(t *testing.T) {
if execution.Calls[1].Summary.Usage.Total.Value != 7 || !execution.Calls[1].Summary.Usage.Total.Complete || execution.Calls[0].Summary.Usage.Total.Known {
t.Fatalf("call usage: %+v", execution.Calls)
}
- output := &runner.RunOutput{Observations: snapshot}
- utilization := BuildUtilization(output, map[string]*pk.BackendInspection{"a": {}, "b": {}})
- total := utilization.Backends["a"].Usage.Total
- if total.Value != 7 || !total.Known || total.Complete || utilization.Backends["a"].ProviderAttempts != 2 {
- t.Fatalf("partial backend usage: %+v", utilization)
- }
- if len(utilization.Modules) != 2 || utilization.Modules[1].ProposalLLMCalls != 1 || utilization.Modules[1].ValidationLLMCalls != 1 {
- t.Fatalf("module grouping: %+v", utilization.Modules)
- }
}
-
-func intPointer(value int) *int { return &value }
diff --git a/internal/framework/processreport/report.go b/internal/framework/processreport/report.go
index 5e40e8e..26a9ff7 100644
--- a/internal/framework/processreport/report.go
+++ b/internal/framework/processreport/report.go
@@ -25,7 +25,6 @@ type BuildInput struct {
StartedAt time.Time
CompletedAt *time.Time
TranscriptPath string
- GlossaryPath string
OutputPath string
ModuleSpecs []contracts.ModuleRunSpec
RunOutput *runner.RunOutput
@@ -36,7 +35,7 @@ type BuildInput struct {
func Build(input BuildInput) reporting.Snapshot {
snapshot := reporting.Snapshot{
RunID: input.RunID, Status: input.Status, StartedAt: input.StartedAt.UTC(), CompletedAt: input.CompletedAt,
- TranscriptPath: input.TranscriptPath, GlossaryPath: input.GlossaryPath, OutputPath: input.OutputPath,
+ TranscriptPath: input.TranscriptPath, OutputPath: input.OutputPath,
StageNames: make([]string, len(input.ModuleSpecs)), Errors: make([]reporting.ErrorRecord, 0),
Recoveries: make([]reporting.RecoveryRecord, 0), WorkUnits: make([]reporting.WorkUnit, 0),
}
diff --git a/internal/framework/runner/runner.go b/internal/framework/runner/runner.go
index 7a02ae6..5452def 100644
--- a/internal/framework/runner/runner.go
+++ b/internal/framework/runner/runner.go
@@ -7,7 +7,6 @@ import (
"path/filepath"
"sort"
"sync"
- "time"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/chunking"
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
@@ -51,8 +50,6 @@ type ModuleResult struct {
AppliedChanges []proposals.AppliedChange `json:"applied_changes,omitempty"`
SkippedChanges []proposals.SkippedChange `json:"skipped_changes,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
- StartedAt time.Time `json:"started_at"`
- CompletedAt time.Time `json:"completed_at"`
}
// WorkUnitOutcome keeps initial section identity while text evolves between
@@ -102,22 +99,16 @@ type RunInput struct {
ProposalDiagnosticsDir string
ValidationDiagnosticsDir string
CaptureCandidates bool
- timings *validatorTimingCollector
}
// RunOutput is the deterministic runner output.
type RunOutput struct {
- FinalTranscript *schema.Transcript `json:"-"`
- ModuleResults []ModuleResult `json:"module_results"`
- Sections []contracts.SectionMetadata `json:"sections"`
- InventoryAvailable bool `json:"-"`
- WorkUnits []WorkUnitOutcome `json:"work_units"`
- Candidates []proposals.EnrichedCorrectionProposal `json:"-"`
- Observations llm.ObservationSnapshot `json:"-"`
- ObservationsAvailable bool `json:"-"`
- LLMSummary llm.ExecutionSummary `json:"-"`
- RunWallTimeMS int64 `json:"-"`
- ValidatorTimings []ValidatorTiming `json:"-"`
+ FinalTranscript *schema.Transcript `json:"-"`
+ ModuleResults []ModuleResult `json:"module_results"`
+ Sections []contracts.SectionMetadata `json:"sections"`
+ InventoryAvailable bool `json:"-"`
+ WorkUnits []WorkUnitOutcome `json:"work_units"`
+ Candidates []proposals.EnrichedCorrectionProposal `json:"-"`
}
func New(factory ModuleFactory) *Runner {
@@ -125,15 +116,6 @@ func New(factory ModuleFactory) *Runner {
}
func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
- started := time.Now()
- input.timings = newValidatorTimingCollector()
- output, err := r.run(ctx, input)
- output.RunWallTimeMS = time.Since(started).Milliseconds()
- output.ValidatorTimings = input.timings.snapshot()
- return output, err
-}
-
-func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
if r == nil || r.factory == nil {
return RunOutput{}, fmt.Errorf("runner module factory is required")
}
@@ -167,7 +149,6 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
}
for moduleIndex, spec := range input.ModuleSpecs {
- startedAt := time.Now().UTC()
module, err := r.factory.ModuleForSpec(spec)
if err != nil {
failed := ModuleResult{
@@ -176,8 +157,6 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
ModuleInstance: spec.InstanceName,
Status: ModuleStatusFailed,
ErrorMessage: err.Error(),
- StartedAt: startedAt,
- CompletedAt: time.Now().UTC(),
}
results = append(results, failed)
return snapshot(), fmt.Errorf("module %q setup failed: %w", spec.InstanceName, err)
@@ -193,7 +172,6 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
DiagnosticsDir: input.ProposalDiagnosticsDir,
ValidationClient: spec.ValidationClient,
ValidationDiagnosticsDir: input.ValidationDiagnosticsDir,
- Timings: input.timings,
ReportDiagnosticError: input.ReportDiagnosticError,
})
if input.CaptureCandidates {
@@ -211,8 +189,6 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
ValidatorDecisions: pipelineResult.ValidatorDecisions,
ValidatorRejected: pipelineResult.ValidatorRejected,
ErrorMessage: pipelineErr.Error(),
- StartedAt: startedAt,
- CompletedAt: time.Now().UTC(),
}
results = append(results, failed)
copy(workUnits[moduleIndex*len(sections):(moduleIndex+1)*len(sections)], pipelineResult.WorkUnits)
@@ -252,8 +228,6 @@ func (r *Runner) run(ctx context.Context, input RunInput) (RunOutput, error) {
ValidatorRejected: pipelineResult.ValidatorRejected,
AppliedChanges: applied,
SkippedChanges: skipped,
- StartedAt: startedAt,
- CompletedAt: time.Now().UTC(),
})
}
@@ -312,7 +286,6 @@ type collectSectionProposalsInput struct {
DiagnosticsDir string
ValidationClient contracts.StructuredLLMClient
ValidationDiagnosticsDir string
- Timings *validatorTimingCollector
}
type sectionProposalResult struct {
@@ -515,7 +488,6 @@ func runModulePipeline(ctx context.Context, input collectSectionProposalsInput)
SectionEnriched: enriched,
ValidationLLMClient: input.ValidationClient,
DiagnosticsDir: input.ValidationDiagnosticsDir,
- Timings: input.Timings,
ReportDiagnosticError: input.ReportDiagnosticError,
})
setErr(err)
@@ -599,7 +571,6 @@ type validateSectionCandidatesInput struct {
SectionEnriched []proposals.EnrichedCorrectionProposal
ValidationLLMClient contracts.StructuredLLMClient
DiagnosticsDir string
- Timings *validatorTimingCollector
}
type validateSectionCandidatesResult struct {
@@ -637,7 +608,6 @@ func validateSectionCandidates(ctx context.Context, input validateSectionCandida
}
}
- validatorStarted := time.Now()
vResult, err := validator.Validate(ctx, contracts.ValidationRequest{
StageIndex: &input.Spec.Index, StageID: input.Spec.ID,
WorkingTranscript: input.WorkingTranscript,
@@ -654,9 +624,6 @@ func validateSectionCandidates(ctx context.Context, input validateSectionCandida
DiagnosticsWriter: diagnosticsWriter,
ReportDiagnosticError: input.ReportDiagnosticError,
})
- if input.Timings != nil {
- input.Timings.record(input.Spec.ModuleKey, input.Spec.InstanceName, validator.Name(), validatormetadata.ClassOf(validator) == validatormetadata.ExecutionClassLLMBacked, time.Since(validatorStarted))
- }
operations = append(operations, vResult.Operations...)
var decisionErr error
diff --git a/internal/framework/runner/runner_test.go b/internal/framework/runner/runner_test.go
index c4aff57..7251f4a 100644
--- a/internal/framework/runner/runner_test.go
+++ b/internal/framework/runner/runner_test.go
@@ -753,9 +753,6 @@ func TestRunnerValidatorApprovedProposalApplied(t *testing.T) {
if len(out.ModuleResults[0].ValidatorDecisions) != 1 {
t.Fatalf("expected validator decisions recorded")
}
- if len(out.ValidatorTimings) != 1 || out.ValidatorTimings[0].Calls != 1 || out.ValidatorTimings[0].ValidatorName != "allow" || out.RunWallTimeMS < 0 {
- t.Fatalf("runner timings missing: %+v", out.ValidatorTimings)
- }
}
func TestRunnerValidatorRejectedProposalNotApplied(t *testing.T) {
diff --git a/internal/framework/runner/timing.go b/internal/framework/runner/timing.go
deleted file mode 100644
index 7bd832b..0000000
--- a/internal/framework/runner/timing.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package runner
-
-import (
- "sort"
- "sync"
- "time"
-)
-
-// ValidatorTiming records domain validator work without treating a scheduler
-// permit or provider attempt as a logical LLM call.
-type ValidatorTiming struct {
- ModuleKey string `json:"module_key"`
- ModuleInstance string `json:"module_instance"`
- ValidatorName string `json:"validator_name"`
- LLMBacked bool `json:"llm_backed"`
- Calls int `json:"calls"`
- ElapsedMS int64 `json:"elapsed_ms"`
-}
-
-type validatorTimingCollector struct {
- mu sync.Mutex
- byKey map[string]*ValidatorTiming
-}
-
-func newValidatorTimingCollector() *validatorTimingCollector {
- return &validatorTimingCollector{byKey: make(map[string]*ValidatorTiming)}
-}
-
-func (c *validatorTimingCollector) record(moduleKey, moduleInstance, validatorName string, llmBacked bool, elapsed time.Duration) {
- c.mu.Lock()
- defer c.mu.Unlock()
- key := moduleInstance + "\x00" + validatorName
- timing := c.byKey[key]
- if timing == nil {
- timing = &ValidatorTiming{ModuleKey: moduleKey, ModuleInstance: moduleInstance, ValidatorName: validatorName, LLMBacked: llmBacked}
- c.byKey[key] = timing
- }
- timing.Calls++
- timing.ElapsedMS += elapsed.Milliseconds()
-}
-
-func (c *validatorTimingCollector) snapshot() []ValidatorTiming {
- c.mu.Lock()
- defer c.mu.Unlock()
- result := make([]ValidatorTiming, 0, len(c.byKey))
- for _, timing := range c.byKey {
- result = append(result, *timing)
- }
- sort.Slice(result, func(i, j int) bool {
- if result[i].ModuleInstance != result[j].ModuleInstance {
- return result[i].ModuleInstance < result[j].ModuleInstance
- }
- return result[i].ValidatorName < result[j].ValidatorName
- })
- return result
-}