feat(infra): audit_dod_schema + fn doctor dod (issue 0114)

Adds AuditDodSchema(issuesDir, flowsDir) which scans dev/issues/ and
dev/flows/ frontmatter for the new optional dod_evidence_schema: block.
Validates id uniqueness, kind in {screenshot,log,url,cmd}, expected
non-empty and required bool (default true). Tolerant to malformed YAML
and missing block.

Wires it into fn doctor dod with human-readable caveman output and
--json support.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 18:12:49 +02:00
parent ddb5366884
commit 78d955fd72
3 changed files with 319 additions and 0 deletions
+42
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"text/tabwriter"
@@ -63,6 +64,8 @@ func cmdDoctor(args []string) {
doctorAppLocation(r, jsonOut)
case "modules":
doctorModules(r, jsonOut)
case "dod":
doctorDod(r, jsonOut)
default:
fmt.Fprintf(os.Stderr, "unknown doctor subcommand: %s\n", sub)
doctorUsage()
@@ -90,6 +93,7 @@ Subcommands:
capabilities Drift entre docs/capabilities/INDEX.md, tags de funciones, y paginas <grupo>.md (issue 0086)
app-location Detecta artefactos (apps/analysis) en carpetas de lenguaje (cpp/apps/, etc.) - issue 0096
modules Drift entre uses_modules (app.md) y fn_module_<x> link calls (CMakeLists.txt) - issue 0097
dod Audita bloque dod_evidence_schema en dev/issues/ y dev/flows/ (issue 0114)
Flags:
--json Salida JSON (para scripting/agentes)
@@ -588,3 +592,41 @@ func doctorModules(root string, jsonOut bool) {
fmt.Println("Fix: align uses_modules in app.md with target_link_libraries(fn_module_*) in CMakeLists.txt.")
}
}
func doctorDod(root string, jsonOut bool) {
issuesDir := filepath.Join(root, "dev", "issues")
flowsDir := filepath.Join(root, "dev", "flows")
report, err := infra.AuditDodSchema(issuesDir, flowsDir)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
if jsonOut {
emit(report)
return
}
fmt.Println("=== DoD Schema Audit ===")
fmt.Printf("files scanned: %d\n", report.TotalFiles)
fmt.Printf("with schema: %d\n", report.FilesWithItems)
fmt.Printf("total items: %d\n", report.TotalItems)
fmt.Printf("invalid items: %d\n", report.InvalidItems)
if report.InvalidItems == 0 {
fmt.Println("\nAll DoD schemas valid.")
return
}
fmt.Println()
rel := func(p string) string {
if r, err := filepath.Rel(root, p); err == nil {
return r
}
return p
}
for _, f := range report.Files {
if len(f.Errors) == 0 {
continue
}
for _, e := range f.Errors {
fmt.Printf("%s : %s\n", rel(f.Path), e)
}
}
}