From 54f28681b965d98f69788baf6d8308188d0a70f1 Mon Sep 17 00:00:00 2001 From: Edwin Amirian Date: Wed, 12 Aug 2026 15:40:31 -0700 Subject: [PATCH] test: enforce Intelligence OS ownership boundaries --- docs/README.md | 3 + ...gine-compatibility-disposition-v0.8.0.json | 53 ++++++++ ...-runtime-boundary-v0.8.0-work-packet-v1.md | 24 +++- ...test_intelligence_os_ownership_boundary.py | 117 ++++++++++++++++++ 4 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 docs/design/core-engine-compatibility-disposition-v0.8.0.json create mode 100644 tests/test_intelligence_os_ownership_boundary.py diff --git a/docs/README.md b/docs/README.md index d8e58fb..30e5b50 100644 --- a/docs/README.md +++ b/docs/README.md @@ -78,6 +78,9 @@ support the public roadmap but do not compete with it for outcome state or dispa - [Intelligence OS runtime-boundary realignment](design/intelligence-os-runtime-boundary-v0.8.0-work-packet-v1.md) — the active 0.8B packet, accepted AM4 input, default isolation of embedded product-intelligence engines, compatibility switch, remaining runtime convergence, and rollback. +- [Legacy host compatibility disposition](design/core-engine-compatibility-disposition-v0.8.0.json) + — the machine-checked 0.8 owner and migration treatment for every top-level `core.engine` + package; the canonical public roots remain `ace.core`, `ace.intelligence`, and `ace.application`. - [Intelligence Builder Connect work packet](design/intelligence-builder-connect-v0.7.0-work-packet-v1.md) — the bounded 0.7B Connection Agent implementation, reuse audit, failure controls, and evidence plan. diff --git a/docs/design/core-engine-compatibility-disposition-v0.8.0.json b/docs/design/core-engine-compatibility-disposition-v0.8.0.json new file mode 100644 index 0000000..318dbf1 --- /dev/null +++ b/docs/design/core-engine-compatibility-disposition-v0.8.0.json @@ -0,0 +1,53 @@ +{ + "schema_version": "ace.architecture.core-engine-disposition/v1", + "root": "core.engine", + "canonical_public_roots": [ + "ace.core", + "ace.intelligence", + "ace.application" + ], + "dispositions": { + "transport_adapter_host": { + "owner": "application", + "treatment": "compatibility host; transports may call public ACE services but are not a source of truth", + "packages": ["api", "cli", "mcp", "worker"] + }, + "host_infrastructure": { + "owner": "core", + "treatment": "compatibility infrastructure; migrate behind public Core ports without rewriting evidence", + "packages": ["core", "runtime", "seam"] + }, + "governed_mechanics_bridge": { + "owner": "core", + "treatment": "retained implementation bridge; new public contracts belong in ace.core", + "packages": [ + "cognition", "embedding", "events", "execution", "foresight", "graph", + "grounded_state", "learning", "orchestration", "orchestrator", "reasoning", + "recognition", "review", "session", "verification" + ] + }, + "intelligence_lifecycle_bridge": { + "owner": "intelligence", + "treatment": "retained implementation bridge; new invariant lifecycle behavior belongs in ace.intelligence or ace.application", + "packages": [ + "ai_briefing", "capture", "conductor", "contributions", "flow", "handoff", + "intelligence", "live", "notifications", "proactive", "scanner", "search", + "sentinel", "synthesis", "voice" + ] + }, + "executable_specialization": { + "owner": "adapter_or_strategy", + "treatment": "explicitly enabled executable compatibility surface; never Domain Pack data or implicit authority", + "packages": ["extensions", "generation", "github", "playbooks", "runner", "templates"] + }, + "legacy_product_application": { + "owner": "application", + "treatment": "frozen product-era compatibility surface; no new canonical callers or category language", + "packages": [ + "arms", "atc", "canvas", "canvas_bridge", "chat", "diagram", "eval", + "evaluation", "ideas", "onboarding", "pm", "product", "product_state", + "reports", "research", "skills" + ] + } + } +} diff --git a/docs/design/intelligence-os-runtime-boundary-v0.8.0-work-packet-v1.md b/docs/design/intelligence-os-runtime-boundary-v0.8.0-work-packet-v1.md index 4c50737..2b8e75b 100644 --- a/docs/design/intelligence-os-runtime-boundary-v0.8.0-work-packet-v1.md +++ b/docs/design/intelligence-os-runtime-boundary-v0.8.0-work-packet-v1.md @@ -1,6 +1,6 @@ # ACE 0.8.0 runtime-boundary realignment work packet -Status: **active 0.8B packet; B1 compatibility isolation implemented** +Status: **active 0.8B packet; B1 compatibility isolation and B2 ownership guards implemented** Public milestone: [issue #40](https://github.com/augmented-cognition-engine/core/issues/40) Accepted base: `main@bb7f4ba` (0.8A plus explicitly reviewed AM4 lifecycle semantics) @@ -44,6 +44,25 @@ kernel-boundary, exact-eleven, AM1–AM4, and restart-oriented tests. Retention, soft forget, and dependency-complete supported-store erasure are therefore available to the later 0.8C resource plane and 0.8D Atrium experience; they are not reimplemented here. +## B2 — enforce ownership and classify the compatibility host + +The canonical dependency direction is now machine checked across every Python module in the three +public layers: + +- `ace.core` cannot import Intelligence, application, the legacy host, transports, or extensions; +- `ace.intelligence` cannot import application, the legacy host, transports, or extensions; +- `ace.application` may compose public Core and Intelligence ports but cannot import the legacy + host or a transport framework; and +- none of the three layers may acquire sources or execute external effects through a concrete + network, process, or socket client. Those operations enter through declared ports and adapters. + +The broader `core.engine` tree remains a compatibility host during 0.8. Every top-level package is +therefore assigned exactly one machine-checked disposition in +`core-engine-compatibility-disposition-v0.8.0.json`. Adding a directory without declaring its +owner and treatment fails the boundary suite. Product-era arms, product surfaces, and Canvas are +explicitly frozen compatibility applications; their presence does not make their vocabulary or +dependency direction canonical. + ## Acceptance B1 passes only when: @@ -59,9 +78,8 @@ B1 passes only when: ## Remaining 0.8B work -B1 does not close 0.8B. The remaining runtime-boundary packet must: +B1 and B2 do not close 0.8B. The remaining runtime-boundary packet must: -- publish a machine-checked disposition for the broader product-era `core.engine` surface; - keep generic planning, authority, execution admission, assurance, outcomes, and erasure behind Core ports; - keep Observation-to-Feedback interpretation behind Intelligence and application services; diff --git a/tests/test_intelligence_os_ownership_boundary.py b/tests/test_intelligence_os_ownership_boundary.py new file mode 100644 index 0000000..9683bf7 --- /dev/null +++ b/tests/test_intelligence_os_ownership_boundary.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +import ast +import json +from pathlib import Path + +import pytest + +pytestmark = pytest.mark.unit + +REPO = Path(__file__).resolve().parents[1] +ACE = REPO / "ace" +LEGACY_HOST = REPO / "core" / "engine" +DISPOSITION = REPO / "docs" / "design" / "core-engine-compatibility-disposition-v0.8.0.json" + + +def _imports(path: Path) -> list[tuple[int, str]]: + tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) + found: list[tuple[int, str]] = [] + for node in ast.walk(tree): + if isinstance(node, ast.Import): + found.extend((node.lineno, alias.name) for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + found.append((node.lineno, node.module)) + return found + + +def _offenders(root: Path, forbidden: tuple[str, ...]) -> list[str]: + offenders: list[str] = [] + for path in root.rglob("*.py"): + if "__pycache__" in path.parts: + continue + for line, imported in _imports(path): + if imported.startswith(forbidden): + offenders.append(f"{path.relative_to(REPO)}:{line} ({imported})") + return sorted(offenders) + + +def test_core_has_no_inward_intelligence_application_or_host_dependency() -> None: + assert ( + _offenders( + ACE / "core", + ( + "ace.intelligence", + "ace.application", + "core.engine", + "extensions", + "ace_mcp_client", + "fastapi", + ), + ) + == [] + ) + + +def test_intelligence_has_no_application_host_transport_or_extension_dependency() -> None: + assert ( + _offenders( + ACE / "intelligence", + ("ace.application", "core.engine", "extensions", "ace_mcp_client", "fastapi"), + ) + == [] + ) + + +def test_application_composes_public_ports_without_importing_legacy_hosts() -> None: + assert ( + _offenders( + ACE / "application", + ("core.engine", "extensions", "ace_mcp_client", "fastapi"), + ) + == [] + ) + + +def test_public_ace_layers_do_not_acquire_sources_or_execute_effects_directly() -> None: + forbidden_effect_modules = ( + "requests", + "httpx", + "aiohttp", + "urllib.request", + "urllib3", + "boto3", + "subprocess", + "socket", + ) + assert _offenders(ACE / "core", forbidden_effect_modules) == [] + assert _offenders(ACE / "intelligence", forbidden_effect_modules) == [] + assert _offenders(ACE / "application", forbidden_effect_modules) == [] + + +def test_every_legacy_engine_package_has_one_explicit_0_8_disposition() -> None: + manifest = json.loads(DISPOSITION.read_text(encoding="utf-8")) + assert manifest["schema_version"] == "ace.architecture.core-engine-disposition/v1" + assert manifest["canonical_public_roots"] == ["ace.core", "ace.intelligence", "ace.application"] + + declared: list[str] = [] + for disposition in manifest["dispositions"].values(): + assert disposition["owner"] in {"core", "intelligence", "application", "adapter_or_strategy"} + assert disposition["treatment"].strip() + declared.extend(disposition["packages"]) + + actual = sorted( + path.name + for path in LEGACY_HOST.iterdir() + if path.is_dir() and path.name != "__pycache__" and any(path.rglob("*.py")) + ) + assert len(declared) == len(set(declared)), "a legacy host package has multiple owners" + assert sorted(declared) == actual + + +def test_product_era_surface_is_explicitly_frozen_compatibility() -> None: + manifest = json.loads(DISPOSITION.read_text(encoding="utf-8")) + legacy = manifest["dispositions"]["legacy_product_application"] + assert legacy["owner"] == "application" + assert "frozen" in legacy["treatment"] + assert {"arms", "product", "product_state", "canvas"}.issubset(legacy["packages"])