feat(config): add validation.on-create and validation.on-sync config options (bd-t7jq)

Add .beads/config.yaml support for template validation settings:
- validation.on-create: warn|error|none (default: none)
- validation.on-sync: warn|error|none (default: none)

When set to "warn", issues missing required sections (based on type) show
warnings but operations proceed. When set to "error", operations fail.

Implementation:
- Add validation keys to YamlOnlyKeys in yaml_config.go
- Add defaults in config.go
- Wire up bd create to check validation.on-create config
- Wire up bd sync to run validation before export
- Add tests for config loading
- Update CONFIG.md documentation

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
topaz
2026-01-01 19:31:12 -08:00
committed by Steve Yegge
parent b73085962c
commit 65fb0c6d77
7 changed files with 164 additions and 2 deletions

View File

@@ -786,3 +786,61 @@ func TestConfigSourceConstants(t *testing.T) {
t.Errorf("SourceFlag = %q, want \"flag\"", SourceFlag)
}
}
func TestValidationConfigDefaults(t *testing.T) {
// Isolate from environment variables
restore := envSnapshot(t)
defer restore()
// Initialize config
if err := Initialize(); err != nil {
t.Fatalf("Initialize() returned error: %v", err)
}
// Test validation.on-create default is "none"
if got := GetString("validation.on-create"); got != "none" {
t.Errorf("GetString(validation.on-create) = %q, want \"none\"", got)
}
// Test validation.on-sync default is "none"
if got := GetString("validation.on-sync"); got != "none" {
t.Errorf("GetString(validation.on-sync) = %q, want \"none\"", got)
}
}
func TestValidationConfigFromFile(t *testing.T) {
// Create a temporary directory for config file
tmpDir := t.TempDir()
// Create a config file with validation settings
configContent := `
validation:
on-create: error
on-sync: warn
`
beadsDir := filepath.Join(tmpDir, ".beads")
if err := os.MkdirAll(beadsDir, 0750); err != nil {
t.Fatalf("failed to create .beads directory: %v", err)
}
configPath := filepath.Join(beadsDir, "config.yaml")
if err := os.WriteFile(configPath, []byte(configContent), 0600); err != nil {
t.Fatalf("failed to write config file: %v", err)
}
// Change to tmp directory
t.Chdir(tmpDir)
// Initialize viper
if err := Initialize(); err != nil {
t.Fatalf("Initialize() returned error: %v", err)
}
// Test that validation settings are loaded correctly
if got := GetString("validation.on-create"); got != "error" {
t.Errorf("GetString(validation.on-create) = %q, want \"error\"", got)
}
if got := GetString("validation.on-sync"); got != "warn" {
t.Errorf("GetString(validation.on-sync) = %q, want \"warn\"", got)
}
}