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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
every OS. Existing Windows lockfiles may retain the old CRLF-domain hash;
keep the lockfile so its `resolved_commit` pins remain intact while the
automatic one-time repair tracked in #2628 lands. (closes #2619)
- `apm compile` now reduces matching work for literal scoped `applyTo` patterns
in large repositories while preserving historical placement. It shares a
source inventory across discovery and placement while preserving cleanup
behavior, and preserves commas in character classes. (#2595)
- Hook commands such as `"${CLAUDE_PLUGIN_ROOT}"/hooks/probe.py` now rewrite to
`"${CLAUDE_PLUGIN_ROOT}/hooks/probe.py"` and warn when a supported plugin-root
placeholder remains unresolved instead of silently deploying a dead hook.
Expand Down
75 changes: 75 additions & 0 deletions scripts/check_compile_inventory_authority.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Verify compile traversal is owned by the shared inventory."""

from __future__ import annotations

import sys
from pathlib import Path

ROOT = Path(__file__).parents[1]
INVENTORY = ROOT / "src/apm_cli/compilation/inventory.py"
OPTIMIZER = ROOT / "src/apm_cli/compilation/context_optimizer.py"
DISCOVERY = ROOT / "src/apm_cli/primitives/discovery.py"
DISTRIBUTED = ROOT / "src/apm_cli/compilation/distributed_compiler.py"
AGENTS = ROOT / "src/apm_cli/compilation/agents_compiler.py"


def _has_all(source: str, required: tuple[str, ...]) -> bool:
"""Return whether every required contract fragment appears in source."""
return all(fragment in source for fragment in required)


def main() -> int:
"""Return nonzero when compile traversal has a duplicate authority."""
inventory = INVENTORY.read_text(encoding="utf-8")
optimizer = OPTIMIZER.read_text(encoding="utf-8")
discovery = DISCOVERY.read_text(encoding="utf-8")
distributed = DISTRIBUTED.read_text(encoding="utf-8")
agents = AGENTS.read_text(encoding="utf-8")

valid = (
inventory.count("class CompileInventory") == 1
and inventory.count("os.walk(") == 1
and "os.walk(" not in optimizer
and "os.walk(" not in distributed
and _has_all(
optimizer,
(
"from .inventory import CompileInventory",
"inventory = self._inventory or CompileInventory.collect(self.base_dir)",
"inventory.files_under(self._scan_top_level_roots)",
),
)
and _has_all(
discovery,
(
"inventory: CompileInventory | None = None",
"inventory.files_within(base_path)",
),
)
and _has_all(
distributed,
(
"source_inventory: CompileInventory | None = None",
"deploy_inventory: CompileInventory | None = None",
"for directory_path, (relative_path, files) in sorted(cleanup_directories.items()):",
),
)
and _has_all(
agents,
(
"self._source_inventory = CompileInventory.collect(",
"self.source_dir, exclude_patterns=config.exclude",
"source_inventory=self._source_inventory",
"deploy_inventory=self._deploy_inventory",
),
)
)
if valid:
return 0

print("[x] Compile traversal must route through compilation/inventory.py")
return 1


if __name__ == "__main__":
sys.exit(main())
25 changes: 10 additions & 15 deletions scripts/lint-architecture-boundaries.sh
Original file line number Diff line number Diff line change
Expand Up @@ -195,20 +195,8 @@ if [ -n "$compiled_write_hits" ]; then
violations=$((violations + 1))
fi
distributed_compiler="src/apm_cli/compilation/distributed_compiler.py"
nested_worktree_walk_count=$(grep -Fc \
'for directory, child_dirs, files in os.walk(self.base_dir, followlinks=False):' \
"$distributed_compiler" || true)
nested_worktree_boundary_count=$(grep -Fc \
'(directory_path / ".git").is_file()' \
"$distributed_compiler" || true)
nested_worktree_prune_count=$(grep -Fc 'child_dirs.clear()' "$distributed_compiler" || true)
nested_worktree_rglob_hits=$(grep -En 'rglob\("AGENTS\.md"\)' "$distributed_compiler" || true)
if [ "$nested_worktree_walk_count" -ne 1 ] \
|| [ "$nested_worktree_boundary_count" -ne 1 ] \
|| [ "$nested_worktree_prune_count" -ne 1 ] \
|| [ -n "$nested_worktree_rglob_hits" ]; then
echo "[x] Nested worktree cleanup must prune .git-file roots"
[ -n "$nested_worktree_rglob_hits" ] && echo "$nested_worktree_rglob_hits"
if ! python3 scripts/check_compile_inventory_authority.py; then
echo "[x] Compile traversal must use the shared inventory"
violations=$((violations + 1))
fi
agents_source_attribution_output=$(python3 scripts/check_agents_source_attribution_owner.py \
Expand Down Expand Up @@ -1678,20 +1666,27 @@ apply_to_owner="src/apm_cli/utils/patterns.py"
apply_to_normalizer_defs=$(grep -rEc --include='*.py' \
'^def _?normalize_apply_to\(' src/apm_cli \
| awk -F: '{sum += $2} END {print sum + 0}')
apply_to_prefix_defs=$(grep -rEc --include='*.py' \
'^def literal_apply_to_top_level_roots\(' src/apm_cli \
| awk -F: '{sum += $2} END {print sum + 0}')
apply_to_parser="src/apm_cli/primitives/parser.py"
hidden_tool_placement_owner="src/apm_cli/compilation/context_optimizer.py"
hidden_tool_tree_defs=$(grep -rEc --include='*.py' \
'^PLACEMENT_HIDDEN_TOOL_TREES[[:space:]]*=' src/apm_cli \
| awk -F: '{sum += $2} END {print sum + 0}')
if [ "$apply_to_normalizer_defs" -ne 1 ] \
|| ! grep -q '^def normalize_apply_to(' "$apply_to_owner" \
|| [ "$apply_to_prefix_defs" -ne 1 ] \
|| ! grep -q '^def literal_apply_to_top_level_roots(' "$apply_to_owner" \
|| ! grep -q 'from apm_cli.utils.patterns import normalize_apply_to' "$apply_to_parser" \
|| grep -Eq '^def _?normalize_apply_to\(' "$apply_to_parser" \
|| ! grep -q 'normalize_apply_to(metadata.get("applyTo"), default="")' "$apply_to_parser" \
|| [ "$hidden_tool_tree_defs" -ne 1 ] \
|| ! grep -q '^PLACEMENT_HIDDEN_TOOL_TREES = frozenset(' "$hidden_tool_placement_owner" \
|| ! grep -q 'literal_apply_to_top_level_roots(' "$hidden_tool_placement_owner" \
|| grep -q '^ def _targeted_top_level_roots(' "$hidden_tool_placement_owner" \
|| ! grep -q 'not self._is_supported_hidden_tool_root(path)' "$hidden_tool_placement_owner"; then
echo "[x] applyTo normalization must use utils/patterns.py and hidden placement ContextOptimizer"
echo "[x] applyTo parsing must use utils/patterns.py and hidden placement ContextOptimizer"
violations=$((violations + 1))
fi

Expand Down
22 changes: 19 additions & 3 deletions src/apm_cli/compilation/agents_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
from ..primitives.discovery import discover_primitives
from ..primitives.models import Instruction, PrimitiveCollection
from ..utils.path_security import PathTraversalError, ensure_path_within
from ..utils.paths import portable_relpath
from ..utils.paths import portable_relpath, resolve_base_and_source_dirs
from ..version import get_version
from .claude_formatter import CLAUDE_HEADER, ClaudeFormatter
from .constants import BUILD_ID_PLACEHOLDER
from .inventory import CompileInventory
from .link_resolver import resolve_markdown_links, validate_link_targets
from .template_builder import (
TemplateData,
Expand Down Expand Up @@ -336,13 +337,14 @@ def __init__(self, base_dir: str = ".", source_dir: str | None = None):
compile --root`` redirects writes but sources remain in
``$PWD``.
"""
self.base_dir = Path(base_dir)
self.source_dir = Path(source_dir) if source_dir else self.base_dir
self.base_dir, self.source_dir = resolve_base_and_source_dirs(base_dir, source_dir)
self.warnings: list[str] = []
self.errors: list[str] = []
self._logger = None
self._distributed_placement: dict[Path, tuple[Instruction, ...]] | None = None
self._distributed_context_optimizer: ContextOptimizer | None = None
self._source_inventory: CompileInventory | None = None
self._deploy_inventory: CompileInventory | None = None

def _log(self, method: str, message: str, **kwargs):
"""Delegate to logger if available, else no-op."""
Expand Down Expand Up @@ -403,6 +405,14 @@ def compile(
# Placement is valid only for this invocation's primitive snapshot.
self._distributed_placement = None
self._distributed_context_optimizer = None
self._source_inventory = CompileInventory.collect(
self.source_dir, exclude_patterns=config.exclude
)
self._deploy_inventory = (
self._source_inventory
Comment on lines +408 to +412
if self.source_dir == self.base_dir and not config.exclude
else CompileInventory.collect(self.base_dir)
)
Comment on lines +411 to +415

try:
# Use provided primitives or discover them (with dependency support)
Expand All @@ -412,6 +422,7 @@ def compile(
primitives = discover_primitives(
str(self.source_dir),
exclude_patterns=config.exclude,
inventory=self._source_inventory,
)
else:
# Use enhanced discovery with dependencies (Task 4 integration)
Expand All @@ -420,6 +431,7 @@ def compile(
primitives = discover_primitives_with_dependencies(
str(self.source_dir),
exclude_patterns=config.exclude,
inventory=self._source_inventory,
)

# Route to targets based on config.target.
Expand Down Expand Up @@ -555,6 +567,8 @@ def _compile_distributed(
str(self.base_dir),
exclude_patterns=config.exclude,
source_dir=str(self.source_dir),
source_inventory=self._source_inventory,
deploy_inventory=self._deploy_inventory,
)

# Skip instructions in AGENTS.md when they are already deployed to the
Expand Down Expand Up @@ -865,6 +879,8 @@ def _compile_claude_md(
str(self.base_dir),
exclude_patterns=config.exclude,
source_dir=str(self.source_dir),
source_inventory=self._source_inventory,
deploy_inventory=self._deploy_inventory,
)
placement_map = self._get_distributed_placement(
config,
Expand Down
Loading
Loading