feat: add production Core runtime modes

This commit is contained in:
thanhnv
2026-07-24 12:16:11 +07:00
parent e359989a74
commit eb3525456f
13 changed files with 659 additions and 397 deletions
+221 -30
View File
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""`casan init` / `casan verify-harness` (Plan-21 hybrid adoption).
"""`casan init` / `casan verify-harness` (Plan-21 production adoption).
Adopt CASAN into an EXISTING project by writing only per-project config — the
shared harness stays under $CASAN_HOME and is NOT copied into the repo. This is
the codegraph-style flow: global install once, then `casan init` per project.
Adopt CASAN into an EXISTING project using an explicit runtime contract:
managed mode pins the shared Core under $CASAN_HOME; vendored mode installs the
same production-only Core under `.casan/runtime/casan-core`.
What init writes into the target repo:
.casan/config.json project id, enforcement/integration mode, clients
@@ -13,6 +13,8 @@ What init writes into the target repo:
.specify/ runtime state root marker (logs/traces/admissions)
.claude/settings.json Plan-20 Claude Code hooks (--client claude|all)
.codex/hooks.json+config Plan-20 Codex hooks (--client codex|all)
.casan/runtime/casan-core
optional self-contained Core (--runtime vendored)
`verify` recomputes the resolved harness gate-code hash and compares it to
version.lock — the pin+VERIFY half. Drift/tamper of the global harness relative
@@ -107,6 +109,8 @@ def _render_init(result):
("Location", result["target"]),
("Level", "%s (%s)" % (
result["target_level_name"].capitalize(), result["target_level"])),
("Runtime", "%s — %s" % (
result["runtime_mode"].capitalize(), result["runtime_path"])),
("Mode", result["enforcement_mode"]),
("Clients", _client_names(result["clients"])),
("Files", "%d created or updated" % len(result["created"])),
@@ -149,6 +153,10 @@ def _render_level(result):
result.get("project_target_level_name"),
result.get("project_target_level")))
if result.get("project_target_level") else "not initialized"),
("Runtime", ("%s — %s" % (
result.get("project_runtime_mode"),
result.get("project_runtime_path")))
if result.get("project_target_level") else "not initialized"),
("Status", str(result.get("project_level_status") or "unknown")),
])
print()
@@ -270,7 +278,10 @@ def _copy_tree_missing(src_dir, dst_dir, target, created):
def resolve_harness(explicit):
for cand in (explicit, os.environ.get("CASAN_HARNESS_ROOT")):
for cand in (
explicit,
os.environ.get("CASAN_HARNESS_ROOT"),
os.environ.get("CASAN_GLOBAL_HARNESS_ROOT")):
if cand and os.path.isdir(os.path.join(cand, "scripts", "bash")):
return os.path.abspath(cand)
install = os.environ.get("CASAN_INSTALL_ROOT")
@@ -290,8 +301,9 @@ def install_root(harness):
def harness_version(harness):
for p in (os.path.join(install_root(harness), "VERSION"),
os.path.join(harness, "..", "..", "VERSION")):
local_root = os.path.abspath(os.path.join(harness, "..", ".."))
for p in (os.path.join(local_root, "VERSION"),
os.path.join(install_root(harness), "VERSION")):
try:
with open(p, "r", encoding="utf-8") as fh:
v = fh.read().strip()
@@ -317,17 +329,38 @@ def compute_harness_hash(harness):
"""For PINNING at init: use the value recorded at install time if present
(it equals a live compute of the same files), else compute live. Verify must
NOT use this — it must call compute_live() to detect drift."""
recorded = os.path.join(install_root(harness), ".harness-hash")
try:
with open(recorded, "r", encoding="utf-8") as fh:
v = fh.read().strip()
if v:
return v, "recorded"
except (OSError, IOError):
pass
local_root = os.path.abspath(os.path.join(harness, "..", ".."))
for recorded in (
os.path.join(local_root, ".harness-hash"),
os.path.join(install_root(harness), ".harness-hash")):
try:
with open(recorded, "r", encoding="utf-8") as fh:
v = fh.read().strip()
if v:
return v, "recorded"
except (OSError, IOError):
continue
return compute_live(harness)
def resolve_project_harness(target, explicit=None):
lock = _load_json_or(
os.path.join(target, ".casan", "version.lock"), {})
runtime_path = lock.get("runtime_path")
if lock.get("runtime_mode") == "vendored" and isinstance(runtime_path, str):
root = (
None if os.path.isabs(runtime_path)
else _safe_project_path(target, runtime_path)
)
if not root:
return None
candidate = os.path.join(root, "packages", "casan-harness")
if os.path.isdir(os.path.join(candidate, "scripts", "bash")):
return os.path.abspath(candidate)
return None
return resolve_harness(explicit)
def _write(path, text, backups):
with owner_writable(path):
if os.path.exists(path):
@@ -366,8 +399,10 @@ def _safe_project_path(target, relative):
if not normalized or os.path.isabs(str(relative)):
return None
candidate = os.path.abspath(os.path.join(target, *normalized.split("/")))
target_real = os.path.realpath(target)
candidate_real = os.path.realpath(candidate)
try:
if os.path.commonpath((target, candidate)) != target:
if os.path.commonpath((target_real, candidate_real)) != target_real:
return None
except ValueError:
return None
@@ -387,6 +422,110 @@ def _file_sha256(path):
return digest.hexdigest()
def _install_vendored_core(target, source_harness):
"""Atomically install the production Core runtime inside one project."""
source_root = os.path.abspath(os.path.join(source_harness, "..", ".."))
copier = os.path.join(source_root, "scripts", "copy-runtime.py")
source_bin = os.path.join(source_root, "bin", "casan")
source_version = os.path.join(source_root, "VERSION")
for required in (source_bin, source_version):
if not os.path.isfile(required):
raise RuntimeError(
"vendored runtime source is incomplete; missing %s" % required)
destination = _safe_project_path(
target, ".casan/runtime/casan-core")
if not destination:
raise RuntimeError(
"vendored runtime path escapes the project through a symlink")
parent = os.path.dirname(destination)
os.makedirs(parent, exist_ok=True)
staging = tempfile.mkdtemp(prefix=".casan-core-", dir=parent)
try:
if os.path.isfile(copier):
result = subprocess.run(
[
sys.executable,
copier,
"--source-root", source_root,
"--destination-root", staging,
"--component", "harness",
"--clean",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
check=False,
)
if result.returncode != 0:
raise RuntimeError(
result.stderr.strip() or result.stdout.strip() or
"production runtime copy failed")
else:
shutil.copytree(
source_harness,
os.path.join(staging, "packages", "casan-harness"))
vendored_harness = os.path.join(
staging, "packages", "casan-harness")
forbidden = [
relative for relative in (
"tests",
"level5",
"scripts/bash/ci-harness-gate.sh",
"scripts/bash/security-gate.sh",
"scripts/bash/dashboard-server.py",
)
if os.path.exists(os.path.join(
vendored_harness, *relative.split("/")))
]
if forbidden:
raise RuntimeError(
"source is not a production runtime; forbidden paths: %s" %
", ".join(forbidden))
os.makedirs(os.path.join(staging, "bin"), exist_ok=True)
shutil.copy2(source_bin, os.path.join(staging, "bin", "casan"))
shutil.copy2(source_version, os.path.join(staging, "VERSION"))
with open(os.path.join(staging, ".casan-level"), "w",
encoding="utf-8") as fh:
fh.write("core\n")
vendored_hash, hash_source = compute_live(vendored_harness)
if hash_source == "error":
raise RuntimeError(
"cannot verify staged vendored Core: %s" % vendored_hash)
with open(os.path.join(staging, ".harness-hash"), "w",
encoding="utf-8") as fh:
fh.write(vendored_hash + "\n")
if os.path.isdir(destination) and not os.path.islink(destination):
shutil.rmtree(destination)
elif os.path.exists(destination) or os.path.islink(destination):
os.unlink(destination)
os.replace(staging, destination)
staging = None
finally:
if staging and os.path.isdir(staging):
shutil.rmtree(staging)
file_count = sum(
len(files) for _root, _dirs, files in os.walk(destination))
return destination, file_count
def _remove_vendored_core(target):
destination = _safe_project_path(
target, ".casan/runtime/casan-core")
if not destination:
return False
if not os.path.exists(destination) and not os.path.islink(destination):
return False
with owner_writable(destination):
if os.path.isdir(destination) and not os.path.islink(destination):
shutil.rmtree(destination)
else:
os.unlink(destination)
_prune_empty_parents(destination, target)
return True
def _backup_once(path, backups):
if backups is None:
return
@@ -831,8 +970,38 @@ def cmd_init(args):
except ValueError as error:
sys.stderr.write("casan init: %s\n" % error)
return 64
version = harness_version(harness)
hhash, hsource = compute_harness_hash(harness)
previous_config = _load_json_or(
os.path.join(target, ".casan", "config.json"), {})
previous_runtime_mode = previous_config.get("runtime_mode")
runtime_mode = (
args.runtime or
(previous_runtime_mode
if previous_runtime_mode in ("managed", "vendored") else "managed")
)
runtime_removed = False
runtime_files = 0
if runtime_mode == "vendored":
try:
runtime_root, runtime_files = _install_vendored_core(
target, harness)
except (OSError, RuntimeError) as error:
sys.stderr.write(
"casan init: cannot install vendored Core runtime: %s\n" %
error)
return 1
active_harness = os.path.join(
runtime_root, "packages", "casan-harness")
runtime_path = os.path.relpath(
runtime_root, target).replace(os.sep, "/")
else:
runtime_removed = _remove_vendored_core(target)
active_harness = harness
runtime_root = install_root(harness)
runtime_path = runtime_root
version = harness_version(active_harness)
hhash, hsource = compute_harness_hash(active_harness)
created = []
backups = []
previous_manifest = _load_json_or(
@@ -889,7 +1058,11 @@ def cmd_init(args):
"vscode-native-copilot": "unsupported_global_interception",
},
"harness_version": version,
"adoption_model": "hybrid-global",
"adoption_model": (
"managed-global" if runtime_mode == "managed"
else "vendored-project"),
"runtime_mode": runtime_mode,
"runtime_path": runtime_path,
"target_level": lvl,
"target_level_name": lvl_name,
}
@@ -903,7 +1076,9 @@ def cmd_init(args):
"harness_hash": hhash,
"hash_algo": "sha256",
"hash_source": hsource,
"install_root": install_root(harness),
"install_root": runtime_root,
"runtime_mode": runtime_mode,
"runtime_path": runtime_path,
"recorded_at": now_iso(),
}
p = os.path.join(cfg_dir, "version.lock")
@@ -924,7 +1099,7 @@ def cmd_init(args):
_write(p, "\n".join(env_lines), backups); created_add(p)
# Stable project-local bootstrap. It loads the config above, resolves the
# global harness and verifies version.lock before dispatching an adapter.
# managed/vendored Core and verifies version.lock before dispatching.
bootstrap_source = os.path.join(devkit_root(), "templates", "project",
"casan-hook.py")
bootstrap_target = os.path.join(cfg_dir, "casan-hook.py")
@@ -1067,6 +1242,10 @@ def cmd_init(args):
"target_level_name": lvl_name,
"harness_version": version,
"harness_hash": hhash,
"runtime_mode": runtime_mode,
"runtime_path": runtime_path,
"runtime_files": runtime_files,
"runtime_removed": runtime_removed,
"enforcement_mode": args.mode,
"clients": clients,
"created": created,
@@ -1076,8 +1255,11 @@ def cmd_init(args):
"level_extras": level_extras,
"level_removed": level_removed,
"level_retained": level_retained,
"note": ("harness NOT copied into repo (hybrid model); selected client hooks "
"MERGED and unselected CASAN hooks removed; run `casan doctor`"),
"note": (
"managed runtime is referenced by version/hash lock"
if runtime_mode == "managed" else
"production-only Core runtime vendored under .casan/runtime/casan-core"
),
}
_emit_json_or_human(args, result, _render_init)
if legacy_migration.get("manual_review"):
@@ -1096,11 +1278,11 @@ def cmd_init(args):
def cmd_verify(args):
harness = resolve_harness(args.harness)
if not harness:
sys.stderr.write("casan verify-harness: cannot locate the harness.\n")
return 1
target = os.path.abspath(args.target or os.getcwd())
harness = resolve_project_harness(target, args.harness)
if not harness:
sys.stderr.write("casan verify-harness: cannot locate the project runtime.\n")
return 1
lock_path = os.path.join(target, ".casan", "version.lock")
if not os.path.exists(lock_path):
sys.stderr.write("casan verify-harness: no .casan/version.lock (run `casan init` first).\n")
@@ -1120,8 +1302,8 @@ def cmd_verify(args):
}
_emit_json_or_human(args, result, _render_verify)
if not ok:
sys.stderr.write("HARNESS_INTEGRITY_DRIFT — the resolved harness does not match the "
"project pin. The global harness changed or was tampered.\n")
sys.stderr.write("HARNESS_INTEGRITY_DRIFT — the resolved Core runtime does not match "
"the project pin. The runtime changed or was tampered.\n")
return 3
return 0
@@ -1144,6 +1326,8 @@ def cmd_level(args):
"installed_level": installed,
"project_target_level": tl,
"project_target_level_name": cfg.get("target_level_name"),
"project_runtime_mode": cfg.get("runtime_mode", "managed"),
"project_runtime_path": cfg.get("runtime_path"),
"project_level_status": status_map.get(tl, "unknown") if tl else None,
"levels": {
"1 core": "implemented — harness + gates + CLI",
@@ -1217,7 +1401,7 @@ def cmd_doctor(args):
else:
clients = config.get("clients") or []
harness = resolve_harness(args.harness)
harness = resolve_project_harness(target, args.harness)
lock = _load_json_or(os.path.join(target, ".casan", "version.lock"), {})
expected = lock.get("harness_hash")
actual, source = compute_live(harness) if harness else ("unavailable", "error")
@@ -1502,6 +1686,8 @@ def cmd_uninstall(args):
removed, retained = _remove_owned_project_artifacts(
target, owned_files, owned_file_hashes)
if _remove_vendored_core(target):
removed.append(".casan/runtime/casan-core")
for relative in (
".casan/config.json",
@@ -1567,6 +1753,11 @@ def main(argv=None):
"(default auto: install when `code` is available)"))
pi.add_argument("--level", default="core",
help="packaging level to adopt: 1|core (default), 2|devkit, 3|platform (preview), 4|enterprise (refused)")
pi.add_argument(
"--runtime", choices=["managed", "vendored"],
help=("Core runtime placement: managed uses the pinned global install "
"(default for new adoption); vendored copies a production-only "
"Core into the project; re-init preserves the current mode"))
pi.add_argument("--mode", choices=["observe", "enforce"], default="enforce",
help="agentic policy mode (default: enforce; use observe for a telemetry-only pilot)")
pi.add_argument("--integration-mode", dest="integration_mode",