Files
acdl/acdl_platform/separation_of_duties.py
T
Jon Chery 727c87339b fix(P08 prep): rename platform/ -> acdl_platform/ (stdlib shadow fix)
---ci---
project: acdl
phase: 8
milestone: v1.1
status: plan-as-execute
persona: lead-developer
task: T-8.0
type: prerequisite-fix
---/ci---

The Phase 07 P1 ('platform/ package shadows stdlib platform module')
became a Phase 08 blocker: boto3 imports uuid -> platform.system(),
which fails when the repo's platform/ package is on sys.path[0]. Renamed
platform/ -> acdl_platform/ (the verifier's recommended v1.2 fix, pulled
forward because Phase 08 needs boto3).

- git mv platform/ acdl_platform/ (history preserved)
- verify_phase07.sh: updated paths; removed the /tmp workaround (no
  longer needed; the shadow is gone)
- verify_phase06.sh: updated the new-dirs check for the rename
- README.md: layout table updated

Both verify_phase06.sh and verify_phase07.sh still pass; confidence_signal
now imports + runs correctly from the repo root. boto3 imports clean.
2026-07-21 18:53:41 +00:00

42 lines
1.8 KiB
Python

"""Check that qaApprover != prodApprover for a contract (ARCHITECTURE.md
§10.3, D-042). Reads `approver_qa` from the DynamoDB outbox for the
contractId, compares to the prod-dispatch `gitea.actor`. Blocks on
equality, emits `SEPARATION_OF_DUTIES_VIOLATION`, routes a halt artifact
to SRE on-call.
Spike scope (A-8.1): the spike is dev-only (REQ-27 contract has
environment: dev); HITL is not exercised. This module is authored to its
full v1.2 shape but the spike calls it with current_prod_approver=None
and a None outbox_client — the check returns (True, 'no QA approver
recorded (dev-only spike)').
"""
from typing import Optional, Tuple
def check(outbox_client, contract_id: str,
current_prod_approver: Optional[str]) -> Tuple[bool, str]:
"""Return (ok, reason). ok=False means block the prod promotion."""
if outbox_client is None:
return (True, "no outbox client (dev-only spike)")
item = outbox_client.get(contract_id)
if item is None:
return (True, "no prior approver (first promotion)")
qa_approver = item.get("approver_qa")
if not qa_approver:
return (True, "no QA approver recorded (dev-only spike)")
if current_prod_approver is None:
return (True, "no prod approver supplied (dev-only spike)")
if qa_approver == current_prod_approver:
return (False,
f"SEPARATION_OF_DUTIES_VIOLATION: "
f"qaApprover==prodApprover=={qa_approver}")
return (True, "distinct")
def route_halt_artifact(contract_id: str, violation_reason: str,
oncall_client) -> None:
"""Route a halt artifact to SRE on-call. Spike: stub that logs. v1.2
wires a real pager."""
print(f"[halt-artifact] contract={contract_id} reason={violation_reason} "
f"oncall={oncall_client}", flush=True)