Build a RACI for the lab and test it for separation of duties
Task
Write a RACI matrix covering the real tasks in your lab system, using the ten roles from the lesson plus the business owner, and test it two ways: that every activity has exactly one accountable role, and that no single role holds responsibility for both halves of a separation-of-duties pair. Then face the fact that you hold every role yourself, and record the compensating control for each pair that collapses onto one person.
Steps
- Create
lab/raci.csvwith headeractivity,data_scientist,data_engineer,ml_engineer,ai_architect,platform_engineer,mlops_engineer,ai_security_architect,ai_governance_engineer,ai_risk_analyst,ai_auditor,business_owner. Each cell holdsR,A,RA,C,Ior nothing. - Add at least twelve activities drawn from your lab, worded as you will reuse them: add a document to
docs/and the index; check a new source's provenance; judge whether the model is fit for purpose; hold and edit the regression set; approve a model change for release; deploy a model to the runner; change gateway rules; retain and protectlogs/gateway.jsonl; threat-model a design change; build the check that blocks unregistered use; score a new AI risk; accept residual risk; verify that approvals followed the procedure; monitor drift. - Give every row exactly one
A. Use the lesson's matching: the data engineer owns ingestion, the MLOps engineer drift monitoring, the governance engineer the enforcing check, the business owner risk acceptance, the auditor verification. - Create
lab/sod-pairs.csvwith headerpair_id,activity_a,activity_b,reasonand at least five pairs from the lesson, using activity names exactly as they appear in the RACI: build versus approve release, train versus hold the regression set, deploy versus protect the log, add data versus approve the model, anything versus verify. - Create
lab/role-holders.csvwith headerrole,holder, one row per role. In this lab every holder isme. - Create
lab/sod-compensating.csvwith headerpair_id,compensating_control,evidence: for each pair, the control that stands in for a second person, such as an immutable copy of the log, an automated gate you cannot bypass, or a review by someone outside the lab. - Plant a conflict: give
data_engineeranAon approving a model change for release, run the verify block, see the role-level conflict list fill, then remove it.
Verify
cd ~/secai-lab
python - <<'EOF'
import csv
raci = {r["activity"]: r for r in csv.DictReader(open("lab/raci.csv", encoding="utf-8"))}
roles = [k for k in next(iter(raci.values())) if k != "activity"]
cell = lambda row, x: (row.get(x) or "").strip()
print(len(raci), "activities,", len(roles), "roles")
print("rows without exactly one A:",
[a for a, r in raci.items() if sum("A" in cell(r, x) for x in roles) != 1])
holder = {r["role"]: r["holder"] for r in csv.DictReader(open("lab/role-holders.csv", encoding="utf-8"))}
comp = {r["pair_id"] for r in csv.DictReader(open("lab/sod-compensating.csv", encoding="utf-8"))
if r["compensating_control"] and r["evidence"]}
role_conf, person_conf, n = [], [], 0
for p in csv.DictReader(open("lab/sod-pairs.csv", encoding="utf-8")):
n += 1
a, b = raci.get(p["activity_a"]), raci.get(p["activity_b"])
if a is None or b is None:
print("unknown activity in", p["pair_id"])
continue
ra = {x for x in roles if set(cell(a, x)) & set("RA")}
rb = {x for x in roles if set(cell(b, x)) & set("RA")}
if ra & rb:
role_conf.append(p["pair_id"])
if {holder.get(x) for x in ra} & {holder.get(x) for x in rb}:
person_conf.append(p["pair_id"])
print(n, "separation pairs; role-level conflicts:", role_conf)
print("person-level conflicts:", len(person_conf))
print("person-level conflicts with no compensating control:",
[x for x in person_conf if x not in comp])
EOF
Twelve or more activities and eleven roles, no row without exactly one A, no unknown activity, five or more pairs with an empty role-level conflict list, person-level conflicts equal to the number of pairs (you hold every role), and an empty uncompensated list. A person-level count of zero means role-holders.csv does not match the RACI's column names.
Notes
The two conflict counts answer different questions. The role-level count says whether the design separates duties; the person-level count says whether the staffing does. A small team can have a perfect design and no separation at all, which is why the lesson's answer for that case is compensating controls rather than pretending.
This is an independent study companion for CompTIA SecAI+ CY0-001 and is not produced by or endorsed by CompTIA.