ouroboros/tests/test_loop_skill_finalization.py
Ouroboros f0d8b147d4 v7next F1: domain D01 pick + wave-5 seam - quiet F1 COMPLETE
(cherry picked from f1-d01 commit 1039baed and squashed with the seam so the
manifest is self-consistent: the pick-time --theirs manifest predated the
D10/D11 hunks, and a follow-up regen would have left an inconsistent
intermediate commit - the 7d2dca49 class; recipe fixed to regen BEFORE
cherry-pick --continue.)

D01: loop.py 7003->865 + nine leaves, agent_dispatch + post_task_synthesis,
177 spans proof-green, zero live v7 deltas in the loop split (55 falsified
spans are all upstream drift), maximal declared sets per the D10 lesson,
HOT_CODE_PATHS closed over the nine loop leaves.

Seam: D11 six server leaves and D10 nine git leaves mapped (D01 mapped its
own eleven in-lane); server.py, tools/git.py, supervisor/git_ops.py split
rows retired - the remaining 21 split_pending rows are exactly the F2
hot-organ set. size_ratchet manifest regenerated by the official tool on the
merged tree. test_module_handle_extraction LEAVES = union of D10 and D01 row
sets (restored the git_ops_updates closing bracket the conflict marker had
split; 78 passed). Quotient report regenerated: 1426 strict module edges.
2026-08-31 00:20:25 +00:00

75 lines
2.9 KiB
Python

"""The self-authored skill finalization gate of ``ouroboros.loop``.
Split out of ``tests/test_loop_misc.py`` when that module was divided by
theme; every moved block is verbatim.
"""
from __future__ import annotations
import json
from ouroboros.loop_nudges import _skill_finalization_message, _skill_names_touched_by_trace
from ouroboros.skill_loader import (
SkillReviewState,
compute_content_hash,
save_enabled,
save_review_state,
)
# ---------------------------------------------------------------------------
# Skill finalization gate (self-authored skills must reach ready+enabled
# before the loop accepts a final text response)
# ---------------------------------------------------------------------------
def _write_self_authored_skill(drive_root, name: str = "alpha"):
skill_dir = drive_root / "skills" / "external" / name
state_dir = drive_root / "state" / "skills" / name
skill_dir.mkdir(parents=True)
state_dir.mkdir(parents=True)
(skill_dir / "SKILL.md").write_text(
"---\nname: alpha\ntype: instruction\nversion: 0.1.0\n---\nbody\n",
encoding="utf-8",
)
marker = {
"schema_version": 1,
"origin": "self_authored",
"task_id": "task-1",
"created_at": "2026-05-07T00:00:00+00:00",
}
(skill_dir / ".self_authored.json").write_text(json.dumps(marker), encoding="utf-8")
(state_dir / "self_authored.json").write_text(json.dumps(marker), encoding="utf-8")
return skill_dir
def test_skill_names_touched_by_trace_detects_data_skill_edits():
trace = {
"tool_calls": [
{"tool": "write_file", "args": {"path": "skills/external/alpha/plugin.py"}},
{"tool": "edit_text", "args": {"path": "data/skills/external/beta/SKILL.md"}},
{"tool": "write_file", "args": {"path": "SKILL.md", "bucket": "external", "skill_name": "delta"}},
]
}
assert _skill_names_touched_by_trace(trace) == ["alpha", "beta", "delta"]
def test_skill_finalization_message_blocks_unreviewed_self_authored_skill(tmp_path):
drive_root = tmp_path / "drive"
drive_root.mkdir()
_write_self_authored_skill(drive_root)
trace = {"tool_calls": [{"tool": "write_file", "args": {"path": "skills/external/alpha/SKILL.md"}}]}
message = _skill_finalization_message(drive_root, trace)
assert "SKILL_NOT_FINALIZED" in message
assert "alpha" in message
def test_skill_finalization_message_allows_ready_self_authored_skill(tmp_path):
drive_root = tmp_path / "drive"
drive_root.mkdir()
skill_dir = _write_self_authored_skill(drive_root)
content_hash = compute_content_hash(skill_dir)
save_review_state(drive_root, "alpha", SkillReviewState(status="pass", content_hash=content_hash))
save_enabled(drive_root, "alpha", True)
trace = {"tool_calls": [{"tool": "write_file", "args": {"path": "skills/external/alpha/SKILL.md"}}]}
assert _skill_finalization_message(drive_root, trace) == ""