Skip to content

ROT Scoring

Track A2 scoring: content deduplication (via dense embeddings), semantic contradiction detection, freshness monitoring, link rot detection, and usage tracking.

Class / Function Description
ContentDedupDetector Dense embedding cosine similarity for body content
ContradictionDetector Semantic contradiction check for similar notes (LLM/Metadata)
FreshnessScorer Type-based exponential decay freshness scoring
LinkRotChecker HTTP HEAD checks for external URL health
UsageTracker SQLite-based access counter (thread-safe)

ContentDedupDetector

detector = ContentDedupDetector(threshold=0.75)
pairs: list[tuple[str, str, float]] = detector.detect(vault_dir)
  • Uses dense embedding cosine similarity via the embedding manager (canonical BAAI/bge-m3 served through direct ONNX Runtime)
  • Threshold defaults to 0.75 — pairs below threshold are not reported
  • Skips notes with body length less than 50 characters
  • Returns sorted list of (path_a, path_b, similarity_score)

ContradictionDetector

detector = ContradictionDetector(similarity_threshold=0.7, api_key=None)
contradicting: list[tuple[str, str, str]] = detector.detect(vault_dir)
  • Find semantically similar note pairs that may contain contradictory facts, instructions, or statements
  • Identifies similar notes using dense embedding similarity (threshold: 0.7)
  • Checks for factual contradictions using a Local LLM (OpenCode) or OpenRouter API. If no API key or local model is configured, falls back to comparing metadata fields (status, owner, expiry, priority)
  • Returns a list of tuples: (path_a, path_b, reason_for_contradiction)

FreshnessScorer

scorer = FreshnessScorer()
scores: dict[str, float] = scorer.score_all(vault_dir)
  • Scores each note 0.0 (stale) to 1.0 (fresh)
  • Uses exponential decay: score = 2^(-age_days / half_life_days)
  • Half-life depends on note type:
Type Half-life
Daily Log 30 days
Project 180 days
Area 365 days
Resource 365 days
System Guide 365 days
Archive 730 days

LinkRotChecker

checker = LinkRotChecker(timeout=5)
broken: dict[str, list[tuple[str, int]]] = checker.check_all(vault_dir)
  • Performs HTTP HEAD requests on external markdown links
  • Returns dict mapping rel_path[(url, status_code)] for broken links only (status >= 400 or connection error)
  • Connection error returns status -1

UsageTracker

tracker = UsageTracker(vault_dir)
tracker.track_access("01_Projects/note.md")
count = tracker.get_count("01_Projects/note.md")
all_counts: dict[str, int] = tracker.get_all_counts()
  • Thread-safe SQLite storage (.power_usage.db in vault root)
  • track_access() upserts with increment
  • get_all_counts() returns dict of rel_path → access_count