feat: M1 determinism fixes for gossip

Refs #1

- F1: canonical fingerprints (git identity by remote URL, log by
  basename, obsidian by relative vault path) so identical facts get
  identical ids across machines
- F2: node_id (persisted in settings) + Hybrid Logical Clock in all
  observation "when" columns; removed time.Now() fact-time fallbacks
- F3: stable TF-IDF tie-break sort (score desc, term asc)
- F4: observations carry (node_id, hcl) locator; dedup ordered by hcl
This commit is contained in:
2026-08-29 03:51:02 -07:00
parent f5766aa6d8
commit 2c0f8fa257
9 changed files with 162 additions and 30 deletions
+12 -12
View File
@@ -8,7 +8,6 @@ import (
"regexp"
"sort"
"strings"
"time"
)
// GitIngester tracks project status for local git repositories.
@@ -141,7 +140,7 @@ type GitStatus struct {
func (g *GitIngester) repoToResult(repoPath string) *IngestResult {
status := gitStatus(repoPath)
name := filepath.Base(repoPath)
fp := Fingerprint([]byte("git:repo:" + repoPath))
fp := Fingerprint([]byte("git:repo:" + gitIdentity(repoPath, status.Branch)))
clean := "clean"
if status.Dirty > 0 {
@@ -157,9 +156,6 @@ func (g *GitIngester) repoToResult(repoPath string) *IngestResult {
}
createdAt := status.LastCommit
if createdAt == "" {
createdAt = gitStatusTime(status)
}
return &IngestResult{
Fingerprint: fp,
@@ -186,13 +182,6 @@ func (g *GitIngester) repoToResult(repoPath string) *IngestResult {
}
}
func gitStatusTime(s GitStatus) string {
if s.LastCommit != "" {
return s.LastCommit
}
return time.Now().UTC().Format(time.RFC3339)
}
func gitStatus(repoPath string) GitStatus {
out, err := gitOut(repoPath, "status", "--porcelain")
if err != nil {
@@ -257,6 +246,17 @@ func gitOut(repoPath string, args ...string) (string, error) {
return sanitizeGitField(string(out)), nil
}
// gitIdentity returns a machine-independent identity for a repo: the remote
// origin URL when configured (works across clones/machines), else the repo
// directory name. The branch is appended so entries are per-branch like the
// underlying gitStatus.
func gitIdentity(repoPath, branch string) string {
if out, err := gitOut(repoPath, "remote", "get-url", "origin"); err == nil && out != "" {
return out + ":" + branch
}
return filepath.Base(repoPath) + ":" + branch
}
func (g *GitIngester) Ingest(_ string) (*IngestResult, error) {
return nil, fmt.Errorf("use IngestAll() for git")
}