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
7 changes: 7 additions & 0 deletions doc/architecture/package-architecture-rfc.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,13 @@ Approve this RFC as the target architecture before implementation phases begin.

Clean packaging so only canonical packages and approved compatibility shims enter the wheel. Add the unified CLI and application factories.

The first packaging-boundary step removes the repository-only `doc`, `env`,
`log`, `scripts`, and `test` namespaces (including their descendants) from the
wheel. Transitional root runtime packages remain explicitly installed until
their focused migrations; `data` remains installed pending a separate consumer
and package-data audit. This interim boundary does not authorize entrypoint or
service migration.

### Phase 2 — contracts and dependency-light domains

Move versioned contracts, Runtime Profile, lifecycle models, capability models, and topology/resource models.
Expand Down
15 changes: 15 additions & 0 deletions doc/package_migration_phase_a.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,21 @@ dropping root packages. The final package-migration phase removes the explicit
transition and enables src-only discovery after every listed root runtime
package is migrated or intentionally deprecated through review.

### Interim repository-only wheel boundary

The first substantive packaging-boundary step removes `doc`, `doc.blog`,
`doc.integrations`, `env`, `env.config`, `env.docker`, `env.docker.cu130`,
`env.docker.cu130.scripts`, `log`, `log.scheduler`, `scripts`, `test`, and
`test.kdn` from the explicit setuptools package list. These directories remain
available in source checkouts but are not application packages supplied by the
wheel.

The exact interim wheel top-level package set is `UI`, `cacheroute`,
`cacheroute_compat`, `client`, `core`, `data`, `instance`, `kdn_server`,
`model`, `proxy`, `scheduler`, `store`, and `util`. These root runtime packages
remain transitional. In particular, `data` and its existing descendants remain
installed and unchanged until a separate consumer and package-data audit.

## Later phases and risks

1. PR #156 performs the observability implementation migration described above.
Expand Down
14 changes: 5 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,26 @@ dev = [
]

[tool.setuptools]
# Transitional Phase A package map. Root packages remain installed until their
# later reviewed migrations; the final migration phase replaces this explicit
# list with src-only discovery.
# Transitional package map. Repository-only namespaces are excluded, while
# root runtime packages remain explicitly installed pending focused migrations.
package-dir = {cacheroute = "src/cacheroute", cacheroute_compat = "src/cacheroute_compat"}
packages = [
"cacheroute", "cacheroute.compat", "cacheroute.observability",
"cacheroute_compat",
"UI", "UI.client_ui", "UI.client_ui.static", "UI.client_ui.templates",
"UI.kdn_ui", "UI.proxy_ui", "UI.proxy_ui.static",
"client", "client.taskset", "core", "data", "data.CacheRoute_dataset",
"data.CacheRoute_dataset.knowledge_document", "doc", "doc.blog",
"doc.integrations", "env", "env.config", "env.docker", "env.docker.cu130",
"env.docker.cu130.scripts", "instance", "instance.TPOT_predictor",
"data.CacheRoute_dataset.knowledge_document", "instance", "instance.TPOT_predictor",
"instance.TTFT_predictor", "instance.TTFT_predictor.data", "instance.pclient",
"instance.resource_agent", "instance.resource_agent.src",
"instance.resource_dashboard", "instance.resource_dashboard.static",
"kdn_server", "kdn_server.KV_database", "kdn_server.contracts",
"kdn_server.domain", "kdn_server.gateway", "kdn_server.sclient",
"kdn_server.text_database", "kdn_server.text_database.blocks",
"kdn_server.util", "log", "log.scheduler", "model", "proxy",
"kdn_server.util", "model", "proxy",
"proxy.metrics", "proxy.metrics.data", "proxy.queue", "proxy.resource",
"proxy.sclient", "proxy.strategy", "scheduler", "scheduler.knowledge",
"scheduler.resource", "scheduler.strategy", "scripts", "store", "test",
"test.kdn", "util",
"scheduler.resource", "scheduler.strategy", "store", "util",
]

[tool.setuptools.package-data]
Expand Down
17 changes: 13 additions & 4 deletions test/test_repository_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"cacheroute", "cacheroute.compat", "cacheroute.observability",
"cacheroute_compat",
}
REPOSITORY_ONLY_PACKAGE_PREFIXES = {
"doc", "env", "log", "scripts", "test",
}
SOURCE_BOOTSTRAP_ENTRYPOINTS = {
Path("client/client.py"),
Path("client/kv_timing_sender.py"),
Expand Down Expand Up @@ -70,11 +73,15 @@
"*.__pycache__", "*.__pycache__.*",
".mypy_cache", ".mypy_cache.*", ".ruff_cache", ".ruff_cache.*",
"tests", "tests.*", "docs", "docs.*",
# Repository-only Markdown architecture documents are not wheel packages.
"doc.architecture", "doc.architecture.*",
*REPOSITORY_ONLY_PACKAGE_PREFIXES,
*(f"{prefix}.*" for prefix in REPOSITORY_ONLY_PACKAGE_PREFIXES),
]


def _has_package_prefix(package, prefixes):
return any(package == prefix or package.startswith(f"{prefix}.") for prefix in prefixes)


def test_no_unreviewed_functional_root_directories():
tracked = {
path.relative_to(ROOT).parts[0]
Expand All @@ -86,8 +93,10 @@ def test_no_unreviewed_functional_root_directories():
def test_transitional_explicit_packages_match_root_discovery():
configuration = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
configured = set(configuration["tool"]["setuptools"]["packages"])
assert "doc.architecture" not in configured
assert not any(package.startswith("doc.architecture.") for package in configured)
assert not any(
_has_package_prefix(package, REPOSITORY_ONLY_PACKAGE_PREFIXES)
for package in configured
)
discovered_root = _discover_root_namespace_packages()
assert CANONICAL_PACKAGES <= configured
assert configured - CANONICAL_PACKAGES == discovered_root
Expand Down
34 changes: 32 additions & 2 deletions test/test_wheel_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

EXPECTED_TOP_LEVEL_PACKAGES = {
"UI", "cacheroute", "cacheroute_compat", "client", "core", "data",
"env", "instance", "kdn_server", "model", "proxy", "scheduler",
"scripts", "store", "test", "util",
"instance", "kdn_server", "model", "proxy", "scheduler", "store", "util",
}
FORBIDDEN_TOP_LEVEL_PACKAGES = {"doc", "env", "log", "scripts", "test"}
REQUIRED_PACKAGE_DATA = {
"UI/client_ui/static/app.js",
"UI/client_ui/static/style.css",
Expand Down Expand Up @@ -80,6 +80,12 @@ def test_wheel_preserves_packages_and_runtime_data(built_wheel):
if "/" in name and not name.partition("/")[0].endswith(".dist-info")
}
assert packaged == EXPECTED_TOP_LEVEL_PACKAGES
assert packaged.isdisjoint(FORBIDDEN_TOP_LEVEL_PACKAGES)
for prefix in FORBIDDEN_TOP_LEVEL_PACKAGES:
assert not any(
member == prefix or member.startswith(f"{prefix}/")
for member in members
)
assert REQUIRED_PACKAGE_DATA <= members
assert "instance/TTFT_predictor/prompt_length_validation.log" not in members

Expand Down Expand Up @@ -112,6 +118,30 @@ def test_dependency_light_clean_wheel_imports(built_wheel, tmp_path):
print(result.stdout, end="")


def test_repository_only_namespaces_are_not_importable_from_clean_wheel(built_wheel, tmp_path):
python = _create_isolated_environment(tmp_path / "negative-venv")
subprocess.run([python, "-m", "pip", "install", "--no-deps", str(built_wheel)], check=True)
result = _run_outside_repo(python, tmp_path / "outside-negative", """
import importlib.util
from pathlib import Path
import sys

for name in ("doc", "env", "log", "scripts", "test"):
spec = importlib.util.find_spec(name)
if name == "test":
# CPython may supply its own stdlib test package. It must not resolve
# from this environment's wheel installation directory.
if spec is not None:
assert Path(spec.origin).resolve().is_relative_to(Path(sys.base_prefix).resolve())
assert "site-packages" not in Path(spec.origin).parts
else:
assert spec is None, name
print("repository-only clean-wheel imports: absent")
""")
assert "repository-only clean-wheel imports: absent" in result.stdout
print(result.stdout, end="")


@pytest.mark.network
@pytest.mark.skipif(
os.getenv("CACHEROUTE_RUN_NETWORK_TESTS") != "1",
Expand Down