Agent Trust & Assurance · OBDA-MODEL · OBDA-ACT

The Anatomy of a
Signed Action
acting under signature

Reading safely is half the job. The other half is changing the business — and a change is only as trustworthy as the contract it went through, the person who approved it, and the receipt it left behind.

Trust & Assurance · visual story · companion architecture: OBDA-MODEL & OBDA-ACT · the read half: The Anatomy of a Signed Data Product

Two planes, one road

OBDA-MODEL changes the model itself — a new entity, a new attribute, a performance structure. An agent may only draft; a person publishes. OBDA-ACT changes rows — record a lead, close it — through a signed action type whose posture decides whether a person confirms or approves. Both travel the same road: propose → validate → land → publish → sign → use.

The worked example below is the Act II walkthrough of the policing demo, replayed on the hosted instance on 7 October 2026: an investigator finds that the model has no record of open lines of enquiry, the model grows a Lead, leads are recorded in one click, and closing one waits for a supervising officer.

Act I

Proposed — the dead end becomes a proposal

Before OBDA-MODEL and OBDA-ACT, a question about a concept the model lacks ended at "the model has no leads". Now the agent says that first — and then proposes what the model is missing, in the answer, with the reasons. One click drafts it. Nothing an agent drafts is visible to anyone else, and nothing it drafts is ever published by an agent.

the investigator asks which leads are open? the answer leads with the gap The model records no Lead — nothing in it describes an open line of enquiry. Proposed: a Lead belongs to one case object, has a source exhibit, a status and an owner. provisional · no rows to measure Draft it CaseObject the signed base model Lead authored layer · draft belongs to drafts never seed retrieval and never reach another agent

The draft lands on the graph's authored layer, beside the logical and physical layers — never inside them. A draft never seeds retrieval and never survives subgraph assembly, so no agent grounded on the model sees it until a person publishes it.

Decided by the data, not the model

A normalisation advisor

Entity or attribute is decided deterministically from probes over the rows: functional dependency, multiplicity, hidden identity, shared reference, independent lifecycle, closed enumeration. With no rows the verdict says provisional; with every probe failed it says unknown. It never pretends to have measured.

It refuses what the data cannot support

No per-record link from nothing

Asked to link an incident to the Heist case "as a record", the advisor declines: the two areas share no key, and the only correspondence is class-level — robbery codes to evidence kinds. It offers a Lead that references both instead. The same boundary the read side already holds, now at authoring time.

Act II

Landed — the model grows without touching its base

The operator walks the draft down the road in the ontology's Govern dialog: drafted → validated → deployed → published. The landing preview says, in words, exactly what will be created and where — and where is a matter of policy, never a guess.

Policing_Core core model · not owned by this account v_Incident · v_Offence · v_CaseObject · … base seal · untouched UDERIA_EXT_Policing_Core the platform sibling — provisioned first Lead key lead_id · reference to v_CaseObject.object_id readback per rule: held · violated · unverified not owned → lands beside ontology_extension · Ed25519 signed over { extension_hash , base_attested_hash }

The landing rule: the model's own database when the deploy login owns it, otherwise a platform sibling named from the deployed root. Unknown ownership never lands in the root. After the landing, the warehouse's own dictionary is read back against each design rule — a rule reads verified only when its proving statement was executed on that warehouse version.

A person publishes

An agent drafts; it never publishes

Publishing is refused to an agent: actor under every policy, and the person must hold an approver tier even when the ontology itself needs no approval. Only published nodes reach agents — and a Model B agent sees the released snapshot plus the published extension, never a draft.

Lineage inside the signature

"changed · base moved"

Because the base's signature is inside the extension's, a new release of the base reads the extension changed — and names the mover — while the base itself stays green. An extension can never silently outlive the model it extends. And it never moves the base: an extension, an action type and a tuning structure are all stacked on the graph, outside the base's hash.

Act III

Contracted and proven — the A1–A6 gate earns the signature

A row-level change is never "SQL the agent wrote". It is an action type: a manifest on the ontology naming the target, the operation, the only attributes it may write and their allowed values, the conditions, a required row bound, the database role it needs — and its posture. Before it can be signed, the gate proves it against the live warehouse.

-- close_lead · from the Act II walkthrough
target     Lead  (deployed: UDERIA_EXT_Policing_Core.Lead, key lead_id)
operation  update
writes     status ∈ {open, closed} · closed_reason
bound      1 row
checks     row_exists · one_row_affected   -- implied by the operation, never redeclared
posture    review                           -- a closure waits for a person
ledger     UDERIA_ACT_Lead                  -- append-only, the before-image kept
CheckWhat it provesEvidence
A1the frozen target is what the resolver answers today — drift means re-validate and re-signresolver
A2every column exists with a compatible type; declared value domains agree with the graph's closed domainsdictionary
A3the key really identifies one row — measured on the data; a probe that cannot run failslive
A4conditions are boolean, hold no subquery, read only the target's own columnsstructural
A5one parameterised statement on the target only; a live EXPLAIN shows no product join and no full scanlive
A6the role holds exactly the rights the operation needs — a wider role is named, not accepted — and the ledger is provisionedlive
The compiler

Values are bound, never pasted

Every invocation compiles to one parameterised statement; an update carries WHERE <key> = ? and nothing else; every value is coerced through its declared type and checked against its domain. A value that differs from exactly one declared value only in case — 'Closed' for 'closed' — is that value, and the compile note says so. The statement A5 judged is the statement that runs, byte for byte.

Refusals that teach

Recovery-shaped, not walls

unknown_attribute names the closest attribute, missing_required names what is missing, outside_domain names the allowed values. An agent corrects in one step; a write it cannot express in a granted action is refused with the rule, not with "contact an administrator".

Act IV

Signed and granted — trust and access, kept apart

The signature attests the contract and its proof together: the manifest hash and the validation-report hash. Live evidence signs verified_write; structural evidence signs partial — and the difference is part of what is signed. Then, separately, the action is granted to an agent. A grant is access, not trust: it moves no signature.

refused, by name

invalid_manifest · unvalidated · stale_report · gate_blocked · unsigned_ingredient — a write contract stands only on a signed ontology.

edited → changed

Editing the manifest drops its stored proof and the badge reads changed. Re-validate, re-sign: the ratchet only turns forward.

granted

The grant is the list: an agent can perform only the actions named on it — not_granted otherwise, at every door.

The boundary

With action-only set, every raw INSERT, UPDATE, MERGE or DELETE the agent sends through its data tool is refused at the same chokepoint that enforces a data surface — and the refusal names the actions it is granted. In the walkthrough, "change the offence code of that incident to 220" is refused in words: no action type grants a write to v_Offence. The national extract is never written.

Act V

Performed — the consequence first, then one transaction

In a conversation the agent never writes. It prepares: the write runs inside a transaction that is always rolled back, so the row it shows — status open → closed, 1 row of a bound of 1, every check held — is the database's own answer. Confirm performs that invocation and only that one; a different value set is refused with nothing written.

one transaction pre-checks before-image statement post-checks ledger commit any check fails → the whole write rolls back · a driver error fails closed
PostureSigned into the contract — what happens when the action is performed
observerecords what would be written; writes nothing
reviewruns the write rolled back, so the diff is the database's answer, and parks it in the Assurance Inbox — nothing written until a person approves
enforcewrites in one transaction through the runner above
One core, every door

Seven faces, one perform seam

The conversation, REST, MCP, a flow node, a scheduled task, a pinned dashboard tile and an Inbox approval all reach the same perform seam. Unattended doors never prepare — nobody is there to confirm — so the posture decides, and they always act as the agent: no agent, no write. In the rehearsal a flow node recorded a lead in 2.5 seconds and zero tokens.

Nothing to change, nothing to ask

A no-change write is refused early

If every keyed row already holds every value the write would set — read from the database's before-image, never from the request — the act is refused before anyone is asked to confirm or approve it. A person's attention is part of the cost of a write.

Undo is an act too

The ledger's before-image, replayed

Undo turns the before-image into an invocation of the same signed contract, prepared and confirmed like any act, with its own receipt. The lead reads open again — and both acts stay on the record.

The connection is decided

Never guessed

Explicit, then the agent's configured connection, then its navigator source, then the user's only eligible connection — otherwise refused, naming the candidates. On Teradata the session acts under exactly the role A6 proved.

Act VI

Approved — four eyes, and the owner is not one of them

Closing a lead is a review-posture action, and the investigator's agent names a supervising officer as its approver. The closure parks for that person. The investigator — who owns the agent that asked — cannot approve it.

the investigator "close that lead" parked for the supervisor the diff: status open → closed nothing written yet owner approves → 403 not_your_decision · refused first replay of the row → 409 one confirmation per invocation the supervisor their own agent: "what closures await my approval?" → Approve performed · 1 row · receipt only if the contract, the re-compiled statement and the invocation are unchanged since the park, and the grant still names the action runs as the owner's agent · the ledger and Trust History name who decided

From the Act II rehearsal: the closure parked for the supervisor; the investigator's own approval refused (403); a replay of the parked row refused (409); the supervisor approved from their own agent's answer, and Trust History read "approved by Detective Sergeant (supervisor)".

No agent ever approves

The approver clicks; the agent lists

The supervisor's agent answers "what awaits my approval?" with each parked write and its diff, and Approve / Reject buttons the person clicks. An approval is never an agent's act.

Never parked where nobody may decide

A bad designation refuses the park

A named approver that is not an active account refuses the park outright — a write never waits on nobody, and never falls back to the owner silently. And the approval's write and its disposition run as one unit, so a dropped connection can never leave a performed write on an item still pending.

Act VII

Receipted and kept in check — every act, every door

A performed act inside a conversation is sealed into the turn's Ed25519 provenance chain. An act outside one — a confirm, an approval, a flow, a schedule, a REST or MCP call — carries a receipt signed on its own, over the act and its occurrence: which door, when, who confirmed. Two identical writes on different runs stay distinguishable, and each verifies offline with the public key alone.

One record for all doors

The act log

Every door writes one row into one append-only log: the action, the agent, the door in words, and — for an approval — who decided. Trust History, the activity tally and the signed audit pack all read it, so an act through a dashboard tile is as visible as one in a conversation.

On the ring

BOUNDED reads acted · refused

The Trust Circle's BOUNDED station reads a turn's acts and names each refusal; its evidence lists the actions acted on in recent sessions. REVIEWED counts a granted review-posture action as able to park — the day an agent parks five writes, it does not read "cannot park".

Measured, not assumed

action_safety and write-escape

action_safety scores the share of a turn's writes whose every check held. The write-escape live red-team probe lures an action-only agent into a raw UPDATE and scores, from the persisted turn, whether it held.

Off means absent

One master, a byte-identical baseline

The OBDA-MODEL · OBDA-ACT master (App Config → Trust → Model & actions) ships off. Off, no authoring or action text reaches any prompt, the action tool is not offered and the REST face refuses — exactly as if OBDA-MODEL and OBDA-ACT were not installed. On, nothing acts until an action is signed and granted.

Proof

The walkthrough, in numbers

From the Act II rehearsal and its replay on the hosted instance, and from the gates every increment had to pass.

6
checks in the A1–A6 gate — three of them measured live against the warehouse
5
refusal conditions on an action's signature — each machine-readable
7
doors to one perform seam — conversation, REST, MCP, flow, schedule, dashboard, Inbox
1
click from a sentence to a performed write — the friction budget, measured as a gate
3
clicks from a draft to a published extension — preview, land, publish
2
wrong approvals refused before anything ran — the owner's own (403) and a replayed row (409)
13
Teradata design rules, each executed on the target version before it may read verified
0
tokens for a flow node recording a lead — 2.5 seconds, no model call
Honest limits

Delete is not an operation yet, so an insert cannot be undone. Approvers are named per agent, not per action, and there is no quorum. MCP actions are developer tier only. The PostgreSQL landing was rehearsed locally — the hosted instance reaches no PostgreSQL warehouse. An extension's survival of a pack republish is proven by gates, not yet driven end to end.

Why this matters

An agent that can read safely but not act is an assistant. An agent that can act without a contract is a liability. Signed actions make the contract the only shape of change: proven against the warehouse before it is signed, confirmed or approved by the right person, performed in one transaction, and receipted wherever it happened. The model can grow the same way — beside its signed base, never inside it.