mirror of
https://github.com/HKUDS/OpenSpace.git
synced 2026-08-28 05:15:00 +00:00
204 lines
6.9 KiB
Python
204 lines
6.9 KiB
Python
import asyncio
|
|
from datetime import datetime
|
|
from types import SimpleNamespace
|
|
|
|
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.engine import EvolutionEngine
|
|
from openspace.skill_engine.types import (
|
|
CaptureContract,
|
|
EvolutionSuggestion,
|
|
EvolutionType,
|
|
ExecutionAnalysis,
|
|
)
|
|
|
|
|
|
def _packet(packet_id: str, trigger_job_id: str = "trg_retry") -> EvidencePacket:
|
|
return EvidencePacket(
|
|
packet_id=packet_id,
|
|
trigger_job_id=trigger_job_id,
|
|
packet_type="analysis",
|
|
profile_name="analysis_current_task",
|
|
subprofile="task_finished",
|
|
manifest_watermark=1,
|
|
scope=EvidenceScope(task_id="task_retry", source_task_ids=("task_retry",)),
|
|
selected_refs={
|
|
"runtime_snapshot": [
|
|
ResourceRef(
|
|
ref_id="runtime:finish",
|
|
ref_type="runtime_snapshot",
|
|
task_id="task_retry",
|
|
preview="Task completed successfully.",
|
|
metadata={"status": "success"},
|
|
)
|
|
],
|
|
"tool_event": [
|
|
ResourceRef(
|
|
ref_id="tool:git",
|
|
ref_type="tool_event",
|
|
task_id="task_retry",
|
|
preview="Verified the reusable workflow.",
|
|
metadata={"tool_name": "bash", "status": "success"},
|
|
),
|
|
ResourceRef(
|
|
ref_id="tool:verify",
|
|
ref_type="tool_event",
|
|
task_id="task_retry",
|
|
preview="Independent verification passed.",
|
|
metadata={"tool_name": "bash", "status": "success"},
|
|
),
|
|
],
|
|
},
|
|
expanded_snippets=[],
|
|
readable_paths=[],
|
|
instructions={},
|
|
budget=PacketBudget(max_chars=1000, used_chars=100),
|
|
redaction_status="ok",
|
|
build_status="ok",
|
|
missing_ref_types=[],
|
|
)
|
|
|
|
|
|
def _analysis(suggestions: list[EvolutionSuggestion]) -> ExecutionAnalysis:
|
|
return ExecutionAnalysis(
|
|
task_id="task_retry",
|
|
timestamp=datetime.now(),
|
|
task_completed=True,
|
|
execution_note="Completed and verified.",
|
|
evolution_suggestions=suggestions,
|
|
)
|
|
|
|
|
|
def test_decision_identity_survives_packet_rebuild_and_suggestion_reordering() -> None:
|
|
first = EvolutionSuggestion(
|
|
evolution_type=EvolutionType.CAPTURED,
|
|
local_category_path="local/workflow/git-recovery",
|
|
direction="Capture the exact dangling-commit recovery workflow.",
|
|
capture_contract=_capture_contract("Recover a dangling commit."),
|
|
)
|
|
second = EvolutionSuggestion(
|
|
evolution_type=EvolutionType.CAPTURED,
|
|
local_category_path="local/tool-guide/read-before-write",
|
|
direction="Capture the exact read-before-write guard workflow.",
|
|
capture_contract=_capture_contract("Read a file before overwriting it."),
|
|
)
|
|
adapter = AnalyzerDecisionAdapter()
|
|
|
|
initial = adapter.from_analysis(
|
|
_analysis([first, second]),
|
|
_packet("pkt_initial"),
|
|
)
|
|
retried = adapter.from_analysis(
|
|
_analysis([second, first]),
|
|
_packet("pkt_retry"),
|
|
)
|
|
|
|
initial_ids = {item.reason_summary: item.decision_id for item in initial}
|
|
retried_ids = {item.reason_summary: item.decision_id for item in retried}
|
|
assert retried_ids == initial_ids
|
|
|
|
|
|
def _capture_contract(capability: str) -> CaptureContract:
|
|
return CaptureContract(
|
|
capability=capability,
|
|
procedure_refs=["tool:git"],
|
|
validation_refs=["tool:verify"],
|
|
validation_summary="A separate verification command passed.",
|
|
)
|
|
|
|
|
|
def test_evidence_store_loads_only_committed_action_for_decision(tmp_path) -> None:
|
|
store = EvidenceStore(tmp_path / "evidence.db")
|
|
try:
|
|
failed = store.begin_action(
|
|
action_id="act_failed",
|
|
decision_id="dec_retry",
|
|
trigger_job_id="trg_retry",
|
|
authoring_id="auth_failed",
|
|
validation_id="val_failed",
|
|
action_type="CAPTURED",
|
|
staging_dir="/tmp/stage-failed",
|
|
active_target_dir="/tmp/failed",
|
|
)
|
|
store.finalize_action(failed.action_id, status="failed")
|
|
assert store.load_committed_action_for_decision("dec_retry") is None
|
|
|
|
committed = store.begin_action(
|
|
action_id="act_committed",
|
|
decision_id="dec_retry",
|
|
trigger_job_id="trg_retry",
|
|
authoring_id="auth_committed",
|
|
validation_id="val_committed",
|
|
action_type="CAPTURED",
|
|
skill_id="git-recovery__v0_test",
|
|
staging_dir="/tmp/stage-committed",
|
|
active_target_dir="/tmp/committed",
|
|
)
|
|
store.finalize_action(
|
|
committed.action_id,
|
|
status="committed",
|
|
skill_id="git-recovery__v0_test",
|
|
)
|
|
|
|
loaded = store.load_committed_action_for_decision("dec_retry")
|
|
assert loaded is not None
|
|
assert loaded.action_id == "act_committed"
|
|
assert loaded.commit_status == "committed"
|
|
finally:
|
|
store.close()
|
|
|
|
|
|
def test_engine_reuses_committed_action_without_reauthoring() -> None:
|
|
decision = SimpleNamespace(
|
|
decision_id="dec_retry",
|
|
trigger_job_id="trg_retry",
|
|
proposed_action="CAPTURED",
|
|
admission={"outcome": "direct"},
|
|
)
|
|
existing_action = SimpleNamespace(
|
|
action_id="act_committed",
|
|
decision_id="dec_retry",
|
|
trigger_job_id="trg_retry",
|
|
commit_status="committed",
|
|
skill_id="git-recovery__v0_test",
|
|
)
|
|
|
|
class FakeEvidenceStore:
|
|
def load_committed_action_for_decision(self, decision_id: str):
|
|
assert decision_id == "dec_retry"
|
|
return existing_action
|
|
|
|
class ExplodingAuthoringBackend:
|
|
called = False
|
|
|
|
async def author_from_action_packet(self, *args, **kwargs):
|
|
self.called = True
|
|
raise AssertionError("committed decisions must not be re-authored")
|
|
|
|
authoring = ExplodingAuthoringBackend()
|
|
engine = EvolutionEngine(
|
|
authoring_backend=authoring,
|
|
committer=SimpleNamespace(
|
|
evidence_store=FakeEvidenceStore(),
|
|
skill_store=SimpleNamespace(load_record=lambda _skill_id: None),
|
|
),
|
|
evolution_mode="autonomous",
|
|
)
|
|
job = SimpleNamespace(
|
|
job_id="trg_retry",
|
|
status="running",
|
|
packet=SimpleNamespace(decisions=[decision]),
|
|
)
|
|
|
|
result = asyncio.run(engine.process_job(job))
|
|
|
|
assert result.status == "completed"
|
|
assert result.actions == [existing_action]
|
|
assert result.errors == []
|
|
assert authoring.called is False
|