tesoro 0.1.3

The shared vocabulary

Six terms the rest of this documentation uses precisely. They are worth ten minutes because getting any of them slightly wrong makes every later sentence wrong — and because most of them come from a real bug that a loose definition allowed.

Three names: AEGL, AEGS, Tesoro

NameIsIs not
AEGL Autonomous Economic Governance Layer — the category a package
AEGS Autonomous Economic Governance Standard — the open specification an implementation
Tesoro a Python package; a policy-engine host the standard

In prose, a layer is an AEGL. Tesoro implements AEGS, and AEGS is one profile Tesoro can enforce rather than Tesoro's identity — you can run it with profile: none and it governs exactly as well, it just makes no conformance claim.

Two channels

An agent spends in two directions, and they are never one budget.

internalexternal
Whattokens the agent burns thinkingwhat it pays out
Currencyreal money, on your provider keyoften stablecoin, on a chain
Counterpartyone provider you choseanyone the agent picks
When exhaustedREJECTREVIEW

That last row is the whole reason they are separate. The same breach has a different correct answer depending on which money it was.

A payout can wait for a person — REVIEW means pausable, and a human can look at it. A token budget cannot: there is no human in the loop of a running agent, so “pause and ask” becomes “hang”, and the tokens already spent are spent. Worse, starting a step that cannot finish wastes the budget that is already short.

An implementation may share a limit between channels but never an envelope, because an envelope carries consumption. Sharing consumption would mean an agent's thinking exhausted its ability to pay, which is not a relationship anybody intended.

Four verdicts

APPROVE < REVIEW < ESCALATE < REJECT

VerdictMeansThe agent should
APPROVEproceedpay, then settle
REVIEWa human should look — pausablequeue it and continue with something else
ESCALATEa human must look — blockingstop this line of work
REJECTno, and no amount of waiting changes itabandon the action

The ordering is normative — it is not a display preference — and the middle pair is the one that invites disagreement. Swapping REVIEW and ESCALATE fails six of the standard's test vectors, which exist precisely because two implementations could otherwise each be internally consistent and mutually incompatible.

Narrowing, and why widening is unreachable

Every control returns an assessment, and the final verdict is the most severe one any control produced. So:

That second property is stronger than it looks, because widening is not forbidden, it is unreachable. Taking a maximum has no branch that could return something lower. A hostile or buggy control can refuse things it should not; it cannot approve anything. Which is what makes a third-party extension safe to load, and why an optional advisory model is allowed to exist at all — it can only make the answer stricter.

treasury APPROVE trust APPROVE intent REVIEW policy APPROVE REVIEW the most severe wins attributed to: intent order does not change the verdict — but it does change which control gets the attribution
Because the verdict is a maximum, running the controls in any order gives the same answer. The attribution is not order-independent, which is why the standard makes evaluation order normative and why conformance scores attribution rather than just the verdict.

Attribution — the field that matters most

Which control decided, not what was decided. A refusal with no attributable cause is not auditable evidence.

It sounds trivial and is not. Consider a sanctioned counterparty asking for more than the per-transaction ceiling. Both controls refuse. If attribution means “the last control that narrowed”, the answer depends on which limit happened to trip first — so a sanctions refusal can be reported as a budget problem, and the operator raises a limit that had nothing to do with it.

The standard fixes this with dispositive controls: a finding that decides attribution whether or not it narrowed anything, and which must be declared rather than inferred. Tesoro declares exactly one — sanctions.

Thirteen controls

A control is a named thing that can examine an action. The name set is closed and matching is exact:

ControlAsks
AgentIdentitywho is acting, under whose authority
EconomicIntentwhat was this agent sent to do
Policywhat do the operator's rules say
BudgetEnvelopeis there headroom
RiskAssessmentdoes this action's shape score as risky
TrustAssessmenthas this counterparty earned anything
GovernanceDecisionthe verdict itself, as a recorded artifact
EvidenceRecordthe chained journal entry
Authorizationthe attribution and reasons
ConformanceDeclarationwhat levels are claimed
AMLAssessmentscreening — a schema with no engine behind it
ComplianceAssessmentjurisdictional checks — also unimplemented
IncidentRecordwhat went wrong, recorded

Exact matching looks pedantic until two deployments disagree about whether budgetenvelope is the budget envelope — at which point a profile requiring BudgetEnvelope silently scores an implementation that never had it. A case variant is an extension, not the defined control.

Envelopes

An envelope is a limit plus the consumption against it. Two properties are worth separating, because conflating them was a real defect:

FieldAnswersExists
bindingwhy was this refused?only on a refusal
tightestwhat bites next?always

An earlier version of the report had only binding, under a heading meaning tightest — so an approved decision showed no envelope at all, blank precisely when somebody was checking headroom.

A per-call ceiling is not an envelope in this sense: it never accumulates, so it has no used value. Rendering it as $0.00 of $10.00 beside the cumulative rows reads as “nothing was spent”, which is false — and was also a real defect.

Four states

absent ≠ not-run ≠ unknown ≠ zero. Four different answers, and collapsing any two loses something a reader needs.

StateMeans
absentthe field is not there. Nothing was claimed, so nothing may be inferred
not-runthe control exists, was asked, and declined to answer
unknownit ran and cannot report a value
zeroit ran, and the answer really is zero or false

This comes from a real bug. An unmeasured vendor history rendered as 0, so every advisor treated established counterparties as strangers — the system was reading “we have never measured this” as “this counterparty has no history”.

The same rule applies to a validator: “I could not check” is not “this is invalid”. And to an unreachable external assessor: the answer is not-run, never pass, because fail-open is a governance layer that stops governing exactly when it is attacked.

Profiles vs policy packs

The distinction most worth keeping straight, because they are both YAML and both about rules:

ProfilePolicy pack
Sayswhich controls must exist, what must be recordedwhat the rules are — the numbers
Written bythe standardyou
Changeswhat is scoredwhat is decided
Exampleaegs-1, aegs-2, nonepolicies/default.yaml

A profile never touches a verdict — asserted four different ways in the test suite. Switching from aegs-1 to none changes no decision; it only stops making a conformance claim.

Evidence

A Decision Record is the artifact one decision produces: the action, the verdict, the attributed control, the reasons, the policy hash, and both version lines. Records are appended to a hash-chained journal, where each entry commits to its predecessor.

DetectedNot detected
editing an entry · deleting one from the middle · reordering · replay deleting entries off the end

Any prefix of a valid chain is itself valid, so truncation is undetectable without an external anchor. That is disclosed rather than papered over — and the fix that looks like one is worth naming: writing the chain head to a second file beside the journal is not an anchor, because whoever can truncate the journal can rewrite that file too.

Money

Integer atomic units internally. USDC has 6 decimals, so 2500000 is $2.50. Conversion happens once at the boundary, with an explicitly specified rounding mode (half-up).

amount_usd="2.50"      # a decimal string
amount_usd=2500000    # atomic units — the same amount
amount_usd=2.50       # TypeError

The rounding mode is pinned because every language's default differs and none is wrong in isolation — an implementation inheriting its own has made a decision it did not know it was making. And a float raises rather than rounding, because 0.1 + 0.2 is not 0.3 and there is no rounding the layer could pick that the caller would know about.