Learn onetrace track 2 · five levels · about an hour

Decisions, tracing, nesting.

Five levels on what onetrace 0.5.0 added: four on your own machine, in your track 1 folder, and a last one on the live demo. A step gated by a decision; paying on a deny, and the verifier catching it; a run inside a trace; a step inside a step; and the demo's planted incidents.

Before you start

What you need.

Track 1's folder

The onetrace-learn folder and its virtual environment from track 1, with onetrace 0.5.0 or later. onetrace --version tells you which.

No onedoor needed

The decision levels use a hand-written stand-in reference. The rows that need onedoor's own permit and export read NOT-RUN, and say so; the demo's level shows them checked.

Nothing sent

No level sends anything. The trace level uses an environment variable, not a tracing service; the last level reads the public demo in your browser.

The levels

Five levels, in order.

2.1 Gate a step

A run can declare which of its steps must be gated: each must cite the onedoor decision that allowed it. Create pay.py in your track 1 folder. Its decision reference is a stand-in with the right shape, written by hand: there is no onedoor here, and the page says what that leaves unchecked.

import onetrace as ot

# A stand-in for the reference onedoor returns. Not from onedoor: there is no onedoor here.
REF = {
    "format": "onedoor-decision-ref/1",
    "request_id": "req-0001",
    "decision_digest": "sha256:" + "0" * 64,
    "verdict": "permit",
    "issuer": "https://issuer.example/learn",
}


@ot.stage("approve_payment", instrument=ot.pkg("pay-invoice", "onetrace", kind="tool"))
def approve_payment(invoice):
    ot.decision(REF, action={"type": "pay", "invoice": invoice, "amount_eur": "40.00"})
    return {"paid": invoice}


@ot.run(stages=["approve_payment"], gated_stages=["approve_payment"])
def main(invoice):
    print(approve_payment(invoice))


if __name__ == "__main__":
    main("INV-1")

Windows (PowerShell)

$env:ONETRACE_RUN_ID = "level-2-1"
python pay.py
onetrace-verify --require-artifacts runs/level-2-1

macOS or Linux (Terminal)

ONETRACE_RUN_ID=level-2-1 python pay.py
onetrace-verify --require-artifacts runs/level-2-1

Pass when: the check ends -> PASS, with five decisions: rows:

[PASS   ] decisions: gated steps cite a decision -- 1 gated step(s), each citing a decision
[PASS   ] decisions: references are well formed -- 1 reference(s), each a well-formed onedoor-decision-ref/1
[PASS   ] decisions: a step that acted cites a permitting decision -- 1 acting step(s), each citing a permitting decision
[NOT-RUN] decisions: the permit covers the recorded action -- no permit recorded
[NOT-RUN] decisions: the decision is in the export -- no export given (pass --decisions)

Read the two NOT-RUN rows: they are the checks that need onedoor's side, a signed permit and onedoor's own export of its decisions. Without them the record shows that the step cited a permitting decision of the right shape, and nothing reads as checked that was not. The action itself is recorded by digest only, never as the object.

Badge: gated a step. Keep runs/level-2-1.

2.2 Pay on a deny, and catch it

Copy pay.py to pay_deny.py and change "verdict": "permit" to "verdict": "deny". The step still pays. Run it and check:

Windows (PowerShell)

$env:ONETRACE_RUN_ID = "level-2-2"
python pay_deny.py
onetrace-verify --require-artifacts runs/level-2-2

macOS or Linux (Terminal)

ONETRACE_RUN_ID=level-2-2 python pay_deny.py
onetrace-verify --require-artifacts runs/level-2-2

Pass when: the first line reads record NOT intact: decisions: a step that acted cites a permitting decision, the summary ends -> FAIL, and the failing row is:

[FAIL   ] decisions: a step that acted cites a permitting decision -- approve_payment: acted on a decision whose last verdict is 'deny', not 'permit'

Now the other way to get it wrong: copy pay.py to pay_none.py and delete the ot.decision(...) line, so the gated step cites nothing.

Windows (PowerShell)

$env:ONETRACE_RUN_ID = "level-2-2-none"
python pay_none.py
onetrace-verify --require-artifacts runs/level-2-2-none

macOS or Linux (Terminal)

ONETRACE_RUN_ID=level-2-2-none python pay_none.py
onetrace-verify --require-artifacts runs/level-2-2-none

It fails with [FAIL ] decisions: gated steps cite a decision -- declared gated, cited no decision: approve_payment, and the three rows that need a reference read NOT-RUN.

The right way to handle a deny is not to act. In pay_deny.py, put these two lines just before return {"paid": invoice}:

    if REF["verdict"] != "permit":
        raise PermissionError("onedoor did not permit this payment")

Windows (PowerShell)

$env:ONETRACE_RUN_ID = "level-2-2-ok"
python pay_deny.py
onetrace-verify runs/level-2-2-ok

macOS or Linux (Terminal)

ONETRACE_RUN_ID=level-2-2-ok python pay_deny.py
onetrace-verify runs/level-2-2-ok

The script stops with the PermissionError, as it should. Verify without --require-artifacts: a step that did not act wrote no output. The row reads [NOT-RUN] decisions: a step that acted cites a permitting decision -- no references on a step that acted and the check ends -> PASS. A deny is fine; paying on one is the finding.

Badge: caught a deny. Keep runs/level-2-2 and runs/level-2-2-none, and the two refusals as text.

2.3 Run inside a trace

When a run starts inside a trace, each step's receipt records the trace and span it ran in. No OpenTelemetry package is needed: onetrace reads the TRACEPARENT environment variable. Run pay.py inside the W3C specification's own example trace:

Windows (PowerShell)

$env:TRACEPARENT = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
$env:ONETRACE_RUN_ID = "level-2-3"
python pay.py
Select-String -Path runs\level-2-3\MANIFEST.json -Pattern traceparent -Quiet
Select-String -Path runs\level-2-3\receipts\*.json -Pattern 4bf92f3577b34da6a3ce929d0e0e4736 | Measure-Object | Select-Object Count

macOS or Linux (Terminal)

TRACEPARENT=00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 ONETRACE_RUN_ID=level-2-3 python pay.py
grep -c traceparent runs/level-2-3/MANIFEST.json
grep -l 4bf92f3577b34da6a3ce929d0e0e4736 runs/level-2-3/receipts/*.json

Pass when: the manifest holds the run's traceparent, both receipts (01-intake.json and 02-approve-payment.json) hold a trace block with that trace id and span id 00f067aa0ba902b7, and onetrace-verify --require-artifacts runs/level-2-3 still ends -> PASS.

Then give it a broken one, and a new run id:

Windows (PowerShell)

$env:TRACEPARENT = "not-a-trace"
$env:ONETRACE_RUN_ID = "level-2-3-bad"
python pay.py
Remove-Item Env:TRACEPARENT

macOS or Linux (Terminal)

TRACEPARENT=not-a-trace ONETRACE_RUN_ID=level-2-3-bad python pay.py

The run is refused before any step: EmissionRefused: TRACEPARENT 'not-a-trace' is not a W3C traceparent, with a fix line, and runs/level-2-3-bad holds no receipt. A broken trace is never written into the record. The trace stays a view: it says which trace the steps ran in, and only the record proves what went in and came out.

Badge: traced a run. Keep runs/level-2-3.

2.4 A step inside a step

A step called while another runs records its caller, and the caller lists it. Create order.py: a plan step that calls a price_lookup step, with a setting recorded by name.

import onetrace as ot

RATE_CENTS = 120  # the price of one unit, in cents


@ot.stage("price_lookup", instrument=ot.pkg("rate-table", "onetrace", kind="tool"))
def price_lookup(item, qty):
    ot.constant("rate_cents", RATE_CENTS)
    return {"item": item, "total_cents": qty * RATE_CENTS}


@ot.stage("plan", instrument=ot.pkg("planner", "onetrace", kind="planner"))
def plan(item, qty):
    quote = price_lookup(item, qty)
    return {"steps": ["quote", "order"], "quote": quote}


@ot.run(stages=["plan", "price_lookup"])
def main(item, qty):
    print(plan(item, qty))


if __name__ == "__main__":
    main("widget", 3)

Windows (PowerShell)

$env:ONETRACE_RUN_ID = "level-2-4"
python order.py
onetrace-verify --require-artifacts runs/level-2-4

macOS or Linux (Terminal)

ONETRACE_RUN_ID=level-2-4 python order.py
onetrace-verify --require-artifacts runs/level-2-4

The check ends -> PASS. Open the receipts: 02-price-lookup.json holds "parent":"plan" and 03-plan.json holds "children":["price_lookup"]. The child finishes first, so its receipt comes first in the chain.

Now change RATE_CENTS = 120 to RATE_CENTS = 125, record again and compare:

Windows (PowerShell)

$env:ONETRACE_RUN_ID = "level-2-4-b"
python order.py
onetrace localize runs/level-2-4 runs/level-2-4-b --out r24
Remove-Item Env:ONETRACE_RUN_ID

macOS or Linux (Terminal)

ONETRACE_RUN_ID=level-2-4-b python order.py
onetrace localize runs/level-2-4 runs/level-2-4-b --out r24

Pass when: localize: diverged; price_lookup reads FIRST DIFFERENCE with constant rate_cents differs; and plan also reads FIRST DIFFERENCE, the line below saying first difference price_lookup, plan (2 branches).

That second line is the lesson. plan used the child's result inside its own body, but nothing passed it in as an input, so the record links the call (parent and children) and not the data. Each output's change is traced to where it starts, and plan's reads "nothing it read differs". When a result is passed from one step to another as an argument, the record links the two by digest, and a comparison follows it.

Badge: nested a step. Keep runs/level-2-4, runs/level-2-4-b and r24/.

2.5 Boss: the live demo's planted incidents

Three questions on the live demo, each answered from the record alone.

1. Open pipeline P8 and the run p8-49-invoice-paid-on-deny. Which decisions: row fails, and what did the step do that it should not have?

2. Open p8-50-invoice-permit-other-amount. Which row fails this time, and what was wrong with the permit?

3. Open pipeline P10, an agent whose one step calls a sub-agent's run (P11). In the comparison of pz-51-agent-baseline and pz-53-agent-amount-changed: in which step of the outer run do the runs part, into which run does the comparison descend, and at which of that run's steps?

Pass when your answers match what the demo shows:

1. decisions: a step that acted cites a permitting decision -- approve_payment: acted on a decision whose last verdict is 'deny', not 'permit'
2. decisions: the permit covers the recorded action -- approve_payment: the permit covers a different action than the step recorded
3. The runs first differ inside agent_step, into run pz-52-agent-baseline-subagent at settle.

On the demo the decisions and permits are invented example data, signed with a throwaway key made when the demo was built; the checks on them are the verifier's own.

Badge: solved the incidents. Your answers are the output; each run's own rows and the comparison's short answer are the check.

When you're done

What a record of an action can say, and what it can't.

You have seen the verifier pass a gated step, refuse a payment made on a deny and a gated step that cited nothing, and leave unchecked what it had no evidence for. You have recorded the trace a run ran in, seen a broken one refused, and seen a record name a step's caller. None of it says a decision was right: only that the record names an unaltered decision, and what that decision allowed.

Back to Learn, or take it to your own code.