feat: complete Epic 1 production foundation

This commit is contained in:
2026-08-25 01:24:11 +03:00
parent 767428436d
commit 182bde8ac0
298 changed files with 35719 additions and 5299 deletions
+35
View File
@@ -100,6 +100,25 @@ python3 scripts/check-config-boundaries.py --root .
committed machine contract. PostgreSQL DDL вне `crank-registry::migrations`
блокируется Rust module boundary check.
Операторские auth-команды живут в том же `crank-migrate` binary и используют
database-only config projection:
```bash
crank-migrate admin-auth bootstrap-create --email owner@example.local
crank-migrate admin-auth recover \
--email owner@example.local \
--password-file /secure/new-admin-password.txt \
--password-pepper-file /secure/password-pepper.txt \
--master-key-file /secure/current-master.key
```
Команда создаёт одноразовый local bootstrap contract и выводит token для UI
`/login`; static startup password не является production bootstrap authority.
Recovery проверяет active master-key identity, заменяет verifier существующего
Admin account, отзывает browser sessions и не выводит password, pepper,
master key или Secret material.
## Typed metrics contract
`just metrics-contract-check` сверяет Rust registry с versioned JSON snapshot и
@@ -107,6 +126,22 @@ committed machine contract. PostgreSQL DDL вне `crank-registry::migrations`
Checker распознаёт прямые macro calls, imports, aliases и re-exports; новые
Rust-файлы можно передать явно через `--files`.
## Unified execution boundary
`check-execution-boundaries.py` запрещает Admin/MCP route и service слоям
обходить typed `RuntimeExecutionRequest`: напрямую импортировать REST adapter,
Reqwest, legacy `execute_with_*` helpers или запускать execution через
persistence. Проверка видит tracked и untracked файлы, а explicit mode
fail-closed отклоняет missing, symlink и path escape:
```bash
python3 scripts/check-execution-boundaries.py --root .
python3 scripts/check-execution-boundaries.py --root . --files <paths...>
```
Canonical composition roots создают adapters, но продуктовые Admin Draft Test
и MCP snapshot calls проходят только через единый runtime outcome contract.
## `check-community-scope.sh`
Проверяет, что в community-репозиторий не попали функции и тексты за пределами
+77 -1
View File
@@ -276,6 +276,17 @@ def run_operation_test(
raise safe_error("operation_test", "outcome_not_ok")
def operation_etag(client: Client, workspace_id: str, operation_id: str) -> str:
response = client.request_json(
"GET",
admin_path(workspace_id, f"/operations/{operation_id}"),
)
etag = response.headers.get("ETag") if response.headers is not None else None
if not isinstance(etag, str) or len(etag) > 128 or not etag.startswith('"') or not etag.endswith('"'):
raise safe_error("operation_precondition", "invalid_response")
return etag
def publish_operation(
client: Client,
workspace_id: str,
@@ -286,6 +297,7 @@ def publish_operation(
"POST",
admin_path(workspace_id, f"/operations/{operation_id}/publish"),
{"version": operation_version},
headers={"If-Match": operation_etag(client, workspace_id, operation_id)},
).body
try:
published_version = int(published["published_version"])
@@ -314,6 +326,35 @@ def create_agent(client: Client, workspace_id: str, agent_slug: str) -> tuple[st
raise safe_error("agent_create", "invalid_response") from error
def edit_and_archive_operation(
client: Client,
workspace_id: str,
operation_id: str,
operation_name: str,
internal_upstream: str,
) -> int:
payload = build_operation_payload(operation_name, internal_upstream)
payload.pop("name", None)
payload.pop("protocol", None)
payload["display_name"] = "Internal Health Smoke Edited Draft"
updated = client.request_json(
"PATCH",
admin_path(workspace_id, f"/operations/{operation_id}"),
payload,
headers={"If-Match": operation_etag(client, workspace_id, operation_id)},
).body
try:
draft_version = int(updated["version"])
except (KeyError, TypeError, ValueError) as error:
raise safe_error("operation_edit", "invalid_response") from error
client.request_json(
"POST",
admin_path(workspace_id, f"/operations/{operation_id}/archive"),
headers={"If-Match": operation_etag(client, workspace_id, operation_id)},
)
return draft_version
def bind_and_publish_agent(
client: Client,
workspace_id: str,
@@ -407,6 +448,7 @@ def cleanup_smoke_assets(
client.request_json(
"DELETE",
admin_path(workspace_id, f"/operations/{operation_id}"),
headers={"If-Match": operation_etag(client, workspace_id, operation_id)},
expected=(200, 404),
)
except SmokeError:
@@ -414,6 +456,7 @@ def cleanup_smoke_assets(
client.request_json(
"POST",
admin_path(workspace_id, f"/operations/{operation_id}/archive"),
headers={"If-Match": operation_etag(client, workspace_id, operation_id)},
expected=(200, 404),
)
except SmokeError as error:
@@ -524,7 +567,15 @@ def build_safe_summary(
"command_id": "authenticated-product-smoke",
"operation_id": operation_id[:128],
"operation_version": operation_version,
"stages": ["admin_test", "operation_publish", "agent_publish", "mcp_list", "mcp_call"],
"stages": [
"admin_test",
"operation_publish",
"agent_publish",
"mcp_list",
"mcp_call",
"operation_edit_archive",
"pinned_mcp_list_call",
],
"verdict": "pass",
}
@@ -614,6 +665,31 @@ def run(args: argparse.Namespace) -> None:
)
validate_tool_call_result(result)
print("tools/call: ok")
edited_draft_version = edit_and_archive_operation(
client,
workspace_id,
operation_id,
operation_name,
args.internal_upstream,
)
print(f"operation edited and archived: draft_version={edited_draft_version}")
tools = call_mcp(
client,
mcp_url,
api_key,
session_id,
{"jsonrpc": "2.0", "id": 4, "method": "tools/list", "params": {}},
)
validate_tools_list(tools, operation_name)
pinned_result = call_mcp(
client,
mcp_url,
api_key,
session_id,
tools_call_payload(operation_name, {"probe": "ok"}),
)
validate_tool_call_result(pinned_result)
print("pinned tools/list and tools/call after Operation archive: ok")
summary = build_safe_summary(
operation_id, published_operation_version, agent_id, published_agent_version
)
+133
View File
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import re
import subprocess
import sys
from pathlib import Path
MAX_FILE_BYTES = 2 * 1024 * 1024
FORBIDDEN = (
(re.compile(r"\bcrank_adapter_rest\b|\bRestAdapterError\b"), "direct REST adapter dependency"),
(re.compile(r"\breqwest\b"), "direct HTTP client dependency"),
(
re.compile(r"\.execute_with_(?:auth|context|auth_and_context)\s*\("),
"legacy runtime execution bypass",
),
(
re.compile(r"\.(?:execute_request|prepare_request|invoke_unary)\s*\("),
"raw execution pipeline bypass",
),
(
re.compile(r"\bpub\s+use\s+[^;]*(?:RuntimeExecutor|ProtocolAdapter)\b"),
"execution boundary re-export",
),
(re.compile(r"\bsqlx\s*::"), "direct execution persistence dependency"),
(
re.compile(r"\.(?:validate_shape|apply_mapping|apply)\s*\("),
"direct execution validation or mapping bypass",
),
)
SCOPED_PREFIXES = (
"apps/admin-api/src/routes/",
"apps/admin-api/src/service/",
"apps/admin-api/src/service.rs",
)
SCOPED_MCP_FILES = {
"crates/crank-community-mcp/src/app.rs",
"crates/crank-community-mcp/src/approval_execution.rs",
"crates/crank-community-mcp/src/tool_error.rs",
"crates/crank-community-mcp/src/app/invocation_history.rs",
"crates/crank-community-mcp/src/app/tool_resolution.rs",
"crates/crank-community-mcp/src/tool_search.rs",
}
SCOPED_MCP_PREFIXES = ("crates/crank-community-mcp/src/app/",)
def parse_args(argv: list[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Check the canonical execution boundary.")
parser.add_argument("--root", type=Path, default=Path.cwd())
parser.add_argument("--files", nargs="*")
return parser.parse_args(argv)
def candidate_paths(root: Path, explicit: list[str] | None) -> list[Path]:
if explicit is None:
result = subprocess.run(
["git", "ls-files", "--cached", "--others", "--exclude-standard", "-z"],
cwd=root,
check=True,
stdout=subprocess.PIPE,
)
names = [name for name in result.stdout.decode("utf-8").split("\0") if name]
else:
names = explicit
return [root / name for name in sorted(set(names))]
def logical_path(root: Path, path: Path) -> str:
if path.is_absolute():
resolved = path.resolve(strict=False)
else:
resolved = (root / path).resolve(strict=False)
try:
return resolved.relative_to(root.resolve()).as_posix()
except ValueError as error:
raise ValueError("path escapes repository root") from error
def main(argv: list[str]) -> int:
args = parse_args(argv)
root = args.root.resolve()
failures: list[str] = []
try:
paths = candidate_paths(root, args.files)
except (OSError, subprocess.SubprocessError, UnicodeError) as error:
print(f"error: execution boundary input unavailable ({type(error).__name__})", file=sys.stderr)
return 1
for supplied in paths:
if supplied.is_symlink():
failures.append("INVALID_PATH")
continue
try:
logical = logical_path(root, supplied)
except ValueError:
failures.append("INVALID_PATH")
continue
if (
not logical.endswith(".rs")
or not (
logical.startswith(SCOPED_PREFIXES)
or logical.startswith(SCOPED_MCP_PREFIXES)
or logical in SCOPED_MCP_FILES
)
):
continue
path = root / logical
if path.is_symlink() or not path.is_file():
failures.append(f"INVALID_PATH {logical[:256]}")
continue
if path.stat().st_size > MAX_FILE_BYTES:
failures.append(f"INPUT_TOO_LARGE {logical[:256]}")
continue
try:
source = path.read_text(encoding="utf-8")
except (OSError, UnicodeError):
failures.append(f"INVALID_SOURCE {logical[:256]}")
continue
for pattern, reason in FORBIDDEN:
if pattern.search(source):
failures.append(f"EXECUTION_BOUNDARY {logical[:256]} {reason}")
if failures:
for failure in sorted(failures)[:1000]:
print(f"error: {failure}", file=sys.stderr)
return 1
print("Execution boundary check passed")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
+9
View File
@@ -209,6 +209,14 @@ def validate_runtime_reference(
raise ContractError(f"divergent Compose inline default: {name}")
def compose_optional(specification: dict[str, object]) -> bool:
compatibility = specification.get("compatibility")
return (
isinstance(compatibility, str)
and "deprecated startup-bootstrap password" in compatibility
)
def validate(root: Path) -> None:
effective, specifications, deployment = load_contract(root)
declared_values: dict[str, set[str]] = {name: set() for name in effective}
@@ -235,6 +243,7 @@ def validate(root: Path) -> None:
name
for name in effective
if specifications[name]["process"] in {"shared", "admin_api"}
and not compose_optional(specifications[name])
}
required_mcp = {
name
+1
View File
@@ -23,6 +23,7 @@ echo "Rust module boundary check: checking module-level imports"
python3 "$ROOT_DIR/scripts/check-config-boundaries.py" --root "$ROOT_DIR" || status=1
python3 "$ROOT_DIR/scripts/check-migration-boundaries.py" --root "$ROOT_DIR" || status=1
python3 "$ROOT_DIR/scripts/check-metrics-boundaries.py" --root "$ROOT_DIR" || status=1
python3 "$ROOT_DIR/scripts/check-execution-boundaries.py" --root "$ROOT_DIR" || status=1
check_no_match \
"admin-api service modules must not depend on axum HTTP types" \