Files
chemenu/tools/chemenu/tests/test_confidence_decay.py
T
torben 0b8ca746fa
CI / verify (push) Successful in 49s
Release / release (push) Successful in 36s
docs/ als ausgelieferter Hintergrund-Ort; Decision-Seiten bleiben in kb/, Decay-Skip fuer concept_type: decision (4.3.0, #38)
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
2026-09-03 19:01:21 +02:00

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