Action X-Ray

Builds debugger-level visibility straight into the architecture: an operation's progress and its intermediate state at every step are visible online — even in production, without stopping the process.

A complex business operation is usually a chain of method calls. During development you can run it in a debugger, step through the entire execution path, and see the intermediate state. That helps you find complex, non-obvious bugs. But some problems surface only on real data and under real load. At that moment you need an online debugger — one that can x-ray the operation's real execution and find the cause without stopping the process. Action X-Ray gives you exactly that.

Documentation ↗

You can't attach a debugger in production

A debugger shows an operation from the inside — but you can't attach one in production, and a live run offers nothing to grab onto.

During development a debugger freezes execution at any step: you see the arguments, the intermediate values, and what will be passed on. In production that's impossible — you can't halt the process for a single request, and a rare input is hard to reproduce. Nor can you watch a run on the fly: the chain of calls lives in the call stack, its intermediate values in local variables, and the moment a call returns, all of it disappears. Logs preserve only pre-selected values; a technical trace sees the calls and their duration but doesn't know which of them are business steps or which intermediate values between them matter. The cause has to be guessed from indirect clues.

The data exists at runtime — a debugger proves it. What's missing is something else: a boundary, known in advance, around the run and around each of its steps, one you can attach online observation to — without stopping the process.

Development
validate
reservebreakpoint{ validated_items: 3 } · all values visible
charge

pause at any step — the whole state is visible

Production
requestresponse / exception

can't stop — the middle is invisible

Full docs ↗

The run's boundaries are already set by the architecture

To observe a run as a whole, it needs a precise boundary — and so does each of its steps.

In AOA an operation is an Action with an external boundary Params → Result. It's run by the machine — the AOA engine that executes an Action and knows in advance where the run begins and ends. Inside, the path is split into Aspects, each with its own intermediate state, state. An Aspect receives a snapshot of the previous state and returns its own new one — which fully replaces the previous state rather than augmenting it. So everything the following steps need, the Aspect carries forward explicitly, and nothing leaks between steps on its own; @result_* checks the output right at the boundary. These boundaries are not an observation tool but the very structure of the operation; yet they are exactly what gives the known-in-advance points where each step's input, output, and state are visible.

At any moment the machine knows the run's frame and its entire contractual path: what entered each Aspect and what state came out. The observable "single run" artifact is already assembled by the architecture itself — all that remains is to let it out.

external boundary of the Action
Params
state before{ }
01 · observation pointvalidate@result_* ✓
state after{ validated_items: 3 }
state before{ validated_items: 3 }
02 · observation pointreserve@result_* ✓
state after{ …, reservation_id: 'res_42' }
state before{ …, reservation_id: 'res_42' }
03 · observation pointcharge@result_* ✓
state after{ …, payment_id: 'pay_91' }
Result
Full docs ↗

Online debug: a live run seen from the outside

Since the boundaries are known, the machine emits an event at each one — and an external observer assembles them into a live picture of the run.

At the external boundary you see the actual Params and Result; at each internal one, the incoming and outgoing state, the duration, and any error. The machine knows all of these points and automatically emits lifecycle events; a plugin receives them from the outside and builds an online projection of a specific run — a tree of steps with real inputs, outputs, errors, and timing. The same in development, in tests, and in production — with no logging calls, manual timers, or tracing code inside the business methods.

This is not an attached debugger or a stopped process, but its safe online projection. The observer sees only what the contract permits: sensitive fields stay opaque, and a failure of the plugin itself is isolated and does not change the operation's result. This is exactly what Action X-Ray is.

live runCreateOrderAction
start · Paramsvalidate · state → state′ · 0.4 msreserve · state′ → state″ · 12 mscharge · error · 3 msfinish · Result · 15 ms
lifecycle events
plugin · outside the operationonline projection of the run
  • start · Params
  • validate · state → state′ · 0.4 ms
  • reserve · state′ → state″ · 12 msopaque
  • charge · error · 3 ms
  • finish · Result · 15 ms

the process is never stopped · the same in dev, test, and production

The plugin is handed to the machine at creation — the business methods stay untouched:

service/machine.py
plugin = OpenTelemetryPlugin(    tracer_provider=tracer_provider,    logger_provider=logger_provider,    service_name="checkout-service",)# The plugin lives outside the Action; business methods stay untouched.machine = ActionProductMachine(plugins=[plugin])result = await machine.run(Context(), CreateOrderAction(), params)
Full docs ↗

In a test — the same x-ray

The same online snapshot is available in a test too: TestBench runs the same operation through the same machine.

TestBench does not create a special test-only visibility — it runs the same Action through the same machine and the same boundaries. Only the outside world changes: instead of the production Context, Resources, and gateway, their test implementations are used, while the roles, pipeline, checkers, and the assembly of the Result stay real. The operation can be run in full or stopped at a single boundary — an Aspect, summary, or compensator — without special hooks in the business code.

A test verifies the same projection that's available in production: not only the final Result, but any intermediate state. What changes is the reality around the operation, not the operation itself or the way it's observed.

PRODUCTIONreal Context · Resources · Gateways
the same Action · the same machineroles · pipeline · checkers · Result assemblyone and the same online projection
TESTtest Context · fixtures · mocks
the whole Action — system behaviorone boundary — precise localization

The same Action through TestBench — in full or at a single boundary:

tests/test_create_order.py
inventory = AsyncMock(spec=InventoryGateway)inventory.reserve.return_value = "res_42"bench = TestBench().with_user(    user_id="customer", roles=(CustomerRole,)).with_mocks({InventoryGateway: inventory})# The whole operation — same machine, same boundariesresult = await bench.run(CreateOrderAction(), params, rollup=False)# Or stop at a single boundary and inspect its statestate_after = await bench.run_aspect(    CreateOrderAction(), "reserve_aspect",    params=params, state={"validated_items": 3},)assert state_after["reservation_id"] == "res_42"
Full docs ↗

Online debug becomes a property of the architecture

AOA sets the external boundary of an Action, the internal boundaries of its Aspects, and turns intermediate state into a contract. During a run, those same boundaries become the coordinate system for lifecycle events — and the visibility that used to appear only under a debugger or after manual instrumentation is now available online for every execution, including production, with no infrastructure code inside the operation.

DebuggingFor a specific run, the state before and after each step, the error, and its exact boundary are all visible — without attaching a debugger.
ProductionThe same projection is available online under real load through external plugins that do not change the Action.
TestingThe same operation and the same boundaries are verified; only the external reality around it is substituted.
Action X-Ray · aoa.run