generated from coulomb/repo-seed
Add Markitect bridge and activation quality
This commit is contained in:
16
tests/fixtures/activation-quality-report.json
vendored
Normal file
16
tests/fixtures/activation-quality-report.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"explanation_coverage": 1.0,
|
||||
"omitted_required_nodes": [
|
||||
"artifact.profile"
|
||||
],
|
||||
"policy_denied_required_nodes": [
|
||||
"artifact.profile"
|
||||
],
|
||||
"provenance_coverage": 0.0,
|
||||
"selected_expected_nodes": [
|
||||
"decision.boundary"
|
||||
],
|
||||
"source_span_coverage": 1.0,
|
||||
"stale_item_activation_count": 0,
|
||||
"token_budget_utilization": 0.45
|
||||
}
|
||||
18
tests/fixtures/markitect-invalid-graph.json
vendored
Normal file
18
tests/fixtures/markitect-invalid-graph.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"schema_version": "markitect.memory.graph.v1",
|
||||
"id": "invalid-graph",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "node.a",
|
||||
"kind": "decision"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "edge.bad",
|
||||
"kind": "depends_on",
|
||||
"source": "node.a",
|
||||
"target": "missing"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
tests/fixtures/markitect-invalid-profile.json
vendored
Normal file
4
tests/fixtures/markitect-invalid-profile.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"schema_version": "markitect.memory.profile.v99",
|
||||
"title": "Invalid Profile Without Id"
|
||||
}
|
||||
8
tests/fixtures/markitect-package-response.json
vendored
Normal file
8
tests/fixtures/markitect-package-response.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"package_id": "package:activation-fixture",
|
||||
"diagnostics": [],
|
||||
"item_count": 2,
|
||||
"metadata": {
|
||||
"compiled_by": "markitect-fixture"
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"event.restart:max_tokens",
|
||||
"risk.durable-write:max_tokens"
|
||||
],
|
||||
"package_request_id": "package-request:854b8a9e0f9a",
|
||||
"package_request_id": "markitect-package-request:60c0ec4c172b",
|
||||
"plan_id": "activation:6e0ba40234cd",
|
||||
"selected_event_ids": [
|
||||
"event.activation"
|
||||
@@ -19,7 +19,7 @@
|
||||
"activation_omitted_items"
|
||||
],
|
||||
"operation": "graph.activation.plan",
|
||||
"operation_id": "op:826d3b06fa5b",
|
||||
"operation_id": "op:85705b61958d",
|
||||
"schema_version": "phase_memory.runtime.envelope.v1",
|
||||
"subject": {
|
||||
"id": "phase-memory-fixture-graph",
|
||||
|
||||
92
tests/test_markitect_bridge.py
Normal file
92
tests/test_markitect_bridge.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from phase_memory.bridge import (
|
||||
MARKITECT_PACKAGE_REQUEST_SCHEMA,
|
||||
MARKITECT_PACKAGE_RESPONSE_SCHEMA,
|
||||
LocalMarkitectValidator,
|
||||
OptionalMarkitectValidator,
|
||||
package_response_envelope,
|
||||
)
|
||||
from phase_memory.contracts import graph_from_markitect
|
||||
from phase_memory.runtime import PhaseMemoryRuntime
|
||||
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def _load(name: str):
|
||||
return json.loads((FIXTURES / name).read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def test_local_markitect_validator_reports_contract_diagnostics() -> None:
|
||||
validator = LocalMarkitectValidator()
|
||||
|
||||
invalid_profile = validator.validate_profile(_load("markitect-invalid-profile.json"))
|
||||
invalid_graph = validator.validate_graph(_load("markitect-invalid-graph.json"))
|
||||
|
||||
assert not invalid_profile.valid
|
||||
assert [diagnostic.code for diagnostic in invalid_profile.diagnostics] == ["unsupported_profile_schema", "missing_profile_id"]
|
||||
assert not invalid_graph.valid
|
||||
assert [diagnostic.code for diagnostic in invalid_graph.diagnostics] == ["missing_edge_target"]
|
||||
|
||||
|
||||
def test_optional_validator_falls_back_to_local_boundary() -> None:
|
||||
validator = OptionalMarkitectValidator()
|
||||
|
||||
result = validator.validate_selection({"schema_version": "markitect.memory.selection.v1", "id": "selection.a"})
|
||||
|
||||
assert result.valid
|
||||
assert result.subject_kind == "memory_selection"
|
||||
|
||||
|
||||
def test_activation_preserves_metadata_for_package_request() -> None:
|
||||
graph_data = _load("memory-graph.json")
|
||||
graph_data["nodes"][0]["confidence"] = 0.95
|
||||
graph_data["nodes"][0]["policy"] = {"labels": ["project-local"], "trust_zone": "local"}
|
||||
graph_data["nodes"][0]["provenance"] = [{"source": "architecture"}]
|
||||
graph = graph_from_markitect(graph_data).value
|
||||
runtime = PhaseMemoryRuntime()
|
||||
|
||||
envelope = runtime.plan_activation(
|
||||
graph.to_dict(),
|
||||
max_items=1,
|
||||
max_tokens=20,
|
||||
profile_id="phase-memory-fixture-profile",
|
||||
priority_node_ids=("decision.boundary",),
|
||||
)
|
||||
|
||||
request = envelope["data"]["package_request"]
|
||||
item = request["provenance"]["selected_items"]["decision.boundary"]
|
||||
assert request["schema_version"] == MARKITECT_PACKAGE_REQUEST_SCHEMA
|
||||
assert request["selection_id"] == envelope["data"]["activation_plan"]["plan_id"]
|
||||
assert request["graph_id"] == "phase-memory-fixture-graph"
|
||||
assert request["profile_id"] == "phase-memory-fixture-profile"
|
||||
assert request["selected_nodes"] == ["decision.boundary"]
|
||||
assert item["source_spans"] == [{"path": "docs/architecture.md", "line_start": 1}]
|
||||
assert item["provenance"] == [{"source": "architecture"}]
|
||||
assert item["confidence"] == 0.95
|
||||
assert item["policy"]["labels"] == ["project-local"]
|
||||
assert item["reason_selected"] == "priority"
|
||||
|
||||
|
||||
def test_package_response_envelope_keeps_markitect_response_opaque() -> None:
|
||||
response = _load("markitect-package-response.json")
|
||||
|
||||
envelope = package_response_envelope(response, request_id="request.a")
|
||||
|
||||
assert envelope["schema_version"] == MARKITECT_PACKAGE_RESPONSE_SCHEMA
|
||||
assert envelope["request_id"] == "request.a"
|
||||
assert envelope["package_ref"] == "package:activation-fixture"
|
||||
assert envelope["response"] == response
|
||||
|
||||
|
||||
def test_runtime_compile_package_uses_bridge_response_envelope() -> None:
|
||||
runtime = PhaseMemoryRuntime()
|
||||
selection = {"schema_version": "markitect.memory.selection.v1", "id": "selection.a", "nodes": ["node.a"], "events": []}
|
||||
|
||||
envelope = runtime.compile_package(selection)
|
||||
|
||||
assert envelope["data"]["package_request"]["schema_version"] == MARKITECT_PACKAGE_REQUEST_SCHEMA
|
||||
assert envelope["data"]["package_response"]["schema_version"] == MARKITECT_PACKAGE_RESPONSE_SCHEMA
|
||||
assert envelope["data"]["package_response"]["package_ref"] == "package:selection.a"
|
||||
95
tests/test_retrieval_quality.py
Normal file
95
tests/test_retrieval_quality.py
Normal file
@@ -0,0 +1,95 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from phase_memory.contracts import graph_from_markitect
|
||||
from phase_memory.models import MemoryEvent, MemoryPath, MemoryPathState
|
||||
from phase_memory.retrieval import (
|
||||
WordCountTokenEstimator,
|
||||
activation_quality_report,
|
||||
plan_neighborhood_activation,
|
||||
retrieve_graph_neighborhood,
|
||||
select_event_path,
|
||||
)
|
||||
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def _graph():
|
||||
return graph_from_markitect(json.loads((FIXTURES / "memory-graph.json").read_text(encoding="utf-8"))).value
|
||||
|
||||
|
||||
def test_retrieve_graph_neighborhood_is_stable_and_filterable() -> None:
|
||||
candidates = retrieve_graph_neighborhood(
|
||||
_graph(),
|
||||
seed_node_ids=("decision.boundary",),
|
||||
max_hops=1,
|
||||
edge_kinds=("governs",),
|
||||
direction="out",
|
||||
)
|
||||
|
||||
assert [candidate.node_id for candidate in candidates] == ["decision.boundary", "artifact.profile"]
|
||||
assert candidates[0].reasons[:2] == ("graph_distance:0", "explicit_priority")
|
||||
|
||||
|
||||
def test_event_path_selection_respects_path_state_and_budget() -> None:
|
||||
events = (
|
||||
MemoryEvent("event.a", "user_turn"),
|
||||
MemoryEvent("event.b", "agent_turn"),
|
||||
MemoryEvent("event.c", "tool_call"),
|
||||
)
|
||||
active = MemoryPath("path.active", event_ids=("event.a", "event.b", "event.c"))
|
||||
abandoned = MemoryPath("path.abandoned", event_ids=("event.a",), state=MemoryPathState.ABANDONED)
|
||||
|
||||
assert select_event_path(events, active, max_events=2) == ("event.a", "event.b")
|
||||
assert select_event_path(events, abandoned, max_events=2) == ()
|
||||
assert select_event_path(events, abandoned, max_events=2, include_inactive=True) == ("event.a",)
|
||||
|
||||
|
||||
def test_neighborhood_activation_uses_retrieval_order_and_estimator_label() -> None:
|
||||
plan, candidates = plan_neighborhood_activation(
|
||||
_graph(),
|
||||
seed_node_ids=("decision.boundary",),
|
||||
max_hops=1,
|
||||
max_items=2,
|
||||
max_tokens=40,
|
||||
profile_id="phase-memory-fixture-profile",
|
||||
)
|
||||
|
||||
assert [candidate.node_id for candidate in candidates][:2] == ["decision.boundary", "artifact.profile"]
|
||||
assert plan.selected_node_ids[:2] == ("decision.boundary", "artifact.profile")
|
||||
assert plan.selection["metadata"]["estimator"] == "WordCountTokenEstimator"
|
||||
|
||||
|
||||
def test_token_estimator_accounts_for_nodes_and_events() -> None:
|
||||
graph = _graph()
|
||||
estimator = WordCountTokenEstimator()
|
||||
|
||||
assert estimator.estimate_node(graph.nodes[0]) == 9
|
||||
assert estimator.estimate_event(graph.events[0]) >= 2
|
||||
|
||||
|
||||
def test_activation_quality_report_is_deterministic() -> None:
|
||||
plan, _ = plan_neighborhood_activation(
|
||||
_graph(),
|
||||
seed_node_ids=("decision.boundary",),
|
||||
max_hops=1,
|
||||
max_items=1,
|
||||
max_tokens=20,
|
||||
profile_id="phase-memory-fixture-profile",
|
||||
)
|
||||
|
||||
report = activation_quality_report(
|
||||
plan,
|
||||
expected_node_ids=("decision.boundary", "artifact.profile"),
|
||||
policy_denied_node_ids=("artifact.profile",),
|
||||
)
|
||||
|
||||
expected = json.loads((FIXTURES / "activation-quality-report.json").read_text(encoding="utf-8"))
|
||||
assert report["selected_expected_nodes"] == ["decision.boundary"]
|
||||
assert report["omitted_required_nodes"] == ["artifact.profile"]
|
||||
assert report["policy_denied_required_nodes"] == ["artifact.profile"]
|
||||
assert report["token_budget_utilization"] == 0.45
|
||||
assert report["source_span_coverage"] == 1.0
|
||||
assert report["explanation_coverage"] == 1.0
|
||||
assert report == expected
|
||||
@@ -79,4 +79,5 @@ def test_runtime_compile_package_wraps_compiler_response() -> None:
|
||||
|
||||
assert envelope["operation"] == "package.compile"
|
||||
assert envelope["data"]["package_request"]["selection"] == selection
|
||||
assert envelope["data"]["package_response"]["package_id"] == "package:selection.a"
|
||||
assert envelope["data"]["package_response"]["package_ref"] == "package:selection.a"
|
||||
assert envelope["data"]["package_response"]["response"]["package_id"] == "package:selection.a"
|
||||
|
||||
Reference in New Issue
Block a user