Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
document the canonical eight-domain backend layout and LangGraph extras, and
restore current platform concepts to the docs navigation.

## [0.15.1.0.1] - 2026-08-05

### Changed

- Classify `zeroth.core.langgraph_gateway` under the backend architecture
policy through five legacy prefix mappings, so every module in the package
resolves to a domain instead of escaping the dependency scanner.
- Record the two forbidden edges the classification exposes as documented
temporary exceptions — the governance decision service's admission reuse and
the LangGraph integration client's enforcement reuse — with removal tracked
by the gateway relocation task.

## [0.15.1] - 2026-08-05

### Added
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/lib/version.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { VERSION } from "./version";

describe("console version", () => {
it("matches the integrated Python package version", () => {
expect(VERSION).toBe("0.15.1");
expect(VERSION).toBe("0.15.1.0.1");
});
});
2 changes: 1 addition & 1 deletion frontend/app/lib/version.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// Generated from pyproject.toml; run `npm run gen:version`.
export const VERSION = "0.15.1";
export const VERSION = "0.15.1.0.1";
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "zeroth-core"
version = "0.15.1"
version = "0.15.1.0.1"
description = "Governed medium-code platform for production-grade multi-agent systems"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
51 changes: 51 additions & 0 deletions src/zeroth/_architecture.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@
"zeroth.core.guardrails": "governance",
"zeroth.core.http": "integrations",
"zeroth.core.identity": "governance",
"zeroth.core.langgraph_gateway": "service",
"zeroth.core.langgraph_gateway.models": "contracts",
"zeroth.core.langgraph_gateway.inventory": "contracts",
"zeroth.core.langgraph_gateway.capabilities": "governance",
"zeroth.core.langgraph_gateway.events": "governance",
"zeroth.core.mappings": "contracts",
"zeroth.core.memory": "integrations",
"zeroth.core.observability": "platform",
Expand Down Expand Up @@ -298,6 +303,52 @@ def _exception_group(
"docs/backend-import-migration.md."
),
),
**_exception_group(
("zeroth.governance.decisions.service", "zeroth.core.langgraph_gateway.admission"),
reason=(
"DecisionService reuses the admission orchestrator that ships inside "
"the service-classified langgraph_gateway package to evaluate "
"governance decisions against the same admission path the gateway "
"runs at request time, but governance may only depend on contracts "
"and platform."
),
removal_task=(
"Task ZER-24 (gateway relocation, Phase B): relocation alone does "
"not clear this exception, because the adopted layout moves "
"admission.py to zeroth.service.langgraph_gateway, keeping it "
"service-owned. The exception is removable only once admission "
"grows a governance-importable seam in a domain governance may "
"depend on (contracts or platform), or governance gets its own "
"decision-admission surface, with "
"zeroth.governance.decisions.service redirected to that seam or "
"surface instead of importing the service-owned orchestrator."
),
),
**_exception_group(
(
"zeroth.integrations.langgraph._gateway_client",
"zeroth.core.langgraph_gateway.enforcement",
),
reason=(
"The LangGraph integration client reuses the enforcement evidence "
"and decision surface that ships inside the service-classified "
"langgraph_gateway package so the client and the gateway speak one "
"wire contract, but integrations may depend on contracts, econ, "
"governance, platform, and runtime, never on service."
),
removal_task=(
"Task ZER-24 (gateway relocation, Phase B): enforcement.py "
"postdates the ZER-23 mapping table, so its canonical home is "
"explicitly a ZER-24 decision. Relocation alone does not clear this "
"exception, because moving enforcement.py to "
"zeroth.service.langgraph_gateway keeps it service-owned and leaves "
"the edge forbidden. The exception is removable only once "
"relocation assigns enforcement a canonical home in a domain "
"integrations may import, or extracts a contracts-level enforcement "
"surface, with zeroth.integrations.langgraph._gateway_client "
"redirected to that home or surface."
),
),
**_exception_group(
("zeroth.econ.analytics.adapter", "zeroth.runtime.agents.provider"),
("zeroth.econ.analytics.opportunities", "zeroth.governance.audit.models"),
Expand Down
107 changes: 106 additions & 1 deletion tests/architecture/test_backend_dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import pytest


REPO_ROOT = Path(__file__).parents[2]
SOURCE_ROOT = REPO_ROOT / "src"

Expand Down Expand Up @@ -243,3 +242,109 @@ def test_real_repository_obeys_backend_dependency_direction() -> None:
f"{item.path.relative_to(REPO_ROOT)}:{item.line}: {item.importer} imports {item.imported}"
for item in scan.violations
)


def test_langgraph_gateway_modules_classify_into_the_expected_domains() -> None:
architecture = importlib.import_module("zeroth._architecture")

assert {
module: architecture._canonical_domain(module)
for module in (
"zeroth.core.langgraph_gateway",
"zeroth.core.langgraph_gateway.models",
"zeroth.core.langgraph_gateway.inventory",
"zeroth.core.langgraph_gateway.capabilities",
"zeroth.core.langgraph_gateway.events",
"zeroth.core.langgraph_gateway.admission",
"zeroth.core.langgraph_gateway.compatibility",
"zeroth.core.langgraph_gateway.context",
"zeroth.core.langgraph_gateway.enforcement",
"zeroth.core.langgraph_gateway.enforcement_store",
"zeroth.core.langgraph_gateway.headers",
"zeroth.core.langgraph_gateway.proxy",
"zeroth.core.langgraph_gateway.routes",
"zeroth.core.langgraph_gateway.transport",
)
} == {
"zeroth.core.langgraph_gateway": "service",
"zeroth.core.langgraph_gateway.models": "contracts",
"zeroth.core.langgraph_gateway.inventory": "contracts",
"zeroth.core.langgraph_gateway.capabilities": "governance",
"zeroth.core.langgraph_gateway.events": "governance",
"zeroth.core.langgraph_gateway.admission": "service",
"zeroth.core.langgraph_gateway.compatibility": "service",
"zeroth.core.langgraph_gateway.context": "service",
"zeroth.core.langgraph_gateway.enforcement": "service",
"zeroth.core.langgraph_gateway.enforcement_store": "service",
"zeroth.core.langgraph_gateway.headers": "service",
"zeroth.core.langgraph_gateway.proxy": "service",
"zeroth.core.langgraph_gateway.routes": "service",
"zeroth.core.langgraph_gateway.transport": "service",
}


def test_legacy_domain_prefixes_pin_the_exact_gateway_mapping_set() -> None:
architecture = importlib.import_module("zeroth._architecture")

gateway_prefixes = {
key: value
for key, value in architecture.LEGACY_DOMAIN_PREFIXES.items()
if key.startswith("zeroth.core.langgraph_gateway")
}

assert gateway_prefixes == {
"zeroth.core.langgraph_gateway": "service",
"zeroth.core.langgraph_gateway.models": "contracts",
"zeroth.core.langgraph_gateway.inventory": "contracts",
"zeroth.core.langgraph_gateway.capabilities": "governance",
"zeroth.core.langgraph_gateway.events": "governance",
}


def test_every_langgraph_gateway_file_on_disk_classifies_to_a_domain() -> None:
architecture = importlib.import_module("zeroth._architecture")
gateway_root = SOURCE_ROOT / "zeroth" / "core" / "langgraph_gateway"

for path in sorted(gateway_root.glob("*.py")):
module, _ = architecture._module_name(path, SOURCE_ROOT)
assert architecture._canonical_domain(module) is not None, module


def test_langgraph_gateway_scan_surfaces_exactly_the_forbidden_edges() -> None:
architecture = importlib.import_module("zeroth._architecture")
scan = architecture.scan_backend_dependencies(SOURCE_ROOT, exceptions={})
gateway_edges = {
(item.importer, item.imported)
for item in scan.violations
if item.importer.startswith("zeroth.core.langgraph_gateway")
or item.imported.startswith("zeroth.core.langgraph_gateway")
}

assert gateway_edges == {
("zeroth.governance.decisions.service", "zeroth.core.langgraph_gateway.admission"),
(
"zeroth.integrations.langgraph._gateway_client",
"zeroth.core.langgraph_gateway.enforcement",
),
}


def test_langgraph_gateway_exceptions_are_the_only_gateway_entries_and_documented() -> None:
architecture = importlib.import_module("zeroth._architecture")
gateway_exceptions = {
edge
for edge in architecture.TEMPORARY_EXCEPTIONS
if "langgraph_gateway" in edge[0] + edge[1]
}

assert gateway_exceptions == {
("zeroth.governance.decisions.service", "zeroth.core.langgraph_gateway.admission"),
(
"zeroth.integrations.langgraph._gateway_client",
"zeroth.core.langgraph_gateway.enforcement",
),
}
for edge in sorted(gateway_exceptions):
exception = architecture.TEMPORARY_EXCEPTIONS[edge]
assert exception.reason.strip()
assert exception.removal_task.startswith("Task ")
2 changes: 1 addition & 1 deletion tests/docs/test_release_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

REPO_ROOT = Path(__file__).resolve().parents[2]

EXPECTED_VERSION = "0.15.1"
EXPECTED_VERSION = "0.15.1.0.1"


def test_project_version_matches_release() -> None:
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading