chore: auto-commit (95 archivos)
- cmd/fn/doctor.go - cmd/fn/main.go - cpp/apps/primitives_gallery/playground/tables/CMakeLists.txt - cpp/apps/primitives_gallery/playground/tables/data_table.cpp - cpp/apps/primitives_gallery/playground/tables/data_table_logic.cpp - cpp/apps/primitives_gallery/playground/tables/data_table_logic.h - cpp/apps/primitives_gallery/playground/tables/self_test.cpp - cpp/apps/primitives_gallery/playground/tables/tql.cpp - cpp/apps/primitives_gallery/playground/tables/viz.cpp - cpp/apps/primitives_gallery/playground/tables/viz.h - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// VaultDoctorEntry holds the health report for a single vault.
|
||||
type VaultDoctorEntry struct {
|
||||
VaultName string `json:"vault_name"`
|
||||
VaultPath string `json:"vault_path"`
|
||||
ProjectID string `json:"project_id"`
|
||||
Issues []string `json:"issues"` // human-readable issues; empty = healthy
|
||||
IndexedFiles int `json:"indexed_files"` // 0 if no vault_index.db
|
||||
LastIndexedAt int64 `json:"last_indexed_at"` // unix seconds; 0 if N/A
|
||||
DiskFiles int `json:"disk_files"` // count via WalkDir (no hashing)
|
||||
Status string `json:"status"` // "ok" | "warning" | "error"
|
||||
}
|
||||
|
||||
// VaultDoctor audits every vault declared in projects/*/vaults/vault.yaml under
|
||||
// repoRoot. For each vault it performs a series of checks (disk presence, layout,
|
||||
// index existence, staleness, drift) and returns a slice of VaultDoctorEntry.
|
||||
//
|
||||
// The function is read-only: it never writes to disk or any database.
|
||||
// Returns an error only if VaultManifestRead fails (manifest parse error).
|
||||
func VaultDoctor(repoRoot string) ([]VaultDoctorEntry, error) {
|
||||
entries, err := VaultManifestRead(repoRoot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vault_doctor: read manifests: %w", err)
|
||||
}
|
||||
|
||||
results := make([]VaultDoctorEntry, 0, len(entries))
|
||||
for _, e := range entries {
|
||||
result := auditVault(e)
|
||||
results = append(results, result)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func auditVault(e VaultManifestEntry) VaultDoctorEntry {
|
||||
entry := VaultDoctorEntry{
|
||||
VaultName: e.Name,
|
||||
VaultPath: e.Path,
|
||||
ProjectID: e.ProjectID,
|
||||
}
|
||||
|
||||
// Resolve symlinks for disk checks
|
||||
realPath, err := filepath.EvalSymlinks(e.Path)
|
||||
if err != nil || realPath == "" {
|
||||
realPath = e.Path
|
||||
}
|
||||
|
||||
// CHECK 1: directory_missing
|
||||
info, statErr := os.Stat(realPath)
|
||||
if statErr != nil || !info.IsDir() {
|
||||
entry.Issues = append(entry.Issues, "directory_missing")
|
||||
entry.Status = "error"
|
||||
return entry
|
||||
}
|
||||
|
||||
// COUNT disk files (cheap walk — no hashing, no mime detection)
|
||||
diskCount := countDiskFiles(realPath)
|
||||
entry.DiskFiles = diskCount
|
||||
|
||||
// CHECK 2: layout_missing / non_standard_layout
|
||||
hasData := dirExists(filepath.Join(realPath, "data"))
|
||||
hasKnowledge := dirExists(filepath.Join(realPath, "knowledge"))
|
||||
if !hasData && !hasKnowledge {
|
||||
// Check if it looks like a non-standard but intentional layout
|
||||
if hasNonStandardLayout(realPath) {
|
||||
entry.Issues = append(entry.Issues, "non_standard_layout")
|
||||
} else {
|
||||
entry.Issues = append(entry.Issues, "layout_missing")
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK 3: index_missing
|
||||
indexPath := filepath.Join(realPath, "vault_index.db")
|
||||
_, indexStatErr := os.Stat(indexPath)
|
||||
if indexStatErr != nil {
|
||||
entry.Issues = append(entry.Issues, "index_missing")
|
||||
entry.setWarningStatus()
|
||||
entry.setFinalStatus()
|
||||
return entry
|
||||
}
|
||||
|
||||
// Open vault index (read-only) for checks 4 and 5
|
||||
vdb, openErr := VaultIndexOpen(realPath)
|
||||
if openErr != nil {
|
||||
entry.Issues = append(entry.Issues, fmt.Sprintf("index_open_error: %v", openErr))
|
||||
entry.setWarningStatus()
|
||||
return entry
|
||||
}
|
||||
defer vdb.Close()
|
||||
|
||||
// Query indexed file count and max indexed_at
|
||||
var indexedCount int
|
||||
var maxIndexedAt int64
|
||||
row := vdb.QueryRow(`SELECT COUNT(*), COALESCE(MAX(indexed_at), 0) FROM files`)
|
||||
if scanErr := row.Scan(&indexedCount, &maxIndexedAt); scanErr != nil {
|
||||
entry.Issues = append(entry.Issues, fmt.Sprintf("index_query_error: %v", scanErr))
|
||||
} else {
|
||||
entry.IndexedFiles = indexedCount
|
||||
entry.LastIndexedAt = maxIndexedAt
|
||||
}
|
||||
|
||||
// CHECK 4: index_stale — any file on disk newer than MAX(indexed_at)
|
||||
if maxIndexedAt > 0 {
|
||||
maxTime := time.Unix(maxIndexedAt, 0)
|
||||
if isIndexStale(realPath, maxTime) {
|
||||
entry.Issues = append(entry.Issues, "index_stale")
|
||||
}
|
||||
}
|
||||
|
||||
// CHECK 5: index_drift — disk file count != indexed count
|
||||
if indexedCount != diskCount {
|
||||
entry.Issues = append(entry.Issues, fmt.Sprintf("index_drift: disk=%d indexed=%d", diskCount, indexedCount))
|
||||
}
|
||||
|
||||
// CHECK 6: empty_vault
|
||||
if diskCount == 0 {
|
||||
entry.Issues = append(entry.Issues, "empty_vault")
|
||||
}
|
||||
|
||||
entry.setFinalStatus()
|
||||
return entry
|
||||
}
|
||||
|
||||
// setWarningStatus sets status to warning if not already error.
|
||||
func (e *VaultDoctorEntry) setWarningStatus() {
|
||||
if e.Status != "error" {
|
||||
e.Status = "warning"
|
||||
}
|
||||
}
|
||||
|
||||
// setFinalStatus derives the final Status from Issues.
|
||||
func (e *VaultDoctorEntry) setFinalStatus() {
|
||||
if e.Status == "error" {
|
||||
return
|
||||
}
|
||||
if len(e.Issues) == 0 {
|
||||
e.Status = "ok"
|
||||
} else {
|
||||
e.Status = "warning"
|
||||
}
|
||||
}
|
||||
|
||||
// countDiskFiles walks realPath and counts regular files, excluding:
|
||||
// vault_index.db*, .git/, hidden files/dirs at any depth.
|
||||
func countDiskFiles(realPath string) int {
|
||||
count := 0
|
||||
_ = filepath.WalkDir(realPath, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
name := d.Name()
|
||||
// Skip hidden entries
|
||||
if strings.HasPrefix(name, ".") {
|
||||
if d.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Skip .git
|
||||
if d.IsDir() && name == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
// Skip vault_index.db files
|
||||
if !d.IsDir() && (name == "vault_index.db" || name == "vault_index.db-shm" || name == "vault_index.db-wal") {
|
||||
return nil
|
||||
}
|
||||
if !d.IsDir() {
|
||||
count++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return count
|
||||
}
|
||||
|
||||
// isIndexStale returns true if any regular file under realPath has an mtime
|
||||
// strictly after maxTime (excluding vault_index.db* and hidden files).
|
||||
func isIndexStale(realPath string, maxTime time.Time) bool {
|
||||
stale := false
|
||||
_ = filepath.WalkDir(realPath, func(path string, d os.DirEntry, err error) error {
|
||||
if err != nil || stale {
|
||||
return nil
|
||||
}
|
||||
name := d.Name()
|
||||
if strings.HasPrefix(name, ".") {
|
||||
if d.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() && name == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if !d.IsDir() {
|
||||
if name == "vault_index.db" || name == "vault_index.db-shm" || name == "vault_index.db-wal" {
|
||||
return nil
|
||||
}
|
||||
fi, statErr := d.Info()
|
||||
if statErr == nil && fi.ModTime().After(maxTime) {
|
||||
stale = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return stale
|
||||
}
|
||||
|
||||
// hasNonStandardLayout returns true when a vault directory contains
|
||||
// subdirectories that are clearly intentional but not data/knowledge.
|
||||
// Heuristic: any subdir at the vault root that is not data/knowledge.
|
||||
func hasNonStandardLayout(realPath string) bool {
|
||||
entries, err := os.ReadDir(realPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
standardDirs := map[string]bool{"data": true, "knowledge": true, ".git": true}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() && !standardDirs[e.Name()] && !strings.HasPrefix(e.Name(), ".") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user