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:
parent
280c1d38bf
commit
d8d3a22b54
9 changed files with 439 additions and 54 deletions
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue