0b8ca746fa
Files changed: - AGENTS.md - CHANGES.md - VERSION - instructions/kb-profiles.md - kb/CONVENTIONS.md - kb/concepts/COLLECTION.md - tools/CONTRACT.md - tools/chemenu/commands/confidence_decay.py - tools/chemenu/commands/dist_cmd.py - tools/chemenu/tests/test_confidence_decay.py - tools/chemenu/tests/test_dist_cmd.py
90 lines
3.5 KiB
Python
90 lines
3.5 KiB
Python
import datetime
|
|
|
|
import pytest
|
|
|
|
from chemenu import config
|
|
from chemenu.commands import confidence_decay
|
|
from chemenu.commands.confidence_decay import FLOOR, compute_decay
|
|
from chemenu.frontmatter_io import read_page, write_page
|
|
|
|
|
|
def test_no_decay_at_zero_months():
|
|
today = datetime.date(2026, 8, 2)
|
|
assert compute_decay(0.9, today, today) == 0.9
|
|
|
|
|
|
def test_decay_after_ten_months():
|
|
today = datetime.date(2026, 8, 2)
|
|
last_confirmed = datetime.date(2025, 10, 2) # ~10 months earlier
|
|
result = compute_decay(0.9, last_confirmed, today)
|
|
# 0.9 * (1 - 0.01 * ~10) ~= 0.9 * 0.90 = 0.81
|
|
assert 0.80 <= result <= 0.82
|
|
|
|
|
|
def test_decay_floors_at_0_2():
|
|
today = datetime.date(2026, 8, 2)
|
|
long_ago = datetime.date(2015, 1, 1)
|
|
result = compute_decay(0.5, long_ago, today)
|
|
assert result == FLOOR
|
|
|
|
|
|
@pytest.fixture
|
|
def decay_wiki(kb_dir, monkeypatch):
|
|
monkeypatch.setattr(config, "KB_DIR", kb_dir)
|
|
return kb_dir
|
|
|
|
|
|
def test_init_base_backfills_from_current_confidence(decay_wiki):
|
|
confidence_decay.confidence_init_base(apply=True)
|
|
frontmatter, _ = read_page(decay_wiki / "entities/systems/aurora.md")
|
|
assert frontmatter["confidence_base"] == 0.9
|
|
# Written next to `confidence`, keeping the schema's field order.
|
|
keys = list(frontmatter)
|
|
assert keys.index("confidence_base") == keys.index("confidence") + 1
|
|
|
|
|
|
def test_init_base_is_idempotent(decay_wiki):
|
|
confidence_decay.confidence_init_base(apply=True)
|
|
before = (decay_wiki / "entities/systems/aurora.md").read_text(encoding="utf-8")
|
|
confidence_decay.confidence_init_base(apply=True)
|
|
assert (decay_wiki / "entities/systems/aurora.md").read_text(encoding="utf-8") == before
|
|
|
|
|
|
def test_decay_is_idempotent_across_runs(decay_wiki):
|
|
"""The regression that motivated `confidence_base`: decaying the stored
|
|
`confidence` in place compounded on every run, because the elapsed-months
|
|
factor kept growing while the multiplicand had already shrunk."""
|
|
confidence_decay.confidence_init_base(apply=True)
|
|
confidence_decay.confidence_decay(apply=True)
|
|
after_first = (decay_wiki / "entities/systems/aurora.md").read_text(encoding="utf-8")
|
|
confidence_decay.confidence_decay(apply=True)
|
|
confidence_decay.confidence_decay(apply=True)
|
|
assert (decay_wiki / "entities/systems/aurora.md").read_text(encoding="utf-8") == after_first
|
|
|
|
|
|
def test_decay_skips_pages_without_a_base(decay_wiki):
|
|
"""Falling back to the stored `confidence` would silently reintroduce the
|
|
compounding bug, so pages without a base are skipped instead."""
|
|
confidence_decay.confidence_decay(apply=True)
|
|
frontmatter, _ = read_page(decay_wiki / "entities/systems/aurora.md")
|
|
assert frontmatter["confidence"] == 0.9
|
|
|
|
|
|
def test_decay_skips_decision_pages(decay_wiki):
|
|
"""A decision is not falsified by elapsed time, only by a later decision
|
|
superseding it - `concept_type: decision` is a categorical skip, not
|
|
something an old `modified` date should ever decay (Gitea #38)."""
|
|
decision_path = decay_wiki / "concepts" / "some-decision.md"
|
|
write_page(
|
|
decision_path,
|
|
{
|
|
"type": "types/concept.md", "concept_type": "decision",
|
|
"tags": [], "created": "2015-01-01", "modified": "2015-01-01",
|
|
"related": [], "sources": [], "confidence": 0.9, "confidence_base": 0.9,
|
|
},
|
|
"\n# some-decision\n",
|
|
)
|
|
confidence_decay.confidence_decay(apply=True)
|
|
frontmatter, _ = read_page(decision_path)
|
|
assert frontmatter["confidence"] == 0.9
|