Files
knox/internal/ingest/obsidian_file.go
T
david 2c0f8fa257 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
2026-08-29 03:51:02 -07:00

70 lines
1.6 KiB
Go

package ingest
import (
"os"
"path/filepath"
"strings"
"time"
)
var _ Ingester = (*ObsidianFileIngester)(nil)
type ObsidianFileIngester struct {
Version string
Vault string
}
func NewObsidianFileIngester(vault string) *ObsidianFileIngester {
return &ObsidianFileIngester{Version: "obsidian-file/v1", Vault: vault}
}
func (o *ObsidianFileIngester) SourceID() string { return "obsidian" }
func (o *ObsidianFileIngester) Ingest(path string) (*IngestResult, error) {
if !strings.HasSuffix(path, ".md") {
return nil, nil
}
data, err := os.ReadFile(path)
if err != nil || len(data) < 10 {
return nil, nil
}
content := string(data)
title := extractObsidianTitle(content, path, o.Vault)
tags := extractObsidianTags(content)
created, _ := extractObsidianTimestamps(content, path)
if created == "" {
if fi, err := os.Stat(path); err == nil {
created = fi.ModTime().UTC().Format(time.RFC3339)
}
}
relPath, _ := filepath.Rel(o.Vault, path)
fp := FingerprintWithMeta(data, map[string]string{
"source": "obsidian",
"path": relPath,
})
summary := extractBodyPreview(content, 200)
return &IngestResult{
Fingerprint: fp,
SourceID: o.SourceID(),
SourcePath: relPath,
Project: filepath.Base(o.Vault),
ContentType: ".md",
Title: title,
Summary: summary,
CreatedAt: created,
LineEnd: len(strings.Split(content, "\n")),
Confidence: 0.85,
IngesterVersion: o.Version,
Provenance: map[string]any{
"path": relPath,
"tags": tags,
"note_title": title,
},
}, nil
}