Files
MetaCrate/tools/check_milestone_10_issue_80.py
Chili Palmer 30a0ac1f16
Some checks failed
CI / required (push) Failing after 3m35s
Consolidate required CI gate (#115)
2026-08-12 17:49:47 +00:00

149 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""Audit issue 80's native RLV callback and service orchestration boundary."""
from __future__ import annotations
import json
import re
from pathlib import Path
import generate_api_shims
import generate_surface
ROOT = Path(__file__).resolve().parents[1]
SOURCE = ROOT / "crates" / "libremetaverse-rlv" / "src" / "service.rs"
STATE = ROOT / "crates" / "libremetaverse-rlv" / "src" / "state.rs"
GENERATED = ROOT / "crates" / "libremetaverse-rlv" / "src" / "generated.rs"
SUPPORT = ROOT / "tests" / "compat" / "src" / "rlv_support.rs"
QUERY_TESTS = ROOT / "tests" / "compat" / "tests" / "rlv_query_basics_semantics.rs"
ACTION_TESTS = ROOT / "tests" / "compat" / "tests" / "rlv_attach_commands_semantics.rs"
NOTIFY_TESTS = ROOT / "tests" / "compat" / "tests" / "rlv_notify_semantics.rs"
DOC = ROOT / "crates" / "libremetaverse-rlv" / "README.md"
WORKFLOW = ROOT / ".gitea" / "workflows" / "ci.yml"
CATALOG = ROOT / "api" / "public-api.json"
STUB_RE = re.compile(r"\b(?:not_implemented|unimplemented_api)\b|\b(?:todo|unimplemented)!\s*\(")
TYPES = {
"T:LibreMetaverse.RLV.AttachmentRequest": "crate::service::AttachmentRequest",
"T:LibreMetaverse.RLV.RlvActionCallbacksDefault":
"crate::service::RlvActionCallbacksDefault",
"T:LibreMetaverse.RLV.RlvCallbacksDefault": "crate::service::RlvCallbacksDefault",
"T:LibreMetaverse.RLV.RlvCommandProcessor": "crate::service::RlvCommandProcessor",
"T:LibreMetaverse.RLV.RlvService": "crate::service::RlvService",
}
def require_markers(path: Path, markers: tuple[str, ...]) -> None:
text = path.read_text()
missing = [marker for marker in markers if marker not in text]
if missing:
raise SystemExit(f"{path.name}: audit evidence missing: " + ", ".join(missing))
def catalog_members() -> set[str]:
catalog = json.loads(CATALOG.read_text())
assembly = next(
value for value in catalog["assemblies"]
if value["identity"]["name"] == "LibreMetaverse.RLV"
)
return {
member["doc_id"]
for api_type in assembly["types"]
if api_type["doc_id"] in TYPES
for member in api_type["members"]
}
def generated_type_block(text: str, doc_id: str) -> str:
marker = f"/// C# type: `{doc_id}`."
start = text.find(marker)
if start < 0:
raise SystemExit(f"generated declaration is missing for {doc_id}")
next_type = text.find("/// C# type:", start + len(marker))
return text[start:] if next_type < 0 else text[start:next_type]
def main() -> None:
for api_type, declaration in TYPES.items():
if generate_api_shims.NATIVE_TYPES.get(api_type) != declaration:
raise SystemExit(f"issue 80 native type mapping is missing for {api_type}")
members = catalog_members()
if len(members) != 53:
raise SystemExit(f"issue 80 expected 53 mapped members, found {len(members)}")
source = SOURCE.read_text()
if STUB_RE.search(source):
raise SystemExit("issue 80 owned Rust stubs remain in service.rs")
if re.search(r"\b(?:Mutex|RwLock|read_lock|write_lock)\b", source):
raise SystemExit("service.rs must not acquire internal locks around host callbacks")
generated = GENERATED.read_text()
for api_type in TYPES:
if STUB_RE.search(generated_type_block(generated, api_type)):
raise SystemExit(f"issue 80 owned generated stubs remain for {api_type}")
if "pub trait IRlvActionCallbacks: Send + Sync" not in generated:
raise SystemExit("action callbacks are not thread-safe")
if "pub trait IRlvQueryCallbacks: Send + Sync" not in generated:
raise SystemExit("query callbacks are not thread-safe")
catalog = json.loads((ROOT / "tests" / "upstream-tests.json").read_text())
rlv_cases = [
case for case in catalog["tests"]
if case.get("rust_file", "").startswith("tests/compat/tests/rlv_")
]
if len(rlv_cases) != 616 or len({case["rust_file"] for case in rlv_cases}) != 24:
raise SystemExit("issue 80 RLV parity catalog is not the complete 616-case slice")
markers = generate_surface.scan_parity_markers(ROOT)
evidence_keys = ("status", "rust_file", "rust_line", "rust_test", "rust_body_sha256")
for case in rlv_cases:
marker = markers.get(case["id"], [])
if len(marker) != 1 or any(marker[0][key] != case[key] for key in evidence_keys):
raise SystemExit(f"issue 80 parity metadata is stale for {case['id']}")
require_markers(SOURCE, (
"MAX_RLV_MESSAGE_BYTES", "MAX_RLV_COMMANDS", "pub struct AttachmentRequest",
"pub struct RlvActionCallbacksDefault", "pub struct RlvCallbacksDefault",
"pub struct RlvCommandProcessor", "pub struct RlvService",
"process_instant_message", "process_message", "process_restriction",
"send_restriction_notification", "send_notification", "refresh_inventory",
"report_inventory_offer_accepted", "report_item_attached",
"report_send_public_message", "report_sit", "throw_if_cancellation_requested",
"default_callbacks_observe_cancellation_without_side_effects",
"attachment_request_hash_is_stable_and_value_based",
))
require_markers(STATE, (
"set_action_callbacks", "notify_removed_restriction",
"let removed = {", "let callbacks = read_lock", ".send_reply(",
))
require_markers(SUPPORT, (
"struct RecordingActions", "struct RecordingQueries", "struct RlvHarness",
"inventory_map.clone()", "RlvService::new",
))
require_markers(QUERY_TESTS, (
"manual_blacklist_all", "manual_version", "get_environment_known_setting",
"get_status_all_senders", "get_current_sit_id",
))
require_markers(ACTION_TESTS, (
"attach_this_by_id", "attach_all_this_recursive",
"attach_over_or_replace_uses_position_from_folder_name",
))
require_markers(NOTIFY_TESTS, (
"notify_every_restriction", "notify_multiple_channels_filtered",
"notify_inventory_offer", "notify_sit_and_stand_legal",
))
require_markers(DOC, (
"Callback and service contract", "Send + Sync",
"after releasing every manager lock", "completed inventory and agent",
"616", "ubuntu-latest",
))
require_markers(WORKFLOW, ("required-gate",))
print(
"issue 80 audit: native callbacks, typed actions and queries, bounded service "
"dispatch, inventory/agent adapters, cancellation, post-lock notifications, "
"recording-fake workflows, compatibility evidence, and docs are present"
)
if __name__ == "__main__":
main()