fix(cases): expose explicit versioned type roots in Mason CLI

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
tegwick 2026-10-16 07:51:41 +02:00
parent e4761b843c
commit 57411f25b9
4 changed files with 129 additions and 0 deletions

View file

@ -115,3 +115,31 @@ binding/application; mint is removed from its structural readiness blocker.
WARDEN-WP-0044-T03 and NK-WP-0048-T06 retain native follow-through, full current
run digest/machine source binding, verification and first live-case acceptance.
No case/type status, credential, policy, catalog activation or workload was changed.
## Explicit source-root selection — 2026-10-16
`plan`, `build` and `structure-request` accept `--types-root` and pass it to
NetKingdom's existing exact-version loader. The default remains
`<netkingdom-root>/security-case-types`; a requested version never falls forward
or backward. Provider declarations still resolve from the NetKingdom repository's
siblings, independently of the selected type root.
Prepare the acknowledged 1.2 source projection without a live survey:
```bash
ops-mason plan --case ../net-kingdom/security-case-type-revisions/1.2/oidc-browser-relying-party/examples/forgejo.yaml --types-root ../net-kingdom/security-case-type-revisions/1.2 --no-live --allow-unimplemented
```
This actual case is proposed and has no acceptance. Its six-object draft includes
the dedicated consumer ESO role bound to namespace `forgejo` and service account
`forgejo-keycape-forgejo-eso`. Source-root selection grants no acceptance: normal
admission still refuses the acknowledged type; `build` refuses the proposed case,
and `structure-request` refuses the historical 1.1 plan/bundle for it. A current
accepted case and reviewed plan are needed before regenerating a build/request.
Do not repin historical acceptance or relabel an offline plan as reviewed.
Six integration checks exercise these paths against the actual owner validator
and source package, including malformed namespace refusal. This CLI compatibility
change does not supply the owner-native contracts listed above. Machine descriptors,
source receipt authentication, current CCR42 binding and native six-object proof
remain with MASON-WP-0010-T03 / NK-WP-0048-T06 and their existing owners.

View file

@ -53,6 +53,7 @@ def _parser() -> argparse.ArgumentParser:
help="skip the live OpenBao survey (marks the plan's survey source 'none')",
)
plan.add_argument("--netkingdom-root", type=Path, default=None)
plan.add_argument("--types-root", type=Path, help="explicit exact-version SecurityCaseType root")
plan.add_argument(
"--allow-unimplemented",
action="store_true",
@ -74,6 +75,7 @@ def _parser() -> argparse.ArgumentParser:
help="reference case only: build against an acknowledged type",
)
build.add_argument("--netkingdom-root", type=Path, default=None)
build.add_argument("--types-root", type=Path, help="explicit exact-version SecurityCaseType root")
structure = families.add_parser(
"structure-request", help="read-only typed structural evidence request"
)
@ -81,6 +83,7 @@ def _parser() -> argparse.ArgumentParser:
structure.add_argument("--plan", required=True, type=Path)
structure.add_argument("--bundle", required=True, type=Path)
structure.add_argument("--netkingdom-root", type=Path)
structure.add_argument("--types-root", type=Path, help="explicit exact-version SecurityCaseType root")
structure.add_argument("--allow-unimplemented", action="store_true")
return parser
@ -101,6 +104,7 @@ def _build(args) -> int:
args.plan,
out,
nk_root=nk_root,
types_root=args.types_root,
allow_unimplemented=args.allow_unimplemented,
)
except (
@ -134,6 +138,7 @@ def _plan(args) -> int:
doc, collisions, rendered = case_plan.plan_from_case(
args.case,
nk_root=nk_root,
types_root=args.types_root,
structure=case_plan.Structure() if args.no_live else None,
allow_unimplemented=args.allow_unimplemented,
)
@ -171,6 +176,7 @@ def main(argv: Sequence[str] | None = None) -> int:
args.plan,
args.bundle,
nk_root=args.netkingdom_root or case_plan.DEFAULT_NK_ROOT,
types_root=args.types_root,
allow_unimplemented=args.allow_unimplemented,
)
except Exception:

View file

@ -0,0 +1,84 @@
"""Actual NetKingdom 1.2 source selection; no accepted/native case fixture."""
import json
from pathlib import Path
import pytest
import yaml
from ops_mason import case_plan, cli
NK = case_plan.DEFAULT_NK_ROOT
TYPES = NK / "security-case-type-revisions/1.2"
CASE = TYPES / "oidc-browser-relying-party/examples/forgejo.yaml"
pytestmark = pytest.mark.skipif(not CASE.is_file(), reason="NetKingdom 1.2 source absent")
def test_explicit_source_root_renders_actual_six_object_draft(tmp_path, capsys):
output = tmp_path / "plan.md"
assert cli.main(["plan", "--case", str(CASE), "--types-root", str(TYPES),
"--no-live", "--allow-unimplemented", "--out", str(output)]) == 0
plan = output.read_text()
front = yaml.safe_load(plan.split("---\n", 2)[1])
assert front["status"] == "draft"
assert front["case_acceptance"] is None
assert front["derived_from_type"] == "oidc-browser-relying-party@1.2"
assert front["reference_case"] is True
case, doc, rendered = case_plan.render(CASE, types_root=TYPES, allow_unimplemented=True)
assert case["spec"]["status"] == "proposed"
assert "acceptance" not in case["spec"]
assert doc["spec"]["status"] == "acknowledged"
assert len(rendered["materialization"]) == 6
role = next(item for item in rendered["materialization"]
if item["id"] == "consumer-eso-role")
body = yaml.safe_load(role["body"])
assert body["bound_service_account_namespaces"] == ["forgejo"]
assert body["bound_service_account_names"] == ["forgejo-keycape-forgejo-eso"]
assert front["render_digest"] == rendered["digest"]
assert json.loads(capsys.readouterr().err)["ok"] is True
def test_explicit_root_does_not_waive_implemented_gate(tmp_path, capsys):
output = tmp_path / "plan.md"
assert cli.main(["plan", "--case", str(CASE), "--types-root", str(TYPES),
"--no-live", "--out", str(output)]) == 2
assert not output.exists()
assert "only implemented types" in capsys.readouterr().err
def test_missing_explicit_root_never_falls_forward_to_12(tmp_path, capsys):
output = tmp_path / "plan.md"
assert cli.main(["plan", "--case", str(CASE), "--no-live",
"--allow-unimplemented", "--out", str(output)]) == 2
assert not output.exists()
assert "not 1.2" in capsys.readouterr().err
def test_proposed_case_cannot_build_even_with_selected_acknowledged_source(tmp_path, capsys):
output = tmp_path / "bundle"
assert cli.main(["build", "--case", str(CASE), "--types-root", str(TYPES),
"--allow-unimplemented", "--plan", str(tmp_path / "absent-plan.md"),
"--out", str(output)]) == 2
assert not output.exists()
assert "case is not accepted" in capsys.readouterr().err
def test_historical_bundle_cannot_attest_12_inventory(tmp_path, capsys):
owner = Path("/home/worsch/ops-mason")
assert cli.main(["structure-request", "--case", str(CASE), "--types-root", str(TYPES),
"--allow-unimplemented", "--plan", str(owner / "plans/case-forgejo-netkingdom-oidc.md"),
"--bundle", str(owner / "builds/case-forgejo-netkingdom-oidc")]) == 2
captured = capsys.readouterr()
assert captured.out == ""
assert json.loads(captured.err)["error"] == "structural-source-preparation-refused"
def test_actual_validator_refuses_invalid_consumer_namespace(tmp_path, capsys):
case = yaml.safe_load(CASE.read_text())
case["spec"]["parameters"]["consumer_namespace"] = "../../other"
source = tmp_path / "case.yaml"
source.write_text(yaml.safe_dump(case))
assert cli.main(["plan", "--case", str(source), "--types-root", str(TYPES),
"--no-live", "--allow-unimplemented"]) == 2
captured = capsys.readouterr()
assert captured.out == ""
assert "consumer_namespace" in captured.err

View file

@ -247,3 +247,14 @@ parameter/acceptance field; it writes and signs nothing. Details and exact nativ
`docs/forgejo-structural-materialization-source-v1.md`. T03 remains progress;
case remains accepted@1.1, not materialized/live. CCR42 consumer auth remains
in_flight, and native owner evidence is not manufactured by source preparation.
2026-10-16 (T03 source compatibility): `plan`, `build` and
`structure-request` now expose explicit `--types-root`, passing it to the existing
exact-version NetKingdom API. Actual acknowledged 1.2 source renders the proposed
Forgejo case into a six-object **draft**, preserving its absent acceptance.
Six actual-source integration checks prove the root selection, dedicated consumer
namespace/SA, ordinary unimplemented refusal, proposed build refusal, historical
1.1 bundle refusal and malformed namespace refusal. Existing 44 plan/build/
structure tests pass. No live survey, execution, case/type status, acceptance,
native owner evidence or CCR was changed. T03 remains progress; actual accepted
1.2 case/reviewed source and authenticated native owner evidence remain required.