Bidirectional links: from outcome back to the rule
A trace from request to response is half a trace. The other half is from outcome back to the rule that allowed it. Most platforms have one direction; few have both. Why bidirectional matters for debugging, for audit, and for AI agent decisions, and the pattern that makes it work.
Most observability stacks I've worked with have half a trace. They can answer the forward question, "this request came in. Here's what happened to it. Here's the response that went out", with elaborate detail. Spans, logs, metrics, all stitched together with a request ID. You can follow the request through the system. You can see which services touched it. You can see the latency at each hop.
The half they don't have is the reverse direction. Given an outcome, a permission grant, a policy decision, a denied transaction, an automated action an agent took, what rule made that outcome happen? Not the rule's name. Not "policy.allow returned true." The actual rule, with its source location, its version, its author, and the chain of nested rules that combined to produce the decision. Outcome back to rule.
Forward traces are about latency and errors. Reverse traces are about authority and intent. They answer different questions, they require different infrastructure, and a system that only has one of them is making half the questions you'll eventually need to ask harder than they need to be.
What "outcome back to rule" actually means
Every meaningful action in a system happens because something authorized it. The authorization might be implicit (the user owns the resource), plain (a policy engine returned true), composed (a role-based check combined with a tenant-scope check combined with a time-of-day rule), or inherited (the action is allowed by a default that was decided five years ago and has been carried forward since).
Bidirectional traceability is the discipline of recording, at the moment of every authority decision, an identifier that points back to the rule (or composed set of rules) that produced the decision. The identifier is stable. The rule is versioned. The chain reconstructs.
A worked example. Customer 789 receives an automated refund. The forward trace tells you everything that happened: the trigger event arrived, the refund service processed it, the payment processor was called, the customer was emailed. The reverse trace answers a different question: why was the refund authorized? It points back to policy.refund.automatic.v4, a rule that says "automatic refunds are permitted for customers in tier-A within 30 days of purchase for amounts under $50." The rule is in source control. It was authored by the finance team. It was approved by the VP of Customer Success on a specific date in a specific PR. The chain reconstructs.
Without the reverse direction, you can answer "was the refund processed?" Yes. With the reverse direction, you can answer "was the refund supposed to be processed?" That's a different question with a different answer.
Why one direction without the other isn't enough
I've been the engineer with only forward traces, and I've spent more time than I want to remember debugging the wrong thing. A finance team flags an unexpected automatic refund. I look at the forward trace: refund processed correctly, no errors, latency normal, response 200. The system did what it was told. The question I can't answer from forward traces alone is what told it to do this thing in the first place, was this a real customer in a real edge case, or was the rule wrong?
I had to reconstruct the reverse direction by hand. Pull the customer record. Check their tier. Check the purchase date. Look up the rule that should have applied. Read the rule. Try to match the customer's state against the rule's predicates to figure out whether the system's interpretation matched mine. By the time I had an answer, the finance team had escalated, the customer was on a call, and the rule was about to be changed in the wrong direction because nobody could prove what had actually happened.
If the refund's audit record had carried "authorized by policy.refund.automatic.v4#tier-a-window" inline, the entire investigation would have been ten minutes. The version was right there. The rule's text was a click away. The history of changes to that rule was the version control history. The decision could be evaluated against the rule, the rule could be evaluated against the policy intent, and the answer could be "the rule is wrong" or "the rule is right but it surprised the finance team" or "the rule fired on data it shouldn't have seen", three different conclusions with three different fixes.
The reverse direction shortens debugging from hours to minutes. It's also the only path to answering whether the system did the right thing, not just whether the system did what it was told.
The auditor cares about the same direction
I've written elsewhere about what an auditor actually wants from an audit trail. The "why allowed" field is the same shape as the reverse trace. An auditor reading a financial action without the reverse pointer can confirm the action happened; they cannot confirm the action was supposed to happen. The pointer to the authorizing rule is what closes that loop.
This is why audit trails and observability traces converge in the same place when you do them right. The same identifier, policy.refund.automatic.v4#tier-a-window, appears in the engineering trace, the audit log entry, and the policy engine's evaluation history. One identifier, three uses, one source of truth. The decision is traceable from any direction you happen to start in.
The AI agent case makes it non-negotiable
The reverse direction was a nice-to-have when systems were deterministic. It's a hard requirement when AI agents are taking actions on the system's behalf, because the agent's decisions aren't reproducible from inputs the way a deterministic policy engine's are.
When an agent takes an action, the future investigation is going to ask "why did the agent do this?" The forward trace will tell you the agent's prompt, its tool calls, and its outputs. That's the agent's behavior. It doesn't tell you what the agent was authorized to do (what bounded the agent's action space) and it doesn't tell you which authority decision permitted this particular action.
The reverse direction for agents looks like this. Every agent action is gated by a policy that defines the agent's bounded autonomy. The policy has a stable ID. When the agent takes an action, the action's audit record carries the policy ID and the version of the policy at the time of the action. Six months later, when someone asks "why did the agent do X," you can reconstruct: the agent took action X because policy agent.bounds.v7#refund-class permitted it, the policy was authored by the platform team, the policy was approved on this date, the policy's intent (in the PR description) was to allow refunds in this class. If the agent did the wrong thing, the conversation moves to whether the policy was right, which is the conversation that should be happening.
This is what OPA / Gatekeeper as the AI-policy foundation makes operationally tractable. The policy decisions are recorded with stable IDs; the IDs travel with the agent's actions; the reverse trace closes.
The pattern that makes it work
The pattern is small enough to lay out in one paragraph. Every authority decision in the system (every policy evaluation, every permission check, every rule-based gate) records an ID linking back to the rule it consulted. The ID is stable across rule edits (the rule has a name; the rule has versions; the ID is name#version or name@version). The decision's audit record stores the ID. The forward trace's relevant span carries the ID. The application's response, where it makes sense to expose it, can also carry the ID.
The rules themselves live in version control. They have authors, approvals, and history. The rule's ID resolves to its definition at the time it was consulted, not its current definition. (This matters: a rule that was permissive last quarter and restrictive this quarter should reconstruct correctly for a decision made last quarter.) The version control history is the rule's history. The PR that introduced the rule version is the rule's intent.
The lookup goes both ways. Given an outcome, you find the rule (audit record → rule ID → rule definition). Given a rule, you find every outcome it authorized (rule ID → search of audit records → list of decisions). Both directions are queryable. Both directions are how you actually debug, audit, and understand a real running system.
This is the Decisions as Code shape one more time, viewed from the observability angle. The decisions are centralized (one place for the rules), they're projected (every consumer references them by ID), and the projection is bidirectional (consumers can find rules, rules can find consumers). The approach gives you the architecture; the architecture gives you the bidirectional trace.
What changes when you have it
The investigations get shorter. The five-hour "why did this happen" sessions become fifteen-minute lookups. The audit conversations get shorter, the auditor's "why was this allowed" question is answerable from the audit record without engineering involvement. The agent's actions become explainable in a way that matters when an agent is doing something on the system's behalf.
The conversations between engineering and the rule authors get more honest. When you can show the finance team "here's the action. Here's the rule that allowed it. Here's when the rule was last changed. Here's the PR for that change," the conversation is grounded. When you can only show "the action happened," the conversation is people guessing.
The rules themselves get better, because they get used. A rule that's never traced to is a rule nobody trusts. A rule that's traced to thousands of times (and still produces the right outcomes) is a rule with social proof inside the system. The reverse direction surfaces which rules are doing real work and which rules are dead.
The forward trace tells you what the system did. The reverse trace tells you why the system was allowed to do it. You need both. Most systems only have one. The fix isn't a new tool. It's the discipline of attaching a stable rule identifier to every authority decision, and treating that identifier as a first-class field in the audit record, the trace, and (where appropriate) the response.
Outcome back to rule. Half the value of traceability lives in that direction. Most platforms leave it on the table.
, Sid