Files
beads/internal/export/manifest.go
Steve Yegge e3e0a04496 Add configurable export error handling policies (bd-exug)
Implements flexible error handling for export operations with four policies:
- strict: Fail-fast on any error (default for user exports)
- best-effort: Skip errors with warnings (default for auto-exports)
- partial: Retry then skip with manifest tracking
- required-core: Fail on core data, skip enrichments

Key features:
- Per-project configuration via `bd config set export.error_policy`
- Separate policy for auto-exports: `auto_export.error_policy`
- Retry with exponential backoff (configurable attempts/delay)
- Optional export manifests documenting completeness
- Per-issue encoding error handling

This allows users to choose the right trade-off between data integrity
and system availability for their specific project needs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 20:21:51 -08:00

66 lines
1.6 KiB
Go

package export
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
// WriteManifest writes an export manifest alongside the JSONL file
func WriteManifest(jsonlPath string, manifest *Manifest) error {
// Derive manifest path from JSONL path
manifestPath := strings.TrimSuffix(jsonlPath, ".jsonl") + ".manifest.json"
// Marshal manifest
data, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return fmt.Errorf("failed to marshal manifest: %w", err)
}
// Create temp file for atomic write
dir := filepath.Dir(manifestPath)
base := filepath.Base(manifestPath)
tempFile, err := os.CreateTemp(dir, base+".tmp.*")
if err != nil {
return fmt.Errorf("failed to create temp manifest file: %w", err)
}
tempPath := tempFile.Name()
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempPath)
}()
// Write manifest
if _, err := tempFile.Write(data); err != nil {
return fmt.Errorf("failed to write manifest: %w", err)
}
// Close before rename
_ = tempFile.Close()
// Atomic replace
if err := os.Rename(tempPath, manifestPath); err != nil {
return fmt.Errorf("failed to replace manifest file: %w", err)
}
// Set appropriate file permissions (0600: rw-------)
if err := os.Chmod(manifestPath, 0600); err != nil {
// Non-fatal, just log
fmt.Fprintf(os.Stderr, "Warning: failed to set manifest permissions: %v\n", err)
}
return nil
}
// NewManifest creates a new export manifest
func NewManifest(policy ErrorPolicy) *Manifest {
return &Manifest{
ExportedAt: time.Now(),
ErrorPolicy: string(policy),
Complete: true, // Will be set to false if any data is missing
}
}