#!/usr/bin/env python3 """Prepare selected fixed sources and run explicitly local component checks.""" import argparse import os from pathlib import Path import subprocess import sys from workspace.cli import Workspace, process_environment from workspace.common import WorkspaceError from workspace.state import State SINGLE = {"test-backend": "Agent-System-Backend", "test-backend-pg": "Agent-System-Backend", "test-core": "Agent-System-Core", "test-agentcmd": "Agent-Command", "test-frontend": "Agent-System-Frontend", "test-ui": "Agent-System-UI", "test-multi-instance": "Agent-System-Backend", "eval-deliverables": "Agent-System-Backend", "dev-backend": "Agent-System-Backend", "dev-frontend": "Agent-System-Frontend"} def run(command, cwd, env): print("[component-check] " + " ".join(map(str, command)), flush=True) result = subprocess.run(command, cwd=cwd, env=env) if result.returncode: raise WorkspaceError(f"local component command failed ({result.returncode})") def prepare_local_source(workspace, component): """Repeated local npm checks may reuse only recognized generated directories.""" record = workspace.state.data["components"].get(component["id"]) path = workspace.root / component["path"] if record and record.get("mode") == "versioned" and (path / "package.json").is_file(): actual = workspace.git.safe(path, component["repo"], clean=False) commit, source = workspace.baseline(component) generated = ("node_modules/", "dist/") if actual["head"] != commit or actual["branch"] or actual["changes"] or any(not name.startswith(generated) for name in actual["ignored"]): raise WorkspaceError(f"local source or ignored unique files require owner coordination: {component['id']}") record.update(baseline=commit, source=source) workspace.state.save() return workspace.prepare_one(component) def check(root, target, overlay): root = root.resolve() if target in ("test-backend-pg", "test-multi-instance") and not os.environ.get("AGENT_PG_TEST_DSN"): raise WorkspaceError("AGENT_PG_TEST_DSN required; real PostgreSQL gate is not skipped") if target == "test-backend-pg" and not os.environ.get("AGENT_REDIS_TEST_ADDR"): raise WorkspaceError("AGENT_REDIS_TEST_ADDR required; real Redis gate is not skipped") workspace = Workspace(root) names = [SINGLE[target]] if target in SINGLE else (["Agent-Native", "Agent-Intent", "Agent-Squad"] if target == "test-libs" else ["Agent-System-Backend", "Agent-System-Frontend"]) selected = [workspace.manifest.select(name)[0] for name in names] with workspace.state.operation(): workspace.state = State(root) if any(component["role"] in ("go-library", "npm-library") for component in selected): for component in workspace.manifest.select(fixed=True): if component["role"] == "application": prepare_local_source(workspace, component) for component in selected: prepare_local_source(workspace, component) workspace.snapshot() env = process_environment(root) if overlay: workspace.go.check_owned() if not workspace.state.data.get("go_work") or not (root / "go.work").is_file(): raise WorkspaceError("LOCAL_OVERLAY=1 requires a registered go.work; use component-edit first") env["GOWORK"] = str(root / "go.work") else: env["GOWORK"] = "off" print(f"[component-check] local developer sources; GOWORK={env['GOWORK']}; registered edit branches and unsaved code retained", flush=True) paths = {component["id"]: root / component["path"] for component in selected} if target == "dev": print("Backend: make dev-backend [LOCAL_OVERLAY=1]\nFrontend: make dev-frontend\nPrivate module transport is scoped to those processes.") return path = paths[names[0]] if target == "dev-backend": run(["go", "run", "./cmd/agent-system", "serve", "--addr", "127.0.0.1:8787", "--data", str(root / "data"), "--plugins", str(root / "plugins")], path, env) elif target == "dev-frontend": run(["npm", "ci", "--ignore-scripts", "--no-fund", "--no-audit"], path, env) run(["npm", "run", "dev"], path, env) elif target == "test-multi-instance": suite = root / "tests/integration/multi-instance" binary = suite / ".build/agent-system" binary.parent.mkdir(parents=True, exist_ok=True) run(["go", "build", "-mod=readonly", "-o", str(binary), "./cmd/agent-system"], path, env | {"CGO_ENABLED": "0"}) run(["go", "test", "-mod=readonly", "-count=1", "-timeout=15m", "."], suite, env | {"AGENT_BIN": str(binary)}) elif target == "eval-deliverables": run(["go", "test", "-mod=readonly", "./eval/", "./internal/quality/", "-count=1"], path, env) elif target in ("test-frontend", "test-ui"): run(["npm", "ci", "--ignore-scripts", "--no-fund", "--no-audit"], path, env) if target == "test-ui": run(["npm", "exec", "--offline", "--", "tsc", "--noEmit"], path, env) run(["npm", "exec", "--offline", "--", "vitest", "run"], path, env) run(["node", "scripts/check-knip.mjs"], path, env) else: for component in selected: source = paths[component["id"]] modules = component.get("modules", []) if target == "test-libs" else [{"directory": "."}] for module in modules: directory = source / module.get("directory", ".") if target in ("test-libs", "test-agentcmd"): run(["go", "vet", "-mod=readonly", "./..."], directory, env) run(["go", "test", "-mod=readonly", "-count=1", "./..."], directory, env) if __name__ == "__main__": parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parents[1]) parser.add_argument("--target", choices=(*SINGLE, "test-libs", "dev"), required=True) parser.add_argument("--overlay", action="store_true") args = parser.parse_args() try: check(args.root, args.target, args.overlay) except (WorkspaceError, OSError, ValueError) as error: print(f"component-check: {error}", file=sys.stderr) raise SystemExit(1)