Governance Interface
The governance interface is a contract for services that govern records written against the Triodian Metamodel. An application or agent submits records to a governing service. The service validates them, sends changes that need a person to a named reviewer, and returns published records with a provenance trail. The contract defines what a governing service does, not how it does it.
Contract: governance-interface/README.md · HTTP binding: governance-interface/openapi.yaml · Example policy: governance-interface/example-policies
Schemas: submission, change-proposal, submission-result, review-decision, governed-record, provenance-record, dataset-policy
Version: governance interface 1.0.0, against the Metamodel version in VERSION
What each party does
The Metamodel publishes the record schemas, the message schemas and the HTTP binding. A record that validates here is a record a governing service can accept.
A governing service implements the contract. For each proposal it:
- validates the record;
- matches it to records already held, so one real-world item is never entered twice;
- arbitrates conflicts by the dataset's source and date precedence;
- scores confidence;
- assigns a treatment under the dataset's autonomy rules;
- writes each step to an append-only, hash-chained provenance ledger.
Applications read only governed records. They cite the governed record or provenance ID wherever a record is quoted.
Treatments
| Value | Meaning |
|---|---|
publishedUnverified | Published automatically and marked unverified |
published | Published automatically (reverification only) |
reviewQueue | Waits for a named approver |
escalated | An unresolved conflict, sent to the method owner |
convertedToLead | The evidence cannot set a status or date alone, so it is kept as a lead |
rejected | Invalid, stale or a duplicate |
The dataset policy decides which changes need a review decision. Every approval names the approver on the governed record.
Example: a regulatory register policy
The example policy is for a register of regulatory obligations, such as AI governance obligations by jurisdiction.
Source precedence
| Rank | Source | Can set a status or date alone |
|---|---|---|
| 1 | officialText | Yes |
| 2 | regulatorPublication | Yes (expected, anticipated records) |
| 3 | standardsBodyPublication | Yes (expected records) |
| 4 | commentary | No; a lead only |
| 5 | news | No; a lead only |
Autonomy rules (the first matching rule applies)
| Rule | Changes | Treatment | Description |
|---|---|---|---|
autonomy.lead.publishUnverified | addLead, addCommentaryLink | publishedUnverified | A new lead or commentary link is published automatically and marked unverified. |
autonomy.reverify.publish | reverify | published | Confirming a record against an official or regulator source, with nothing else changed, updates its last-verified date automatically. |
autonomy.anticipated.reviewQueue | create | reviewQueue | A new Anticipated record from a rank 1 or 2 source waits for review. |
autonomy.create.reviewQueue | create | reviewQueue | Any other new record sets a status, a date and a tier, so it waits for a named approver. |
autonomy.statusDateTier.reviewQueue | statusChange, dateChange, tierChange | reviewQueue | A status change, effective date or tier change waits for review; the approver is named on the record. |
autonomy.supersede.reviewQueue | supersede, remove | reviewQueue | Removal or supersession waits for review; the superseded version is kept. |
autonomy.update.reviewQueue | update, reverify | reviewQueue | Any other change to a published record waits for review. |
Language rules, checked on every record and every response:
| Rule | Prevents |
|---|---|
language.addressedInstruction | Records describe what a source says; they never tell a reader what to do. |
language.verdict | No record, screen, export or response states whether anyone is compliant or in breach. |
language.requiredReserved | 'Required' is reserved for counsel's determination; records use source-describing words instead. |
language.clientRequirement | Nothing states what the law requires of a particular client; counsel decides what applies. |
language.passageLikelihood | An Anticipated record shows its trigger date or status, never a likelihood that it will pass or apply. |
Change Proposal
| Attribute | Type | Description | Required |
|---|---|---|---|
changeProposalID | String | Unique identifier for the proposal, assigned by the submitter, e.g. CP-2026-10-27-0001 | ✓ |
changeType | Enum | Kind of change proposed. Decides the autonomy rule that applies: create, update, statusChange, dateChange, tierChange, supersede, remove, reverify, addLead, addCommentaryLink | ✓ |
target | Object | The record the proposal creates or changes | ✓ |
target.domainType | Enum | Domain of the referenced entity, from the shared domain list (domainType in common.schema.json) (required) | |
target.entityID | String | Unique identifier of a record within its domain, for example "CAP-001" or "MKT-ENTSW-001" (required) | |
target.title | String | Name of the referenced entity, for readability | |
record | Object | For create and update changes: the full proposed record, valid against the schema for target.domainType. For other changes: omitted, or only the fields that change | |
changedFields | Array[String] | Top-level fields of the record that the proposal changes, for update changes | |
baseVersion | Integer | Version of the governed record the proposal was made against. The governing service rejects the proposal as stale if the record has moved on | |
supersededByEntityID | String | For supersede changes: the record that replaces the target | |
evidence | Array[Object] | Sources relied on for the change | ✓ |
evidence[].url | String | Address of the source (required) | |
evidence[].title | String | Title of the source document or page | |
evidence[].sourceRank | Integer | Source precedence: 1 official text (legislation register, gazette, the instrument itself); 2 regulator publication; 3 standards body publication; 4 law firm or industry body commentary; 5 news (required) | |
evidence[].externalOrgID | String | External Organization that published the source | |
evidence[].publishedDate | Date | Date the source was published | |
evidence[].retrievedAt | DateTime | When the source was read (required) | |
evidence[].contentHash | String | SHA-256 of the retrieved content, hex encoded, so the same content can be recognised later | |
evidence[].excerpt | String | The passage relied on, quoted from the source | |
evidence[].dataFlowID | String | Data flow (monitored source) the evidence came through | |
rationale | String | Why the change is proposed, in source-describing language | |
triggerID | String | Trigger record for the instrument change that prompted the proposal | |
treatment | Enum | Set by the governing service: how the proposal is handled under the autonomy policy: publishedUnverified, published, reviewQueue, escalated, convertedToLead, rejected | |
matchedEntityID | String | Set by the governing service: an existing record the proposal was matched to, so one instrument is never entered twice | |
review | Object | Set by the governing service: the review of a queued or escalated proposal | |
review.reviewStatus | Enum | Where the review stands: pending, approved, rejected, withdrawn (required) | |
review.reviewDecisionID | String | Review decision record | |
review.approverName | String | Name of the person who decided | |
review.decidedAt | DateTime | When the decision was made |
Change Type (changeType)
| Value | Meaning |
|---|---|
create | A new record |
update | A change to fields other than status, dates or tier |
statusChange | A change of status |
dateChange | A change to an effective or key date |
tierChange | A change of source tier |
supersede | Replace a record with another, keeping the old version |
remove | Withdraw a record from publication, keeping it in history |
reverify | Confirm a record against its source with no other change |
addLead | Record a lead (commentary or news) to an official source |
addCommentaryLink | Attach a commentary link to a record |
{
"changeProposalID": "CP-2026-10-27-0001",
"changeType": "create",
"target": {
"domainType": "obligation",
"entityID": "OBL-AU-CPS230-48"
},
"record": {
"obligationID": "OBL-AU-CPS230-48",
"title": "Material service providers",
"statement": "CPS 230 provides that an APRA-regulated entity must maintain a register of its material service providers.",
"obligationType": "prudentialStandard",
"source": {
"authority": "APRA",
"instrument": "CPS 230 Operational Risk Management",
"citation": "para 48"
},
"jurisdiction": "AU",
"sourceTier": "binding",
"status": "inForce"
},
"evidence": [
{
"url": "https://handbook.apra.gov.au/standard/cps-230",
"sourceRank": 1,
"retrievedAt": "2026-10-27T01:00:00Z"
}
]
}
Submission
| Attribute | Type | Description | Required |
|---|---|---|---|
submissionID | String | Unique identifier for the submission, assigned by the submitter. Resubmitting the same ID is idempotent | ✓ |
interfaceVersion | String | Version of the governance interface the submission follows, e.g. 1.0.0 | ✓ |
metamodelVersion | String | Version of the Triodian Metamodel the records are written against, from its VERSION file | ✓ |
mode | Enum | validate = check only; propose = check, record and route: validate, propose | ✓ |
submitter | Object | Who or what is acting | ✓ |
submitter.actorType | Enum | Kind of actor: agent, person, system (required) | |
submitter.actorID | String | Identifier of the actor, e.g. update-agent or a person's account ID (required) | |
submitter.displayName | String | Name to show for the actor | |
dataset | String | Governed dataset the proposals belong to, e.g. example-regulatory-register. The governing service keeps datasets separate | |
submittedAt | DateTime | When the submission was sent | |
proposals | Array[Object] | The change proposals | ✓ |
proposals[].changeProposalID | String | Unique identifier for the proposal, assigned by the submitter, e.g. CP-2026-10-27-0001 (required) | |
proposals[].changeType | Enum | Kind of change proposed. Decides the autonomy rule that applies: create, update, statusChange, dateChange, tierChange, supersede, remove, reverify, addLead, addCommentaryLink (required) | |
proposals[].target | Object | The record the proposal creates or changes (required) | |
proposals[].record | Object | For create and update changes: the full proposed record, valid against the schema for target.domainType. For other changes: omitted, or only the fields that change | |
proposals[].changedFields | Array[String] | Top-level fields of the record that the proposal changes, for update changes | |
proposals[].baseVersion | Integer | Version of the governed record the proposal was made against. The governing service rejects the proposal as stale if the record has moved on | |
proposals[].supersededByEntityID | String | For supersede changes: the record that replaces the target | |
proposals[].evidence | Array[Object] | Sources relied on for the change (required) | |
proposals[].rationale | String | Why the change is proposed, in source-describing language | |
proposals[].triggerID | String | Trigger record for the instrument change that prompted the proposal | |
proposals[].treatment | Enum | Set by the governing service: how the proposal is handled under the autonomy policy: publishedUnverified, published, reviewQueue, escalated, convertedToLead, rejected | |
proposals[].matchedEntityID | String | Set by the governing service: an existing record the proposal was matched to, so one instrument is never entered twice | |
proposals[].review | Object | Set by the governing service: the review of a queued or escalated proposal |
Submission Result
| Attribute | Type | Description | Required |
|---|---|---|---|
submissionResultID | String | Unique identifier for the result | ✓ |
submissionID | String | The submission this answers | ✓ |
mode | Enum | Mode the submission was processed in: validate, propose | ✓ |
receivedAt | DateTime | When the governing service received the submission | ✓ |
interfaceVersion | String | Version of the governance interface the governing service applied | |
metamodelVersion | String | Version of the Metamodel the governing service validated against | |
outcomes | Array[Object] | One outcome per proposal, in submission order | ✓ |
outcomes[].changeProposalID | String | The proposal (required) | |
outcomes[].valid | Boolean | Whether the proposal and its record are valid against the Metamodel (required) | |
outcomes[].issues | Array[Object] | Validation errors and warnings | |
outcomes[].matchedEntityID | String | Existing record the proposal was matched to | |
outcomes[].conflicts | Array[Object] | Conflicts found between sources or with the current record, and how each was resolved | |
outcomes[].confidence | Object | Confidence in a record, from the reliability, recency and corroboration of its evidence | |
outcomes[].treatment | Enum | Treatment assigned: publishedUnverified, published, reviewQueue, escalated, convertedToLead, rejected (required) | |
outcomes[].ruleIDs | Array[String] | Published rules applied, e.g. autonomy.reviewQueue.statusChange, precedence.officialTextFirst | |
outcomes[].governedRecordID | String | Governed record version published, when the proposal was published | |
outcomes[].provenanceRecordIDs | Array[String] | Provenance records written for the proposal |
The outcome for each proposal:
| Attribute | Type | Description | Required |
|---|---|---|---|
changeProposalID | String | The proposal | ✓ |
valid | Boolean | Whether the proposal and its record are valid against the Metamodel | ✓ |
issues | Array[Object] | Validation errors and warnings | |
issues[].issueLevel | Enum | error blocks the proposal; warning does not: error, warning (required) | |
issues[].code | String | Stable code, e.g. schema.required, reference.unresolved, language.verdict, version.stale, duplicate.existing (required) | |
issues[].path | String | JSON Pointer to the field concerned | |
issues[].message | String | Human-readable explanation (required) | |
matchedEntityID | String | Existing record the proposal was matched to | |
conflicts | Array[Object] | Conflicts found between sources or with the current record, and how each was resolved | |
conflicts[].field | String | Field in conflict (required) | |
conflicts[].values | Array[Object] | The competing values, each with its source rank | |
conflicts[].resolution | Enum | resolved = settled by a published rule; unresolved = escalated to the method owner: resolved, unresolved (required) | |
conflicts[].resolvedValue | String | Value adopted, when resolved | |
conflicts[].ruleID | String | Published rule that resolved it | |
confidence | Object | Confidence in a record, from the reliability, recency and corroboration of its evidence | |
confidence.score | Number | 0 (none) to 1 (highest) (required) | |
confidence.band | Enum | Band the score falls in: low, medium, high (required) | |
confidence.reliability | Number | Reliability component, 0 to 1 | |
confidence.recency | Number | Recency component, 0 to 1 | |
confidence.corroboration | Number | Corroboration component, 0 to 1 | |
confidence.stale | Boolean | Whether the record is past its freshness threshold | |
treatment | Enum | Treatment assigned: publishedUnverified, published, reviewQueue, escalated, convertedToLead, rejected | ✓ |
ruleIDs | Array[String] | Published rules applied, e.g. autonomy.reviewQueue.statusChange, precedence.officialTextFirst | |
governedRecordID | String | Governed record version published, when the proposal was published | |
provenanceRecordIDs | Array[String] | Provenance records written for the proposal |
Review Decision
| Attribute | Type | Description | Required |
|---|---|---|---|
reviewDecisionID | String | Unique identifier for the decision | ✓ |
changeProposalID | String | The proposal decided | ✓ |
decision | Enum | approve = publish the proposal; reject = do not; amendAndApprove = publish with the amended record supplied: approve, reject, amendAndApprove | ✓ |
amendedRecord | Object | For amendAndApprove: the record as approved | |
reviewer | Object | The person deciding | ✓ |
reviewer.name | String | Full name (required) | |
reviewer.role | String | Role, e.g. Analyst, method owner, partner firm reviewer (required) | |
reviewer.organization | String | Organization the reviewer acts for | |
reviewer.actorID | String | Reviewer's account ID | |
decidedAt | DateTime | When the decision was made | ✓ |
note | String | Reasons for the decision |
Governed Record
| Attribute | Type | Description | Required |
|---|---|---|---|
governedRecordID | String | Identifier of this version: the entity ID, '@' and the version, e.g. OBL-AU-CPS230-48@2 | ✓ |
domainType | Enum | Domain of the referenced entity, from the shared domain list (domainType in common.schema.json) | ✓ |
entityID | String | Identifier of the record within its domain | ✓ |
version | Integer | Version number, starting at 1 | ✓ |
dataset | String | Governed dataset the record belongs to | |
publicationStatus | Enum | published = approved or reverified; unverified = published automatically and not yet reviewed (leads and commentary); pendingReview = visible to reviewers only; superseded = replaced by a later version; removed = withdrawn from publication: published, unverified, pendingReview, superseded, removed | ✓ |
record | Object | The Metamodel record, valid against the schema for domainType | ✓ |
metamodelVersion | String | Version of the Metamodel the record was validated against | |
publishedAt | DateTime | When this version was published | ✓ |
approval | Object | The named person who approved this version, when approval was required | |
approval.approverName | String | Name of the approver (required) | |
approval.approverRole | String | Role of the approver | |
approval.approvedAt | DateTime | When it was approved (required) | |
approval.reviewDecisionID | String | Review decision record (required) | |
verification | Object | Verification state of this version | |
verification.lastVerified | Date | Date last checked against the official text (required) | |
verification.freshnessThreshold | String | Maximum age before the record is stale, as an ISO 8601 duration | |
verification.stale | Boolean | Whether the record is past its freshness threshold | |
verification.confidence | Object | Confidence in a record, from the reliability, recency and corroboration of its evidence | |
supersedesVersion | Integer | Version this one replaces | |
provenanceRecordIDs | Array[String] | Provenance entries behind this version, oldest first | ✓ |
changeProposalIDs | Array[String] | Proposals that produced this version |
Provenance Record
Entries are append-only. Each entry carries the previous entry's hash and its own SHA-256 hash, computed over its canonical JSON: keys sorted, no whitespace, and the hash field removed. Anyone holding a run of entries can confirm that none was altered or removed.
| Attribute | Type | Description | Required |
|---|---|---|---|
provenanceRecordID | String | Unique identifier for the entry, e.g. PROV-000123 | ✓ |
sequence | Integer | Position of the entry in the ledger | |
recordedAt | DateTime | When the event was recorded | ✓ |
eventType | Enum | What happened: submissionReceived, sourceRead, extracted, validated, matched, conflictDetected, conflictResolved, scored, treatmentAssigned, reviewDecided, published, superseded, removed, reverified | ✓ |
actor | Object | Who or what acted | ✓ |
actor.actorType | Enum | Kind of actor: agent, person, system, service (required) | |
actor.actorID | String | Identifier of the actor (required) | |
actor.displayName | String | Name to show | |
subject | Object | The record the event concerns | ✓ |
subject.domainType | Enum | Domain of the referenced entity, from the shared domain list (domainType in common.schema.json) (required) | |
subject.entityID | String | Unique identifier of a record within its domain, for example "CAP-001" or "MKT-ENTSW-001" (required) | |
subject.title | String | Name of the referenced entity, for readability | |
dataset | String | Governed dataset, e.g. example-regulatory-register | |
changeProposalID | String | Proposal the event belongs to | |
submissionID | String | Submission the event belongs to | |
governedRecordID | String | Governed record version the event produced or affected | |
inputProvenanceRecordIDs | Array[String] | Earlier entries this one follows from | |
decision | String | Human-readable account of the event | |
ruleIDs | Array[String] | Published rules applied | |
evidenceURLs | Array[String] | Sources relied on | |
confidenceScore | Number | Confidence after the event, 0 to 1 | |
previousHash | String | hash of the previous entry in the ledger; empty for the first | |
hash | String | SHA-256, hex encoded, of this entry's canonical JSON (keys sorted, no whitespace) with the hash field removed | ✓ |
Dataset Policy
| Attribute | Type | Description | Required |
|---|---|---|---|
datasetPolicyID | String | Unique identifier for the policy, e.g. POLICY-REG-REGISTER-1 | ✓ |
dataset | String | Dataset the policy governs | ✓ |
version | String | Version of the policy | ✓ |
effectiveFrom | Date | Date the policy version applies from | |
methodOwner | String | Role that receives escalations | |
sourcePrecedence | Array[Object] | Source ranks and what each can do alone | ✓ |
sourcePrecedence[].rank | Integer | Source rank (required) | |
sourcePrecedence[].basis | Enum | Kind of source: officialText, regulatorPublication, standardsBodyPublication, commentary, news (required) | |
sourcePrecedence[].canSetStatusOrDate | Boolean | Whether this rank alone can set a status or date (required) | |
sourcePrecedence[].onlyForTiers | Array[Enum] | Tiers for which it can set a status or date, when not all: binding, expected, anticipated | |
autonomyRules | Array[Object] | Treatment for each kind of change. The first matching rule applies | ✓ |
autonomyRules[].ruleID | String | Stable rule ID cited in results and provenance (required) | |
autonomyRules[].changeTypes | Array[Enum] | Change types the rule matches: create, update, statusChange, dateChange, tierChange, supersede, remove, reverify, addLead, addCommentaryLink (required) | |
autonomyRules[].whenTier | Array[Enum] | Only match records of these tiers: binding, expected, anticipated | |
autonomyRules[].whenMaxSourceRank | Integer | Only match when the best evidence is at least this rank (a lower number is better) | |
autonomyRules[].treatment | Enum | Treatment assigned: publishedUnverified, published, reviewQueue, escalated, convertedToLead, rejected (required) | |
autonomyRules[].approverRequired | Boolean | Whether publication needs a named approver | |
autonomyRules[].keepSupersededVersion | Boolean | Whether the replaced version is kept in history | |
autonomyRules[].description | String | The rule in plain words | |
datePrecedence | Array[String] | Order in which date evidence is preferred when sources disagree, highest first | |
freshness | Array[Object] | How long a record stays fresh without reverification | |
freshness[].sourceTier | Enum | Tier of an obligation's source. binding = law or prudential standard in force; expected = regulator expectations, guidance and recognised standards; anticipated = made but not yet in force, under consultation, or a pending bill. A tier says nothing about whether an organization meets the source, or whether it will apply: binding, expected, anticipated (required) | |
freshness[].threshold | String | ISO 8601 duration, e.g. P90D (required) | |
confidenceBands | Object | Score thresholds for the confidence bands | |
confidenceBands.medium | Number | Lowest score in the medium band (required) | |
confidenceBands.high | Number | Lowest score in the high band (required) | |
languageRules | Array[Object] | Wording that records and responses must not contain | |
languageRules[].ruleID | String | Stable rule ID (required) | |
languageRules[].pattern | String | Case-insensitive regular expression (ECMAScript syntax) (required) | |
languageRules[].appliesTo | Array[Enum] | Where the rule is checked: record, response, matterBrief | |
languageRules[].description | String | What the rule prevents (required) |