From ab3a9a85485154692d22a45e3475c518fa4f9340 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Sat, 1 Aug 2026 13:09:50 +0000 Subject: [PATCH] =?UTF-8?q?verify(P12):=20split-contract-resolver=20?= =?UTF-8?q?=E2=80=94=204-layer=20verify=20PASS=20+=20ship?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VERIFY: structural — 2 modules extracted + re-export shim (G-113); behavioral — 16 tests + CLI + CI PASS; quality — behavior unchanged. ---ci--- project: acdl phase: 12 milestone: v1.16 status: complete phase_role: execution requirements: covered: [REQ-176] partial: [] ---/ci--- --- core/contract_resolver.py | 41 +++++----------------------------- core/contract_resolver_cli.py | 41 ++++++++++++++++++++++++++++++++++ core/decommission_transform.py | 31 +++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 35 deletions(-) create mode 100644 core/contract_resolver_cli.py create mode 100644 core/decommission_transform.py diff --git a/core/contract_resolver.py b/core/contract_resolver.py index 4e88f9b..a53105d 100644 --- a/core/contract_resolver.py +++ b/core/contract_resolver.py @@ -437,24 +437,9 @@ def _namespace_resources(resources, module_name): def decommission_transform(stack_instance): - """REQ-92: Transform a resolved stack instance for decommission. - - Sets all scalable counts to 0 and deletion_protection to false on - every resource. Used by the decommission pipeline mode after the - first step (disable deletion protection) has been applied. - """ - for res in stack_instance.get("resources", []): - if "nfrs" not in res: - res["nfrs"] = {} - res["nfrs"]["deletion_protection"] = False - inputs = res.get("inputs", {}) - if "desired_count" in inputs: - inputs["desired_count"] = 0 - if "min_capacity" in inputs: - inputs["min_capacity"] = 0 - if "max_capacity" in inputs: - inputs["max_capacity"] = 0 - return stack_instance + """REQ-92: re-export from core.decommission_transform (P12, REQ-176).""" + from core.decommission_transform import decommission_transform as _dt + return _dt(stack_instance) def resolve(contract_path, repo_root=None, environment_override=None): @@ -610,20 +595,6 @@ def resolve(contract_path, repo_root=None, environment_override=None): if __name__ == "__main__": - if len(sys.argv) < 3: - print("usage: contract_resolver.py [--environment ]", file=sys.stderr) - sys.exit(2) - contract_path = sys.argv[1] - out_path = sys.argv[2] - env_override = None - if "--environment" in sys.argv: - idx = sys.argv.index("--environment") - if idx + 1 < len(sys.argv): - env_override = sys.argv[idx + 1] - # Also honor the NOVA_ENVIRONMENT_OVERRIDE env var (used by run_platform.sh). - # Dual-read via core/env.py: NOVA_* preferred, ACDL_* fallback until P5. - if env_override is None and env.get_env("ENVIRONMENT_OVERRIDE"): - env_override = env.get_env("ENVIRONMENT_OVERRIDE") - result = resolve(contract_path, environment_override=env_override) - with open(out_path, "w") as fh: - json.dump(result, fh, indent=2) \ No newline at end of file + # P12 (REQ-176): CLI extracted to core/contract_resolver_cli.py. + from core.contract_resolver_cli import main + sys.exit(main()) \ No newline at end of file diff --git a/core/contract_resolver_cli.py b/core/contract_resolver_cli.py new file mode 100644 index 0000000..a89e5d5 --- /dev/null +++ b/core/contract_resolver_cli.py @@ -0,0 +1,41 @@ +"""Nova Contract Resolver CLI — command-line entry point. + +Extracted from core/contract_resolver.py (P12, REQ-176). + +G-113 import direction: this module imports core.contract_resolver (the +re-export shim) for the resolve function. The shim imports the split +modules. Nothing imports this CLI module except direct invocation. +""" +from __future__ import annotations + +import json +import sys + +from core.contract_resolver import resolve +from core import env + + +def main(argv=None): + """CLI: resolve a contract YAML to a Target Stack JSON.""" + argv = argv if argv is not None else sys.argv[1:] + if len(argv) < 2: + print("usage: contract_resolver.py [--environment ", file=sys.stderr) + return 2 + contract_path = argv[0] + out_path = argv[1] + env_override = None + if "--environment" in argv: + idx = argv.index("--environment") + if idx + 1 < len(argv): + env_override = argv[idx + 1] + # Also honor the NOVA_ENVIRONMENT_OVERRIDE env var (used by run_platform.sh). + if env_override is None and env.get_env("ENVIRONMENT_OVERRIDE"): + env_override = env.get_env("ENVIRONMENT_OVERRIDE") + result = resolve(contract_path, environment_override=env_override) + with open(out_path, "w") as fh: + json.dump(result, fh, indent=2) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/core/decommission_transform.py b/core/decommission_transform.py new file mode 100644 index 0000000..a067c86 --- /dev/null +++ b/core/decommission_transform.py @@ -0,0 +1,31 @@ +"""Nova Decommission Transform — zero counts + disable deletion protection (REQ-92). + +Extracted from core/contract_resolver.py (P12, REQ-176). + +G-113 import direction: this module imports only stdlib. The re-export +shim core/contract_resolver.py imports this module. Nothing imports the +shim except external callers. +""" + +from __future__ import annotations + + +def decommission_transform(stack_instance): + """REQ-92: Transform a resolved stack instance for decommission. + + Sets all scalable counts to 0 and deletion_protection to false on + every resource. Used by the decommission pipeline mode after the + first step (disable deletion protection) has been applied. + """ + for res in stack_instance.get("resources", []): + if "nfrs" not in res: + res["nfrs"] = {} + res["nfrs"]["deletion_protection"] = False + inputs = res.get("inputs", {}) + if "desired_count" in inputs: + inputs["desired_count"] = 0 + if "min_capacity" in inputs: + inputs["min_capacity"] = 0 + if "max_capacity" in inputs: + inputs["max_capacity"] = 0 + return stack_instance \ No newline at end of file