package cmd import ( "fmt" "github.com/spf13/cobra" "github.com/david/knox/internal/db" "github.com/david/knox/internal/watch" ) // NewReconcileCmd rebuilds all derived state (entries cache, threads) from the // append-only observation log. With --dry-run it reports what would change // without writing. func NewReconcileCmd(kdb *db.KnoxDB) *cobra.Command { var dryRun bool cmd := &cobra.Command{ Use: "reconcile", Short: "Rebuild entries and threads from the observation log", Long: `Reconcile makes derived state converge on the append-only observation log. Entries are dropped and rebuilt via deterministic SQL aggregation; the auto-threader then re-links/create threads idempotently by cluster_key. After a gossip pull, reconcile brings a node's materialized view in line with whatever observations it now holds.`, RunE: func(c *cobra.Command, args []string) error { if dryRun { return dryRunReconcile(kdb) } created, linked, err := watch.Reconcile(kdb) if err != nil { return err } fmt.Printf("reconciled: entries rebuilt, %d threads created, %d observations linked\n", created, linked) return nil }, } cmd.Flags().BoolVar(&dryRun, "dry-run", false, "report drift without writing") return cmd } func dryRunReconcile(kdb *db.KnoxDB) error { // Ground truth from the log (computed in a throwaway way via a count of // what rebuild would produce) vs the current materialized cache. current, _ := kdb.EntryCount() logCount := kdb.ObservationEntryEstimate() threader := watch.NewAutoThreader(kdb) threader.DryRun = true created, linked, _ := threader.AutoThread() fmt.Printf("drift: %d entries current, %d from log (%+d), %d threads would be created, %d obs would be linked\n", current, logCount, logCount-current, created, linked) return nil }