Files
knox/internal/cmd/reconcile.go
T
david 8c054094a1 feat: M3 peer gossip protocol
Refs #1

- peers table (peer_id, addr, cursor, last_handshake)
- watch serves HTTP API: GET /v1/ping (knowledge vector),
  GET /v1/log?node=&after= (cursor-paged pull), POST /v1/obs/batch
- anti-entropy sweep (Run): ping, pull what we lack, push own obs;
  echo suppressed by node_id ownership; reconcile-on-pull
- config via KNOX_PEERS / KNOX_PEER_ADDR; knox gossip status
- db: GossipObservation wire type, PushObservations, ObservationsAfter,
  KnowledgeVector, peer upsert/list
- integration tests: bidirectional convergence + idempotency
2026-08-29 04:53:26 -07:00

54 lines
1.8 KiB
Go

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
}