Audit prospective comparison readiness and retain effort gaps

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1045b-933d-7aa1-b2db-104557487a79
This commit is contained in:
tegwick 2026-10-04 03:02:53 +02:00
parent 280c1d38bf
commit d8d3a22b54
9 changed files with 439 additions and 54 deletions

View file

@ -4,6 +4,7 @@ import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import pytest
@ -23,6 +24,7 @@ def test_current_records_and_report_are_consistent(data):
baseline = json.loads((ROOT / evolution.DATA / 'snapshots/baseline-2026-09-26.json').read_text())
evolution.validate_snapshot(baseline)
assert evolution.render(data, baseline) == (ROOT / evolution.DATA / 'EvolutionReport.md').read_text()
assert evolution.comparison_windows(data) == json.loads((ROOT / evolution.DATA / 'ComparisonWindows.json').read_text())
@pytest.mark.parametrize('mutation,match', [
@ -73,14 +75,76 @@ def test_rates_separate_corroboration_unknown_effort_and_streams(data):
events[3]['stream'] = 'F'
evolution.validate(data)
report = evolution.render(data)
assert '| LEARN-0001 | revision | 30 | 2.00 |' in report
assert '| LEARN-0002 | corroboration | 30 | 0.00 |' in report
assert '| LEARN-0003 | revision | unknown | unknown |' in report
assert '| revision | 30 | 2.00 |' in report
assert '| corroboration | 30 | 0.00 |' in report
assert '| revision | unknown | unknown |' in report
assert '### Stream F / controlled-mechanism' in report
assert 'Fewer than three comparable cycles' in report
assert 'Fewer than three declared cycles' in report
assert 'does not demonstrate a plateau' in report
events[0]['effort']['research_minutes'] = 0
assert '| LEARN-0001 | revision | 0 | unknown |' in evolution.render(data)
assert '| revision | 0 | unknown |' in evolution.render(data)
@pytest.mark.parametrize('minutes', [None, 0])
def test_three_declared_cycles_trigger_review_without_fabricating_effort(data, minutes):
entries = deepcopy(data['ledger']['events'][:3])
for entry in entries:
entry.update(recording='prospective', comparison_group='test-window', stream='B', protocol_ref='templates/LearningCycle.md')
entry['effort']['research_minutes'] = 10
entries[1]['effort']['research_minutes'] = minutes
entries[1]['disposition'] = 'inconclusive'
data['ledger']['events'] = entries
window, = evolution.comparison_windows(data)['windows']
assert window['qualitative_review_due']
assert window['cycles_until_review_trigger'] == 0
assert not window['effort_complete']
assert not window['effort_comparison_ready']
assert window['positive_effort_cycle_ids'] == ['LEARN-0001', 'LEARN-0003']
assert window['unknown_effort_cycle_ids'] == (['LEARN-0002'] if minutes is None else [])
assert window['zero_effort_cycle_ids'] == (['LEARN-0002'] if minutes == 0 else [])
assert window['outcomes']['inconclusive'] == 1
# One more measured cycle cannot silently drop the intervening effort gap.
extra = deepcopy(entries[0])
extra['id'] = 'LEARN-0004'
data['ledger']['events'].append(extra)
assert not evolution.comparison_windows(data)['windows'][0]['effort_comparison_ready']
entries[1]['effort']['research_minutes'] = 10
assert evolution.comparison_windows(data)['windows'][0]['effort_comparison_ready']
assert 'subject to protocol comparability review' in evolution.render(data)
def test_window_audit_retains_order_protocols_and_stream_separation(data):
entries = deepcopy(data['ledger']['events'][:4])
for entry in entries[:3]:
entry.update(recording='prospective', comparison_group='same-label', stream='B', protocol_ref='templates/LearningCycle.md')
entry['effort']['research_minutes'] = 10
entries[1]['protocol_ref'] = 'research/evolution/cycles/2026-09-27-navigation.md'
entries[2]['stream'] = 'F'
entries[3]['comparison_group'] = 'same-label' # Retrospective data stays excluded.
entries[3]['effort']['research_minutes'] = 10
data['ledger']['events'] = list(reversed(entries))
audit = evolution.comparison_windows(data)
b, f = audit['windows']
assert b['cycle_ids'] == ['LEARN-0001', 'LEARN-0002']
assert b['protocol_refs'] == ['templates/LearningCycle.md', 'research/evolution/cycles/2026-09-27-navigation.md']
assert b['cycles_until_review_trigger'] == 1
assert b['effort_complete'] and not b['effort_comparison_ready']
assert f['cycle_ids'] == ['LEARN-0003']
assert not f['qualitative_review_due']
def test_check_rejects_stale_window_audit(tmp_path, monkeypatch, capsys):
for ref in ['EvolutionReport.md', 'ComparisonWindows.json', 'snapshots/baseline-2026-09-26.json']:
path = tmp_path / evolution.DATA / ref
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes((ROOT / evolution.DATA / ref).read_bytes())
(tmp_path / evolution.DATA / 'ComparisonWindows.json').write_text('{}\n')
monkeypatch.setattr(evolution, 'ROOT', tmp_path)
monkeypatch.setattr(sys, 'argv', ['research_evolution.py', 'check'])
with pytest.raises(SystemExit) as exc:
evolution.main()
assert exc.value.code == 1
assert 'Stale comparison windows' in capsys.readouterr().err
def test_snapshot_delta_does_not_count_additions_as_maturity_changes(data):