mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-08-28 05:15:00 +00:00
241 lines
8.1 KiB
Python
241 lines
8.1 KiB
Python
import json
|
|
import sqlite3
|
|
from datetime import datetime
|
|
from types import SimpleNamespace
|
|
|
|
from openspace.skill_engine.analyzer import ExecutionAnalyzer
|
|
from openspace.skill_engine.decision.analysis_adapter import AnalyzerDecisionAdapter
|
|
from openspace.skill_engine.evidence import EvidenceStore
|
|
from openspace.skill_engine.evidence.types import (
|
|
EvidencePacket,
|
|
EvidenceScope,
|
|
PacketBudget,
|
|
ResourceRef,
|
|
)
|
|
from openspace.skill_engine.evolution.admission import EvolutionAdmission
|
|
from openspace.skill_engine.evolution.authoring import SkillEvolverAuthoringBackend
|
|
from openspace.skill_engine.evolution.engine import _source_validation_passed
|
|
from openspace.skill_engine.evolution.validator import EvolutionValidator
|
|
from openspace.skill_engine.types import (
|
|
CaptureContract,
|
|
EvolutionSuggestion,
|
|
EvolutionType,
|
|
ExecutionAnalysis,
|
|
SkillCategory,
|
|
)
|
|
|
|
|
|
def test_analyzer_parses_capture_contract() -> None:
|
|
parsed = ExecutionAnalyzer._parse_analysis(
|
|
"task_test",
|
|
{
|
|
"task_completed": False,
|
|
"execution_note": "A validated subworkflow completed.",
|
|
"evolution_suggestions": [
|
|
{
|
|
"type": "captured",
|
|
"target_skills": [],
|
|
"category": "workflow",
|
|
"direction": "Capture only extraction.",
|
|
"capture_contract": _contract(),
|
|
}
|
|
],
|
|
},
|
|
{"selected_skills": []},
|
|
)
|
|
|
|
assert parsed is not None
|
|
contract = parsed.evolution_suggestions[0].capture_contract
|
|
assert contract is not None
|
|
assert contract.capability == _contract()["capability"]
|
|
assert contract.validation_refs == ["tool:validation"]
|
|
|
|
|
|
def test_capture_contract_round_trips_with_stored_analysis() -> None:
|
|
suggestion = EvolutionSuggestion(
|
|
evolution_type=EvolutionType.CAPTURED,
|
|
category=SkillCategory.WORKFLOW,
|
|
direction="Capture only extraction.",
|
|
capture_contract=CaptureContract.from_dict(_contract()),
|
|
)
|
|
|
|
restored = EvolutionSuggestion.from_dict(suggestion.to_dict())
|
|
|
|
assert restored.capture_contract is not None
|
|
assert restored.capture_contract.to_dict() == _contract()
|
|
|
|
|
|
def test_contract_and_source_validation_are_durable(tmp_path) -> None:
|
|
store = EvidenceStore(tmp_path / "evidence.db")
|
|
try:
|
|
packet = _packet()
|
|
store.persist_packet(packet)
|
|
decision = AnalyzerDecisionAdapter().from_analysis(
|
|
_analysis(),
|
|
packet,
|
|
source_analysis_id="analysis:test",
|
|
)[0]
|
|
store.persist_decision(decision, packet.packet_id)
|
|
admission = EvolutionAdmission(evidence_store=store).admit(
|
|
decision,
|
|
packet,
|
|
)
|
|
|
|
conn = sqlite3.connect(store.db_path)
|
|
try:
|
|
raw_contract = conn.execute(
|
|
"SELECT proposal_contract_json FROM decision_rationales "
|
|
"WHERE decision_id=?",
|
|
(decision.decision_id,),
|
|
).fetchone()[0]
|
|
source_validated = conn.execute(
|
|
"SELECT source_validation_passed FROM admission_results "
|
|
"WHERE admission_id=?",
|
|
(admission.admission_id,),
|
|
).fetchone()[0]
|
|
finally:
|
|
conn.close()
|
|
|
|
assert json.loads(raw_contract) == _contract()
|
|
assert source_validated == 1
|
|
decision_ref = store.get_ref(f"decision:{decision.decision_id}")
|
|
assert decision_ref is not None
|
|
assert decision_ref.metadata["proposal_contract"] == _contract()
|
|
loaded = store.load_admission(admission.admission_id)
|
|
assert loaded is not None
|
|
assert loaded.source_validation_passed is True
|
|
finally:
|
|
store.close()
|
|
|
|
|
|
def test_capture_authoring_prompt_is_bounded_by_contract() -> None:
|
|
backend = object.__new__(SkillEvolverAuthoringBackend)
|
|
|
|
prompt = backend._build_prompt(
|
|
"CAPTURED",
|
|
[],
|
|
_packet(),
|
|
"Capture only extraction.",
|
|
category=SkillCategory.WORKFLOW,
|
|
proposal_contract=_contract(),
|
|
)
|
|
|
|
assert "Admitted Capture Contract" in prompt
|
|
assert _contract()["capability"] in prompt
|
|
assert "Do not add unsupported algorithms" in prompt
|
|
assert "No evidence covers encrypted archives" in prompt
|
|
|
|
|
|
def test_validator_rejects_contract_mutation_or_missing_provenance() -> None:
|
|
validator = EvolutionValidator()
|
|
decision = SimpleNamespace(proposal_contract=_contract())
|
|
admission = SimpleNamespace(source_validation_passed=True)
|
|
staged = SimpleNamespace(
|
|
apply_metadata={"proposal_contract": _contract()},
|
|
evidence_refs=["tool:procedure", "tool:validation"],
|
|
)
|
|
|
|
assert validator._capture_contract_failures(
|
|
staged,
|
|
"CAPTURED",
|
|
decision,
|
|
admission,
|
|
) == []
|
|
|
|
changed = dict(_contract())
|
|
changed["capability"] = "An unsupported broader capability."
|
|
staged.apply_metadata = {"proposal_contract": changed}
|
|
staged.evidence_refs = ["tool:procedure"]
|
|
failures = validator._capture_contract_failures(
|
|
staged,
|
|
"CAPTURED",
|
|
decision,
|
|
admission,
|
|
)
|
|
|
|
assert "captured_contract_changed_during_authoring" in failures
|
|
assert "captured_contract_refs_missing_from_provenance" in failures
|
|
|
|
|
|
def test_origin_trust_requires_source_validation_admission() -> None:
|
|
assert _source_validation_passed(SimpleNamespace()) is False
|
|
assert _source_validation_passed(
|
|
SimpleNamespace(source_validation_passed=False)
|
|
) is False
|
|
assert _source_validation_passed(
|
|
SimpleNamespace(source_validation_passed=True)
|
|
) is True
|
|
|
|
|
|
def _analysis() -> ExecutionAnalysis:
|
|
return ExecutionAnalysis(
|
|
task_id="task_test",
|
|
timestamp=datetime.now(),
|
|
task_completed=False,
|
|
execution_note="The overall task failed after extraction passed.",
|
|
evolution_suggestions=[
|
|
EvolutionSuggestion(
|
|
evolution_type=EvolutionType.CAPTURED,
|
|
category=SkillCategory.WORKFLOW,
|
|
direction="Capture only extraction.",
|
|
capture_contract=CaptureContract.from_dict(_contract()),
|
|
)
|
|
],
|
|
)
|
|
|
|
|
|
def _contract() -> dict:
|
|
return {
|
|
"capability": "Extract one file and verify its bytes.",
|
|
"preconditions": ["The input archive is readable."],
|
|
"procedure_refs": ["tool:procedure"],
|
|
"validation_refs": ["tool:validation"],
|
|
"validation_summary": "A separate checksum matched the expected bytes.",
|
|
"limitations": ["No evidence covers encrypted archives."],
|
|
}
|
|
|
|
|
|
def _packet() -> EvidencePacket:
|
|
return EvidencePacket(
|
|
packet_id="pkt_test",
|
|
trigger_job_id="trg_test",
|
|
packet_type="analysis",
|
|
profile_name="analysis_current_task",
|
|
subprofile="task_finished",
|
|
manifest_watermark=1,
|
|
scope=EvidenceScope(task_id="task_test", source_task_ids=("task_test",)),
|
|
selected_refs={
|
|
"runtime_snapshot": [
|
|
ResourceRef(
|
|
ref_id="runtime:finish",
|
|
ref_type="runtime_snapshot",
|
|
task_id="task_test",
|
|
preview="Overall task failed after extraction.",
|
|
metadata={"status": "error"},
|
|
)
|
|
],
|
|
"tool_event": [
|
|
ResourceRef(
|
|
ref_id="tool:procedure",
|
|
ref_type="tool_event",
|
|
task_id="task_test",
|
|
preview="Extracted the file.",
|
|
metadata={"status": "success", "tool_name": "bash"},
|
|
),
|
|
ResourceRef(
|
|
ref_id="tool:validation",
|
|
ref_type="tool_event",
|
|
task_id="task_test",
|
|
preview="Checksum matched.",
|
|
metadata={"status": "success", "tool_name": "bash"},
|
|
),
|
|
],
|
|
},
|
|
expanded_snippets=[],
|
|
readable_paths=[],
|
|
instructions={},
|
|
budget=PacketBudget(max_chars=1000, used_chars=100),
|
|
redaction_status="ok",
|
|
build_status="ok",
|
|
missing_ref_types=[],
|
|
)
|