- testing_mode.py (19L/M/N/O/P): TestingSession (coordination-only, pausable — durable truth stays in the Behaviour Ledger); dynamic queue (fix-for-B-reopens-A via structural closure, 19S-9 proven); Observation→closure gate (closure requires retest AND user confirmation — automated green alone recorded as NOT closed); forge projection through the T18 boundary (failure never rewrites truth, 19S-14 proven); cross-project aggregate. - 19R LIVE_PROOF: /testing command implemented through the full system — self-scaffold lookup → capability-ledger packet (bound exactly ONE file) → qualified INWARD basis → predictive impact → preview → contract 19r-testing-command → event-bridged verification → descriptive impact (prediction matched, delta persisted) → selective invalidation + obligations → ledgers updated → closed. - 19T benchmark (honest): cold agent found the answer in 6 tool calls (4 files) — on this well-organized repo raw speed ≈ equal; the scaffold advantage is correctness/continuity (validation state, obligations, invalidation), recorded without inflation. - 19U: V1 definition gains the T19 layer model + both governing objectives verbatim; implementation audit gains T19 classifications. - type ratchet: forge.py gitea_auth import made resolvable via pyrightconfig extraPaths (the +1 warning FIXED, not re-baselined). Suite 2002 green; ratchets PASS; Henxels rc=0.
134 lines
5.6 KiB
Python
134 lines
5.6 KiB
Python
"""test_testing_mode.py — T19 §19L/19M/19N/19O/19P held-outs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(REPO / "src"))
|
|
|
|
from rune import behaviour_ledger as bl # noqa: E402
|
|
from rune import forge # noqa: E402
|
|
from rune import testing_mode as tm # noqa: E402
|
|
|
|
|
|
@pytest.fixture()
|
|
def env(tmp_path):
|
|
beh = bl.BehaviourLedger(tmp_path)
|
|
beh.define(behaviour_id="T-1", owning_project="p",
|
|
intended_behaviour="first behaviour",
|
|
procedure="run the thing; expect output X",
|
|
pass_criteria="output == X", fail_criteria="output != X",
|
|
owner_component="component:shared",
|
|
dependencies=["component:shared"])
|
|
beh.define(behaviour_id="T-2", owning_project="p",
|
|
intended_behaviour="second behaviour")
|
|
beh.save()
|
|
return beh
|
|
|
|
|
|
def test_19n_session_is_coordination_not_authority(tmp_path, env):
|
|
s = tm.TestingSession(tmp_path, project="p")
|
|
item = s.next_item(env)
|
|
assert item["behaviour_id"] == "behaviour:T-1" # FAIL items none yet;
|
|
# UNVALIDATED sorts after — both outstanding, T-1 first alphabetically
|
|
s.record(env, "T-1", passed=True, detail="checked by hand")
|
|
assert env.get("T-1")["state"] == bl.USER_CONFIRMED
|
|
s.pause()
|
|
assert s.next_item(env) is None # paused
|
|
s.resume()
|
|
assert s.next_item(env)["behaviour_id"] == "behaviour:T-2"
|
|
# session file holds coordination, NOT spec truth
|
|
import json
|
|
sess = json.loads((tmp_path / "testing_session.json").read_text())
|
|
assert "intended_behaviour" not in str(sess)
|
|
# durable truth intact in the ledger
|
|
assert env.get("T-1")["intended_behaviour"] == "first behaviour"
|
|
|
|
|
|
def test_19m_fix_reopens_dependent_confirmation(tmp_path, env):
|
|
"""Test-B-fix-invalidates-Test-A (19S-9): A confirmed; B fails; the
|
|
B fix touches a dependency shared with A → A re-enters the queue."""
|
|
env.record_auto_verification("T-1", passed=True, generation="g1")
|
|
env.record_real_world("T-1", passed=True, generation="g1") # A hardened
|
|
# B fails in the field
|
|
env.record_real_world("T-2", passed=False, detail="broke")
|
|
assert env.get("T-2")["state"] == bl.TEST_FAILED
|
|
# the fix structurally touches A's dependency
|
|
invalidated = env.invalidate_for_change(
|
|
changed_entities={"component:shared"}, generation="g2",
|
|
reasons={"behaviour:T-1": "fix for T-2 rewrote shared component"})
|
|
assert [r["behaviour_id"] for r in invalidated] == ["behaviour:T-1"]
|
|
assert env.get("T-1")["state"] == bl.RETEST_REQUIRED
|
|
# dynamic queue now contains BOTH
|
|
ids = [b["behaviour_id"] for b in env.outstanding()]
|
|
assert "behaviour:T-1" in ids and "behaviour:T-2" in ids
|
|
# A's generation-1 confirmation remains queryable
|
|
hist = env.retest_history("T-1")
|
|
assert any(h["generation"] == "g1" and h["event"].endswith(
|
|
"USER_CONFIRMED") for h in hist)
|
|
|
|
|
|
def test_19s10_no_blanket_invalidation(tmp_path, env):
|
|
env.record_real_world("T-1", passed=True)
|
|
env.record_real_world("T-2", passed=True)
|
|
# a change to an unrelated entity invalidates NOTHING
|
|
invalidated = env.invalidate_for_change(
|
|
changed_entities={"component:unrelated-thing"}, generation="g")
|
|
assert invalidated == []
|
|
assert env.get("T-1")["state"] == bl.USER_CONFIRMED
|
|
assert env.get("T-2")["state"] == bl.USER_CONFIRMED
|
|
|
|
|
|
def test_19o_observation_to_closure(tmp_path):
|
|
obs = tm.record_observation(
|
|
tmp_path, observed="button does nothing",
|
|
expected="button saves the form",
|
|
behaviour_id="behaviour:T-1", environment="production",
|
|
reproduction="click save with empty field",
|
|
candidate_areas=["component:form-handler"]) # hypothesis only
|
|
assert obs["candidate_areas"] == ["component:form-handler"]
|
|
# premature closure (no user confirm) is recorded as NOT closed
|
|
r = tm.close_observation(tmp_path, obs["observation_id"],
|
|
behaviour_retest_passed=True,
|
|
user_confirmed=False)
|
|
assert r["closure"]["closed"] is False # automated green ≠ closure
|
|
# full closure requires retest AND user confirmation
|
|
r2 = tm.close_observation(tmp_path, obs["observation_id"],
|
|
behaviour_retest_passed=True,
|
|
user_confirmed=True)
|
|
assert r2["closure"]["closed"] is True
|
|
|
|
|
|
def test_19p_forge_projection_never_rewrites_truth(tmp_path, env):
|
|
"""19S-14: forge unavailable → projection fails loudly; ledger truth
|
|
unchanged."""
|
|
obs = tm.record_observation(tmp_path, observed="x", expected="y",
|
|
behaviour_id="behaviour:T-1")
|
|
before = env.get("T-1")["state"]
|
|
bad = forge.GiteaForge(server="https://invalid.invalid")
|
|
with pytest.raises(forge.ForgeUnavailable):
|
|
tm.project_failure_to_forge(bad, "conductor/rune",
|
|
behaviour_id="T-1",
|
|
observation=obs)
|
|
assert env.get("T-1")["state"] == before # truth untouched
|
|
|
|
|
|
def test_19l_aggregate_across_projects(tmp_path):
|
|
reg = tm.ProjectRegistry(tmp_path)
|
|
p1 = tmp_path / "p1"
|
|
p1.mkdir()
|
|
beh = bl.BehaviourLedger(p1)
|
|
beh.define(behaviour_id="X-1", owning_project="p1",
|
|
intended_behaviour="x")
|
|
beh.save()
|
|
reg.register(project_id="p1", scaffold_location=str(p1))
|
|
reg.register(project_id="p2", scaffold_location=str(tmp_path / "p2"))
|
|
rows = tm.aggregate(reg, tmp_path)
|
|
by_p = {r["project"]: r for r in rows}
|
|
assert by_p["p1"]["outstanding"] == 1
|
|
assert by_p["p2"]["outstanding"] is None # no ledger yet
|