Make debug evidence opt-in and retain all candidates

This commit is contained in:
2026-09-24 17:41:10 +00:00
parent a80eed4745
commit 87b8d9ad5c
10 changed files with 246 additions and 213 deletions

View File

@@ -1,9 +1,11 @@
package diagnostics
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"gitea.maximumdirect.net/eric/audita/internal/core/chunking"
@@ -16,42 +18,15 @@ import (
type RunDirectory struct {
redactor redaction.Redactor
path string
retention string
createdAt time.Time
}
type RetentionDecisionInput struct {
RetentionMode string
RunSucceeded bool
HasSkippedCorrections bool
}
func ShouldRetainRunDirectory(input RetentionDecisionInput) bool {
// Failed runs are always retained.
if !input.RunSucceeded {
return true
}
switch input.RetentionMode {
case "always":
return true
case "never":
return true
case "auto":
return input.HasSkippedCorrections
default:
// Be conservative for unknown values.
return true
}
}
// 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"`
ReportJSONPath string `json:"report_json_path,omitempty"`
ConfigPath string `json:"config_path,omitempty"`
ConfigSource string `json:"config_source,omitempty"`
Overlay string `json:"overlay,omitempty"`
@@ -63,9 +38,12 @@ type InvocationMetadata struct {
}
// NewRunDirectory creates a new run directory under the configured work dir
func NewRunDirectory(workDir, retention string, secrets ...string) (*RunDirectory, error) {
if workDir == "" {
workDir = ".audita-runs"
func NewRunDirectory(workDir, runID string, secrets ...string) (*RunDirectory, error) {
if strings.TrimSpace(workDir) == "" {
return nil, fmt.Errorf("debug work directory is required")
}
if strings.TrimSpace(runID) == "" || filepath.Base(runID) != runID || runID == "." || runID == ".." {
return nil, fmt.Errorf("valid run ID is required")
}
// Create work directory if it doesn't exist
@@ -73,8 +51,6 @@ func NewRunDirectory(workDir, retention string, secrets ...string) (*RunDirector
return nil, fmt.Errorf("failed to create work directory %q: %w", workDir, err)
}
// Create a unique run directory identifier.
runID := fmt.Sprintf("run-%d", time.Now().UTC().UnixNano())
runPath := filepath.Join(workDir, runID)
if err := os.Mkdir(runPath, 0o755); err != nil {
@@ -84,7 +60,6 @@ func NewRunDirectory(workDir, retention string, secrets ...string) (*RunDirector
return &RunDirectory{
path: runPath,
redactor: redaction.New(secrets),
retention: retention,
createdAt: time.Now().UTC(),
}, nil
}
@@ -126,9 +101,19 @@ func (r *RunDirectory) WriteEffectiveConfig(effectiveYAML []byte) error {
// WriteSourceTranscript writes the source transcript artifact
func (r *RunDirectory) WriteSourceTranscript(transcript *schema.SourceTranscript, raw []byte) error {
// Write raw source for reference
// Preserve the source snapshot without copying unredacted input bytes.
var source any
decoder := json.NewDecoder(strings.NewReader(string(raw)))
decoder.UseNumber()
if err := decoder.Decode(&source); err != nil {
return fmt.Errorf("decode source snapshot: %w", err)
}
sourceBytes, err := r.redactor.JSON(source)
if err != nil {
return fmt.Errorf("redact source snapshot: %w", err)
}
sourcePath := filepath.Join(r.path, ArtifactSourceTranscript)
if err := os.WriteFile(sourcePath, raw, 0o644); err != nil {
if err := os.WriteFile(sourcePath, append(sourceBytes, '\n'), 0o644); err != nil {
return fmt.Errorf("failed to write source transcript: %w", err)
}
@@ -149,7 +134,7 @@ func (r *RunDirectory) WriteSourceTranscript(transcript *schema.SourceTranscript
// WriteNormalizedTranscript writes the normalized transcript artifact
func (r *RunDirectory) WriteNormalizedTranscript(transcript *schema.Transcript) error {
normalizedPath := filepath.Join(r.path, ArtifactNormalizedTranscript)
bytes, err := schema.TranscriptToJSON(transcript)
bytes, err := r.redactor.JSON(transcript)
if err != nil {
return fmt.Errorf("failed to serialize normalized transcript: %w", err)
}
@@ -218,15 +203,3 @@ func (r *RunDirectory) WriteYAMLArtifact(name string, payload []byte) error {
}
return nil
}
func (r *RunDirectory) ApplyRetention(input RetentionDecisionInput) error {
decision := input
if decision.RetentionMode == "" {
decision.RetentionMode = r.retention
}
if ShouldRetainRunDirectory(decision) {
return nil
}
return os.RemoveAll(r.path)
}