alishahryar1--free-claude-code
d428b5904a
## Problem Architecture contracts mixed real dependency rules with deleted-module tombstones and exact internal file inventories. Correct refactors therefore had to preserve history instead of the current ownership model. ## Changes | Before | After | | --- | --- | | Cross-package rules were duplicated across narrow source and layout assertions. | One least-privilege matrix and AST scanner enforce every production package edge. | | Bare imports, undeclared ownership roots, and module cycles could escape generic enforcement. | Namespaced imports, initialized owners, exact exceptions, and an acyclic module graph are enforced. | | Responses, messaging-tree, and optional dependency ownership relied on scattered checks. | Facade use and lazy optional dependency owners are declared and verified centrally. | | The messaging facade re-exported workflow, persistence, and parsing internals. | The messaging facade exposes only ingress values, platform ports, and managed-session protocols. | | Migration-era tests froze deleted modules and internal filenames. | Customer contracts remain while obsolete tombstones and layout inventories are removed. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR replaces historical architecture checks with declarative import-boundary rules. The main changes are: - Added a documented package dependency matrix and facade ownership rules. - Narrowed the messaging package facade to its supported extension surface. - Moved messaging tree consumers to the `messaging.trees` facade. - Reworked architecture tests around AST scanning, optional dependency owners, and acyclic imports. - Bumped the package version and refreshed the lockfile. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - The architecture contracts tests were executed as part of the general contract validation. - The test run completed with exit code 0, indicating success. - The pytest summary shows 26 tests passed in 3.99 seconds. - An artifact log captures the exact command, working directory, environment recreation output, and verbose test item names for audit. <a href="https://app.greptile.com/trex/runs/14085499/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/messaging/__init__.py | Narrows the top-level messaging exports to the documented supported extension types. | | tests/contracts/test_import_boundaries.py | Replaces historical layout checks with declarative import-boundary enforcement. | | ARCHITECTURE.md | Documents the current dependency matrix, facade boundaries, and optional dependency owners. | | src/free_claude_code/messaging/node_event_pipeline.py | Uses the messaging tree facade for `NodeClaim`. | | src/free_claude_code/messaging/node_runner.py | Uses the messaging tree facade for queue, snapshot, cancellation, and claim types. | </details> <sub>Reviews (2): Last reviewed commit: ["Enforce architecture with declarative im..."](https://github.com/alishahryar1/free-claude-code/commit/68508f3d2bad19d4d1b8a9ca5d181e1433db4d71) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43498975)</sub> <!-- /greptile_comment -->
65 行
2.1 KiB
Python
65 行
2.1 KiB
Python
import re
|
|
import tomllib
|
|
from pathlib import Path
|
|
from urllib.parse import unquote, urlsplit
|
|
|
|
|
|
def test_architecture_document_exists() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
|
|
assert (repo_root / "ARCHITECTURE.md").is_file()
|
|
|
|
|
|
def test_architecture_document_relative_links_resolve() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
architecture = repo_root / "ARCHITECTURE.md"
|
|
text = architecture.read_text(encoding="utf-8")
|
|
|
|
missing: list[str] = []
|
|
for match in re.finditer(r"(?<!!)\[[^\]]+\]\(([^)]+)\)", text):
|
|
raw_target = match.group(1).strip()
|
|
target = raw_target.split("#", 1)[0]
|
|
if not target or urlsplit(target).scheme:
|
|
continue
|
|
if not (repo_root / unquote(target)).exists():
|
|
missing.append(raw_target)
|
|
|
|
assert missing == []
|
|
|
|
|
|
def test_root_env_example_is_the_single_template_source() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
root_example = repo_root / ".env.example"
|
|
duplicate_example = (
|
|
repo_root / "src" / "free_claude_code" / "config" / "env.example"
|
|
)
|
|
|
|
assert root_example.is_file()
|
|
assert not duplicate_example.exists()
|
|
|
|
|
|
def test_root_env_example_is_packaged_for_config_template_loader() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
pyproject = tomllib.loads((repo_root / "pyproject.toml").read_text("utf-8"))
|
|
|
|
force_include = pyproject["tool"]["hatch"]["build"]["targets"]["wheel"][
|
|
"force-include"
|
|
]
|
|
|
|
assert force_include[".env.example"] == "free_claude_code/config/env.example"
|
|
|
|
|
|
def test_pyproject_first_party_packages_match_packaged_roots() -> None:
|
|
repo_root = Path(__file__).resolve().parents[2]
|
|
pyproject = (repo_root / "pyproject.toml").read_text(encoding="utf-8")
|
|
match = re.search(r"known-first-party = \[(?P<items>[^\]]+)\]", pyproject)
|
|
|
|
assert match is not None
|
|
configured = {
|
|
item.strip().strip('"')
|
|
for item in match.group("items").split(",")
|
|
if item.strip()
|
|
}
|
|
expected = {"free_claude_code", "smoke"}
|
|
assert configured == expected
|