Skip to content
Open
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
18 changes: 17 additions & 1 deletion doc/developer-handbook/development-and-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,30 @@ Use repository-relative commands. Do not put user-specific absolute paths in PR
| Compile check | Python sources in `src` and `test` compile without syntax errors. | `python3 -m compileall -q src test` |
| Focused package governance | Explicit package lists, repository-only namespaces, and source bootstraps remain reviewable. | `python3 -m pytest -q test/test_repository_governance.py test/test_namespace_layout.py` |
| Editable install | The configured package set remains importable from an editable installation without changing package discovery. | `python3 -m pip install -e . --no-deps` followed by `python3 -m pytest -q test/test_namespace_layout.py` |
| Wheel build | The project builds a wheel from the checked-out source without build isolation when `build` is installed. | `python3 -m build --no-isolation` |
| Tracked-source release chain | Export only files tracked at the revision, build a direct wheel and an sdist, then build the release wheel from the unpacked sdist without `.git` metadata. | `python3 -m pytest -q -s test/test_wheel_install.py -m "not network"` |
| Non-network clean wheel | Runtime packages install from the built wheel without dependency resolution or source checkout leakage. | `python3 -m pytest -q -s test/test_wheel_install.py -m "not network"` |
| Full isolated wheel | Declared dependencies install in a clean venv when a package index or local wheelhouse is available. | `CACHEROUTE_RUN_NETWORK_TESTS=1 python3 -m pytest -q -s test/test_wheel_install.py` |
| Offline full isolated wheel | The network-marked wheel test can use a local wheelhouse instead of indexes. | `CACHEROUTE_RUN_NETWORK_TESTS=1 CACHEROUTE_TEST_WHEELHOUSE="$PWD/wheelhouse" python3 -m pytest -q -s test/test_wheel_install.py` |
| Observability contracts | Current trace models, propagation, startup, and Proxy projection behavior remain valid. | `python3 -m pytest -q test/observability` |
| Contract foundations | KDN/Gateway v1 contracts and migration shims preserve object identity and validation. | `python3 -m pytest -q test/test_contract_foundation.py test/test_contract_service_migration.py` |
| Diff hygiene | The final diff has no whitespace errors. | `git diff --check "$(git merge-base origin/main HEAD)" HEAD` |

## Release artifact boundaries

Release validation distinguishes five layers: the active source checkout, a
Git-tracked source export, the sdist made from that export, the wheel made from
the sdist, and an isolated installation of that final wheel. A raw copy of the
active worktree is not release evidence because ignored or untracked files can
mask a stale explicit package declaration. The non-network wheel test also
builds a direct tracked-source wheel as a separate check, but uses the
sdist-derived wheel for content and isolated-install assertions.

For a manual release check, create a clean tracked-file export without `.git`,
run `python3 -m build --sdist --no-isolation` there, unpack the generated sdist
into a separate clean location, and build the wheel from that unpacked tree. Do
not substitute a wheel built from the original checkout for the sdist-derived
wheel.

## Required final-head checklist

```bash
Expand Down
13 changes: 11 additions & 2 deletions doc/developer-handbook/package-and-module-map.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Package and module map

Installed package set is derived from the explicit `[tool.setuptools].packages` list in [pyproject.toml](../../pyproject.toml); package discovery must not change in this handbook PR.
Installed package set is derived from the explicit `[tool.setuptools].packages` list in [pyproject.toml](../../pyproject.toml); package discovery remains explicit; each configured entry must map to reviewed, Git-tracked Python source.

| Concept / responsibility | Canonical package/module | Current transitional implementation | Dependency direction | Public/internal | Design link | Relevant tests | Migration status | Status |
|---|---|---|---|---|---|---|---|---|
Expand Down Expand Up @@ -33,4 +33,13 @@ Installed package set is derived from the explicit `[tool.setuptools].packages`

## Explicitly configured package coverage

`cacheroute`, `cacheroute.compat`, `cacheroute.observability`, `cacheroute.observability.v1`, `cacheroute.runtime`, `cacheroute.topology`, `cacheroute.cache`, `cacheroute.routing`, `cacheroute.contracts`, `cacheroute.contracts.v1`, `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`, `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`, `model`, `proxy`, `proxy.metrics`, `proxy.metrics.data`, `proxy.queue`, `proxy.resource`, `proxy.sclient`, `proxy.strategy`, `scheduler`, `scheduler.knowledge`, `scheduler.resource`, `scheduler.strategy`, `store`, `util`.
`cacheroute`, `cacheroute.compat`, `cacheroute.observability`, `cacheroute.observability.v1`, `cacheroute.runtime`, `cacheroute.topology`, `cacheroute.cache`, `cacheroute.routing`, `cacheroute.contracts`, `cacheroute.contracts.v1`, `cacheroute_compat`, `UI`, `UI.client_ui`, `UI.kdn_ui`, `UI.proxy_ui`, `client`, `client.taskset`, `core`, `data`, `instance`, `instance.TPOT_predictor`, `instance.TTFT_predictor`, `instance.pclient`, `instance.resource_agent`, `instance.resource_dashboard`, `kdn_server`, `kdn_server.contracts`, `kdn_server.domain`, `kdn_server.gateway`, `kdn_server.sclient`, `kdn_server.util`, `model`, `proxy`, `proxy.metrics`, `proxy.queue`, `proxy.resource`, `proxy.sclient`, `proxy.strategy`, `scheduler`, `scheduler.knowledge`, `scheduler.resource`, `scheduler.strategy`, `store`, `util`.


Tracked datasets, runtime state, browser assets, calibration tables, native-source
directories, and other package data are not Python packages merely because they
are nested below an installed package. Package-data patterns retain the reviewed
runtime assets; the explicit package list contains only directories with tracked
Python source. In particular, repository datasets under `data/CacheRoute_dataset`
and KDN state under `kdn_server/{KV_database,text_database}` remain outside the
wheel and are not package declarations.
15 changes: 5 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,13 @@ packages = [
"cacheroute.runtime", "cacheroute.topology", "cacheroute.cache", "cacheroute.routing",
"cacheroute.contracts", "cacheroute.contracts.v1",
"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", "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",
"UI", "UI.client_ui", "UI.kdn_ui", "UI.proxy_ui",
"client", "client.taskset", "core", "data", "instance", "instance.TPOT_predictor",
"instance.TTFT_predictor", "instance.pclient", "instance.resource_agent",
"instance.resource_dashboard", "kdn_server", "kdn_server.contracts",
"kdn_server.domain", "kdn_server.gateway", "kdn_server.sclient",
"kdn_server.text_database", "kdn_server.text_database.blocks",
"kdn_server.util", "model", "proxy",
"proxy.metrics", "proxy.metrics.data", "proxy.queue", "proxy.resource",
"proxy.metrics", "proxy.queue", "proxy.resource",
"proxy.sclient", "proxy.strategy", "scheduler", "scheduler.knowledge",
"scheduler.resource", "scheduler.strategy", "store", "util",
]
Expand Down
47 changes: 37 additions & 10 deletions test/test_repository_governance.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import ast
from fnmatch import fnmatchcase
import os
from pathlib import Path
import re
import subprocess
Expand Down Expand Up @@ -134,20 +133,48 @@ def test_transitional_explicit_packages_match_root_discovery():

def _discover_root_namespace_packages():
packages = set()
for directory, child_directories, _files in os.walk(ROOT):
relative = Path(directory).relative_to(ROOT)
if relative == Path("."):
child_directories[:] = [name for name in child_directories if not name.startswith(".")]
continue
package = ".".join(relative.parts)
if any(fnmatchcase(package, pattern) for pattern in GENERATED_PACKAGE_EXCLUDES):
child_directories[:] = []
for path in _tracked_files():
relative = path.relative_to(ROOT)
if path.suffix != ".py" or relative.name == "__init__.py" and len(relative.parts) == 1:
continue
if not any(part.startswith(".") for part in relative.parts):
package = ".".join(relative.parent.parts)
if package and not any(
fnmatchcase(package, pattern) for pattern in GENERATED_PACKAGE_EXCLUDES
):
packages.add(package)
return packages


def _configured_package_path(package, package_directories):
mapped = [
prefix for prefix in package_directories
if package == prefix or package.startswith(f"{prefix}.")
]
if not mapped:
return Path(*package.split("."))
prefix = max(mapped, key=len)
suffix = package.removeprefix(prefix).lstrip(".")
return Path(package_directories[prefix], *suffix.split(".")) if suffix else Path(package_directories[prefix])


def test_explicit_packages_have_reviewed_tracked_python_sources():
configuration = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
setuptools = configuration["tool"]["setuptools"]
tracked = {path.relative_to(ROOT) for path in _tracked_files()}
invalid = []
for package in setuptools["packages"]:
directory = _configured_package_path(package, setuptools.get("package-dir", {}))
if not any(
path.parent == directory and path.suffix == ".py"
for path in tracked
):
invalid.append(package)
assert not invalid, (
"explicit setuptools packages without tracked Python source: "
+ ", ".join(sorted(invalid))
)


def test_legacy_compatibility_references_are_narrowly_allowlisted():
references = _files_containing("cacheroute_compat")
assert references <= LEGACY_REFERENCE_ALLOWLIST
Expand Down
92 changes: 78 additions & 14 deletions test/test_wheel_install.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Verify wheel contents and imports without using the source checkout."""

from pathlib import Path
import io
import os
import shutil
import subprocess
import sys
import tarfile
import venv
import zipfile

Expand Down Expand Up @@ -64,26 +65,89 @@
}


@pytest.fixture(scope="module")
def built_wheel(tmp_path_factory):
repo = Path(__file__).resolve().parents[1]
wheelhouse = tmp_path_factory.mktemp("wheelhouse")
source = tmp_path_factory.mktemp("wheel-source") / "CacheRoute"
shutil.copytree(
repo,
source,
ignore=shutil.ignore_patterns(
".git", "build", "dist", "*.egg-info", "__pycache__",
".pytest_cache", ".mypy_cache", ".ruff_cache", ".venv", "wheelhouse",
),
def _tracked_paths(repo: Path) -> set[Path]:
result = subprocess.run(
["git", "ls-tree", "-rz", "--name-only", "HEAD"],
cwd=repo, check=True, capture_output=True,
)
return {
Path(os.fsdecode(name))
for name in result.stdout.split(b"\0") if name
}


def _export_tracked_source(repo: Path, destination: Path) -> None:
archive = subprocess.run(
["git", "archive", "--format=tar", "HEAD"],
cwd=repo, check=True, capture_output=True,
Comment on lines +81 to +82

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip release-chain checks in Git-free sdists

When this test is run from the unpacked sdist, which includes test/test_wheel_install.py but intentionally has no .git directory, this command exits with fatal: not a git repository before any wheel assertion runs. I reproduced that failure from the sdist generated by this commit; either mark this checkout-only test as skipped when Git metadata is absent or structure the fixture so the shipped test suite can validate an unpacked source distribution without resolving HEAD.

Useful? React with 👍 / 👎.

).stdout
destination.mkdir()
with tarfile.open(fileobj=io.BytesIO(archive), mode="r:") as source:
source.extractall(destination, filter="data")


def _pip_wheel(source: Path, wheelhouse: Path) -> Path:
subprocess.run(
[sys.executable, "-m", "pip", "wheel", str(source), "--no-deps", "--no-build-isolation", "-w", str(wheelhouse)],
[sys.executable, "-m", "pip", "wheel", str(source), "--no-deps",
"--no-build-isolation", "-w", str(wheelhouse)],
check=True,
)
return next(wheelhouse.glob("cacheroute-*.whl"))


@pytest.fixture(scope="module")
def release_artifacts(tmp_path_factory):
repo = Path(__file__).resolve().parents[1]
source = tmp_path_factory.mktemp("tracked-source") / "CacheRoute"
_export_tracked_source(repo, source)
exported = {
path.relative_to(source) for path in source.rglob("*")
if path.is_file() or path.is_symlink()
}
assert exported == _tracked_paths(repo)
assert not (source / ".git").exists()

direct_wheelhouse = tmp_path_factory.mktemp("direct-wheelhouse")
direct_wheel = _pip_wheel(source, direct_wheelhouse)

sdist_dir = tmp_path_factory.mktemp("sdist")
backend_script = (
"import setuptools.build_meta as backend, sys; "
"print(backend.build_sdist(sys.argv[1]))"
)
subprocess.run(
[sys.executable, "-c", backend_script, str(sdist_dir)],
cwd=source, check=True,
)
sdist = next(sdist_dir.glob("cacheroute-*.tar.gz"))

sdist_source_root = tmp_path_factory.mktemp("sdist-source")
with tarfile.open(sdist, mode="r:gz") as archive:
archive.extractall(sdist_source_root, filter="data")
sdist_source = next(path for path in sdist_source_root.iterdir() if path.is_dir())
assert not (sdist_source / ".git").exists()
final_wheelhouse = tmp_path_factory.mktemp("sdist-wheelhouse")
final_wheel = _pip_wheel(sdist_source, final_wheelhouse)
return {
"source": source,
"direct_wheel": direct_wheel,
"sdist": sdist,
"wheel": final_wheel,
}


@pytest.fixture(scope="module")
def built_wheel(release_artifacts):
return release_artifacts["wheel"]


def test_release_artifacts_use_tracked_source_and_both_build_paths(release_artifacts):
assert release_artifacts["direct_wheel"].is_file()
assert release_artifacts["sdist"].is_file()
assert release_artifacts["wheel"].is_file()
assert not (release_artifacts["source"] / ".git").exists()


def _create_isolated_environment(path: Path) -> Path:
venv.EnvBuilder(with_pip=True, system_site_packages=False).create(path)
return path / "bin" / "python"
Expand Down