Assigning Spatial Data Ownership with a RACI Matrix

A validation pipeline can be technically flawless and still fail in practice because no one knows who owns a failing check. When a nightly topology run flags overlapping parcels, three people each assume another will act — the GIS analyst thinks it is a QA bug, the QA engineer thinks it is source data, the compliance officer never hears about it, and the dataset ships anyway. A RACI (responsible, accountable, consulted, informed) matrix removes that ambiguity by naming, for every dataset and every validation task, exactly one accountable owner plus the roles that act, advise, and are notified. This page shows how to build one for spatial datasets, gives a worked example table, and maps each assignment to a concrete pipeline gate. It puts the roles defined in data stewardship roles and responsibilities onto a single, auditable grid.


RACI matrix mapped to pipeline gates A grid with validation tasks listed down the left as rows and stewardship roles across the top as columns. Each cell carries a letter R, A, C, or I. The single Accountable cell in each row connects by an arrow to a corresponding pipeline gate on the right, showing that the accountable role's sign-off enforces that gate. Tasks \ Roles Steward Analyst QA Eng Compliance Rule authoring A C R I QC execution I R A I Exception approval R C C A Certification C I I A Rule-authoring gate QC / merge gate Publication gate Each row's single Accountable role gates one pipeline stage

Prerequisites

  • A named list of roles, not people. Build the matrix around roles (data steward, GIS analyst, QA engineer, platform engineer, compliance officer) so it survives staff turnover. Map individuals to roles in a separate, more volatile register.
  • A decomposed task list. You cannot assign ownership of “validation” as a whole. Break the work into discrete tasks — rule authoring, preflight, QC execution, exception approval, certification — each of which can have one accountable owner.
  • A dataset tiering scheme. Group layers by criticality so one matrix covers a tier rather than needing one per file. Tiering methodology is covered under defining spatial data quality policies.
  • An orchestration platform that supports role-based access control. The matrix is only enforceable if the accountable role’s sign-off can be wired into a gate — branch protection, environment approval, or a scoped service account.
  • Gotcha — one Accountable per row, always. The single most common RACI defect is two roles marked Accountable for the same task. When two people own an outcome, neither does. The validation step in this procedure exists specifically to catch this.

Step-by-Step Procedure

Step 1 — List the validation tasks as rows

Enumerate every discrete activity in the dataset’s lifecycle that can succeed or fail independently. Keep the granularity at the level a single role can own end-to-end.

# raci_model.py — tasks are the rows of the matrix
TASKS = [
    "dataset_intake_and_tiering",
    "rule_authoring",
    "source_preflight",
    "automated_qc_execution",
    "exception_approval",
    "publication_certification",
    "lineage_archival",
]

Verification: Read each task and ask “can exactly one role be answerable if this goes wrong?” If a task needs two different owners for two different outcomes, split it into two rows before continuing.

Step 2 — List the stewardship roles as columns

Name the roles that participate in the pipeline. Use the same role vocabulary across every matrix so a person moving between dataset tiers carries a consistent meaning.

ROLES = [
    "data_steward",
    "gis_analyst",
    "qa_engineer",
    "platform_engineer",
    "compliance_officer",
]

Verification: Confirm every role that could appear as an Accountable owner in Step 3 is present. A task with no candidate owner among the columns signals a missing role, not a matrix you can leave blank.

Step 3 — Assign R, A, C, and I to each cell

Fill the grid. Each task gets exactly one A, at least one R, and any number of C and I. Represent the matrix as data so it can be validated programmatically.

# Each task maps role -> one of {"R","A","C","I"} (omit a role that has no involvement)
RACI = {
    "dataset_intake_and_tiering": {"data_steward": "A", "gis_analyst": "R", "compliance_officer": "C"},
    "rule_authoring":             {"data_steward": "A", "qa_engineer": "R", "gis_analyst": "C", "compliance_officer": "I"},
    "source_preflight":           {"gis_analyst": "A", "qa_engineer": "R", "data_steward": "I"},
    "automated_qc_execution":     {"qa_engineer": "A", "gis_analyst": "R", "platform_engineer": "C", "data_steward": "I"},
    "exception_approval":         {"compliance_officer": "A", "data_steward": "R", "qa_engineer": "C"},
    "publication_certification":  {"compliance_officer": "A", "data_steward": "C", "platform_engineer": "I"},
    "lineage_archival":           {"platform_engineer": "A", "qa_engineer": "R", "compliance_officer": "I"},
}

Verification: Eyeball each row for exactly one A. Note that exception_approval marks the data steward R and the compliance officer A — deliberately separating the person who requests an exception from the one who approves it.

Step 4 — Validate the matrix for gaps and overloads

Do not trust a hand-filled grid. Run structural checks: one Accountable per task, at least one Responsible, and no role accountable for a task it is only meant to advise on.

def validate_raci(raci: dict) -> list[str]:
    problems = []
    for task, row in raci.items():
        letters = list(row.values())
        n_a = letters.count("A")
        n_r = letters.count("R")
        if n_a != 1:
            problems.append(f"{task}: needs exactly one Accountable, found {n_a}")
        if n_r < 1:
            problems.append(f"{task}: no Responsible role assigned")
    # Separation of duties: no role is both R and A on a gating task
    for task in ("exception_approval", "publication_certification"):
        row = raci.get(task, {})
        accountable = [r for r, v in row.items() if v == "A"]
        responsible = [r for r, v in row.items() if v == "R"]
        if set(accountable) & set(responsible):
            problems.append(f"{task}: same role is both Responsible and Accountable")
    return problems

Verification: validate_raci(RACI) must return an empty list. Wire this function into CI so any future edit to the matrix that breaks the one-Accountable rule fails the build before it merges.

Step 5 — Map each row to a pipeline gate

A matrix that lives only in a document is advisory. Bind each task’s Accountable role to a concrete gate so the platform enforces the sign-off. The mapping below reads the matrix and asserts the required approver for each gate.

# Map matrix tasks to enforceable pipeline gates
GATE_FOR_TASK = {
    "rule_authoring":            "rule_authoring_gate",
    "automated_qc_execution":   "qc_merge_gate",
    "publication_certification": "publication_gate",
}

def required_approver(task: str, raci: dict) -> str:
    """Return the role whose approval the platform must enforce at this gate."""
    row = raci[task]
    return next(role for role, letter in row.items() if letter == "A")

GATE_APPROVERS = {
    GATE_FOR_TASK[t]: required_approver(t, RACI)
    for t in GATE_FOR_TASK
}
# -> {"rule_authoring_gate": "data_steward", "qc_merge_gate": "qa_engineer",
#     "publication_gate": "compliance_officer"}

Verification: Confirm GATE_APPROVERS names a distinct, correct approver per gate, then encode those approvers as branch-protection reviewers or environment-approval groups in your orchestration platform so an unapproved change cannot pass the gate.

Interpreting Results

The example matrix, read as a table, is the artifact you circulate for sign-off:

Validation task Data Steward GIS Analyst QA Engineer Platform Engineer Compliance Officer
Dataset intake & tiering A R C
Rule authoring A C R I
Source preflight I A R
Automated QC execution I R A C
Exception approval R C A
Publication certification C I A
Lineage archival R A I

Read the matrix two ways. By row, it answers “who owns this task?” — the bold A is the single throat to choke. By column, it answers “what is this role responsible for?” — scan the compliance officer’s column and you see they are Accountable for both exception approval and certification, which is correct because both are independent-review gates. A column with too many A marks signals an overloaded role and a bottleneck; a column with only I marks signals a role that could be dropped from this tier’s matrix entirely.

The separation between exception_approval (steward is R, compliance officer is A) is the load-bearing design decision. It guarantees the requester and approver are different roles, which is what makes the resulting audit trail credible. When you scope which datasets need this rigour, use the risk-tiering approach in audit scoping for municipal GIS assets — regulatory-tier layers warrant the full matrix, while analytical layers may collapse several rows onto one accountable owner.

Gotchas & Edge Cases

Two Accountables is worse than none. When a row has two A marks, each owner assumes the other will act and the task silently drops. The validate_raci check exists to make this a build failure, not a discovered-in-audit surprise.

“Responsible for everything” is a hidden single point of failure. If one role carries R on every row, the matrix is documenting an overloaded individual, not distributing ownership. Rebalance so responsibility spreads across roles; a bus-factor of one is a governance risk even when the paperwork is tidy.

RACI without enforcement is decoration. A matrix that is never wired into a gate changes no behaviour. The value comes from Step 5 — binding the Accountable role to an actual branch-protection reviewer or environment approver so the platform refuses to advance without the right sign-off.

Consulted and Informed are not interchangeable. Consulted means input is sought before the decision — a two-way exchange. Informed means notification after — one-way. Marking the compliance officer I on rule authoring when they should be C means their objection arrives too late to change the rule.

Matrices go stale. A reorganization or a new dataset tier invalidates old assignments. Review the matrix on the same cadence as the quality policies it supports, and version it in the same repository so each change carries a rationale, consistent with the ownership practices in data stewardship roles and responsibilities.

When to Escalate

A single tier-level RACI matrix suits most programmes. Move to a heavier ownership model when:

  • Statutory ownership differs per layer. When individual high-value datasets carry legal ownership requirements distinct from their tier, promote them to their own matrix with a named accountable steward rather than a role.
  • Cross-agency data sharing introduces external roles. A partner organization’s reviewer may need C or A on certification. At that point the matrix must integrate with the formal alignment process in compliance framework alignment so external sign-off is captured in the audit trail.
  • The task list outgrows a single grid. When a tier has dozens of distinct validation tasks, a flat RACI becomes unreadable. Decompose into per-stage sub-matrices aligned to the pipeline architecture, and drive gate enforcement from the rule engine described in defining spatial data quality policies.

Frequently Asked Questions

What does RACI stand for and why use it for spatial data?

RACI stands for responsible, accountable, consulted, and informed. Responsible roles do the work; the single accountable role owns the outcome and signs off; consulted roles give input before a decision; informed roles are told after. For spatial data it removes the ambiguity that causes validation failures to go unowned — when a topology check fails, the matrix names exactly who fixes it, who approves the exception, and who must be notified, so nothing falls between an analyst and a compliance officer.

Should every spatial dataset have its own RACI matrix?

Not one per dataset — one per dataset tier or class. Group datasets by criticality (regulatory, operational, analytical) and write a matrix per tier, then assign a named accountable steward per individual layer within that tier. This keeps the number of matrices manageable while still giving every layer a single accountable owner. A separate matrix per dataset only becomes necessary when a specific high-value layer has statutory ownership requirements that differ from its tier.

Can one person hold both the Responsible and Accountable role?

For low-risk tasks, yes — the same person can be both responsible and accountable. But for any task that gates publication, separate them. The person who authors a validation exception (responsible) must not also be the one who approves it (accountable), because that removes the independent review that makes the audit trail credible. Enforce the separation in the orchestration platform with branch protection or a distinct approval service account.


Related:

Back to Data Stewardship Roles and Responsibilities