Finish companion catalog work and reconcile completed approval tasks
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e324-abce-7e51-bb2b-496f097afdb0
This commit is contained in:
parent
41e4c4a3d8
commit
e33f9c3ca5
18 changed files with 472 additions and 87 deletions
|
|
@ -102,8 +102,9 @@ def test_binding_changed_after_approval_refuses_before_fetch(bound, monkeypatch)
|
|||
exec_delivery.exec_with_secret(object(), entry, "api_token", data["delivery_config"]["exec_owner"]["command"], expected_owner_digest=expected)
|
||||
|
||||
|
||||
def test_pending_real_catalog_refuses_before_approval_and_backend(tmp_path, monkeypatch):
|
||||
def test_pending_owner_refuses_before_approval_and_backend(tmp_path, monkeypatch):
|
||||
entry = load_entry(Path(__file__).resolve().parents[1] / "catalog/glas-claude-agent-dev-anthropic.yaml")
|
||||
entry.delivery_config["exec_owner"] = {"status": "pending", "owner": "fixture", "reason": "not admitted"}
|
||||
monkeypatch.setattr(cli, "get_entry", lambda *a: entry)
|
||||
for name in ["_require_lane_approval", "_open_backend"]:
|
||||
monkeypatch.setattr(cli, name, lambda *a, **k: pytest.fail("no approval consume or backend"))
|
||||
|
|
@ -160,8 +161,35 @@ def test_invalid_binding_is_not_a_catalog_fallback(bound, change):
|
|||
def test_pending_owner_never_advertises_ready(tmp_path, monkeypatch):
|
||||
from secrets_engine import routing
|
||||
entry = load_entry(Path(__file__).resolve().parents[1] / "catalog/glas-claude-agent-dev-anthropic.yaml")
|
||||
entry.delivery_config["exec_owner"] = {"status": "pending", "owner": "fixture", "reason": "not admitted"}
|
||||
monkeypatch.setattr(routing, "resolve_decision", lambda **k: SimpleNamespace(status="approved", review_url="", is_approved=lambda: True))
|
||||
client = SimpleNamespace(is_reachable=lambda: True, read_policy=lambda p: "policy", approle_exists=lambda r: True, kv_fields_present=lambda *a: {"ANTHROPIC_API_KEY": True})
|
||||
result = routing.route_lane(entry, hub_url="", repo_root=tmp_path, client=client)
|
||||
assert not result.ready and "exec owner" in result.missing
|
||||
assert "<command" not in result.next_command
|
||||
|
||||
|
||||
def test_configured_glas_catalog_refuses_arbitrary_child_before_gate(tmp_path, monkeypatch):
|
||||
entry = load_entry(Path(__file__).resolve().parents[1] / "catalog/glas-claude-agent-dev-anthropic.yaml")
|
||||
assert entry.delivery_config["exec_owner"]["status"] == "configured"
|
||||
monkeypatch.setattr(cli, "get_entry", lambda *a: entry)
|
||||
for name in ["_require_lane_approval", "_open_backend"]:
|
||||
monkeypatch.setattr(cli, name, lambda *a, **k: pytest.fail("no approval consume or backend"))
|
||||
with pytest.raises(DeliveryError, match="catalog-bound"):
|
||||
cli.cmd_exec(_cfg(tmp_path), SimpleNamespace(field=None, catalog=entry.id, command=["/bin/echo"], mode="exec-env"))
|
||||
|
||||
|
||||
def test_configured_glas_companion_matches_worker_and_pins_spend_policy():
|
||||
from secrets_engine.exec_owner import resolve_companions
|
||||
root = Path(__file__).resolve().parents[1] / "catalog"
|
||||
entry = load_entry(root / "glas-claude-agent-dev-anthropic.yaml")
|
||||
binding = entry.delivery_config["exec_owner"]
|
||||
companions = resolve_companions(entry, lambda cid: load_entry(root / (cid + ".yaml")))
|
||||
assert [(lane.id, field, env) for lane, field, env in companions] == [
|
||||
("activity-core-metered-worker-token", "token", "ACTIVITY_CORE_WORKER_TOKEN")
|
||||
]
|
||||
assert binding["environment"]["AGENT_HARNESS_WORKER_ID"] == "rein-aharness-metered@railiance01"
|
||||
assert binding["environment"]["AGENT_HARNESS_OPS_LABELS"] == "hfact-metered"
|
||||
assert binding["files"][binding["environment"]["AGENT_HARNESS_SPEND_POLICY"]]["private"] is True
|
||||
request = build_action_request(entry, "exec", subject_id="agent:fixture", subject_type="Agent", purpose="owner-proof", fields=entry.fields)
|
||||
assert request["context"]["exec_owner_sha256"] == owner_digest(entry)
|
||||
|
|
|
|||
|
|
@ -164,7 +164,8 @@ def test_real_exec_handler_rejects_undeclared_claim_before_consume_backend_child
|
|||
assert [r["result"] for r in records] == ["attempt", "failed-DecisionError"]
|
||||
|
||||
|
||||
def test_factory_catalog_declares_human_control_and_keeps_owner_pending():
|
||||
def test_configured_factory_catalog_still_requires_human_control():
|
||||
entry = load_entry(Path(__file__).resolve().parents[1]/"catalog/glas-claude-agent-dev-anthropic.yaml")
|
||||
assert entry.approval["human_control"] is True
|
||||
assert entry.delivery_config["exec_owner"]["status"] == "pending"
|
||||
assert entry.delivery_config["exec_owner"]["status"] == "configured"
|
||||
assert not entry.approval.get("authorization_id")
|
||||
|
|
|
|||
|
|
@ -190,6 +190,27 @@ def test_schema_version_is_not_reached(tmp_path, monkeypatch):
|
|||
assert _run_with(tmp_path, monkeypatch, {}, {"schema_version": "0.2"}) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("patch", [
|
||||
{"intent_version": "0.1.0"},
|
||||
{"rationale": "The v0.5 scope rule is historical provenance."},
|
||||
{"framework": "Historical v0.7 reference; standard identity is separate."},
|
||||
])
|
||||
def test_reference_detector_allows_own_versions_and_prose(tmp_path, monkeypatch, patch):
|
||||
assert _run_with(tmp_path, monkeypatch, patch, {}) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("patch", [
|
||||
{"standard_version_reviewed": "0.7"},
|
||||
{"version_of_companion": "0.2"},
|
||||
{"standard": "security-layer-model v0.7"},
|
||||
{"companion": ["security companion 0.2"]},
|
||||
{"references": [{"path": "unrelated-document-v1.2.md"}]},
|
||||
{"references": ["security-layer-model@0.7"]},
|
||||
])
|
||||
def test_reference_detector_rejects_nested_and_identity_pins(tmp_path, monkeypatch, patch):
|
||||
assert _run_with(tmp_path, monkeypatch, {}, patch) == 2
|
||||
|
||||
|
||||
def test_stance_and_classification_versions_are_not_reached():
|
||||
"""GH-DEC-2026-020 §3: stance maps and classifications keep their version,
|
||||
and the checker never applies A12 to them."""
|
||||
|
|
@ -198,7 +219,7 @@ def test_stance_and_classification_versions_are_not_reached():
|
|||
for path in (STANCE, CLASSIFICATION):
|
||||
data = yaml.safe_load(path.read_text(encoding="utf-8"))
|
||||
# Each carries a version A12 would reject if it were applied there...
|
||||
assert checker._version_hits(data), f"{path.name} keeps its standard_version"
|
||||
assert checker.find_version_pins(data), f"{path.name} keeps its standard_version"
|
||||
# ...and the real-tree run still passes: A12 is not applied to them.
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(SCRIPT)], cwd=ROOT, capture_output=True, text=True, check=False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue