Skip to content

aep/types: no schema signal for 'system permission layer gated this action' (paired with open-agent-audit false-positive) #21

Description

@telleroutlook

No AEP event / adapter mapping for "system permission layer already gated this action"

Repo: packages/aep (paired with an open-agent-audit issue on the adapter side)

Problem

When the platform's permission layer denies (or automatically approves) a high-risk tool call, the wasmagent AEP emitter records action.capability_decision.decision = 'deny' (or 'allow'), and that's all. Downstream, open-agent-audit's canonical schema does have a human_approval event type, but no adapter mapping produces one — because AEP has no "this was gated by a policy engine" signal distinct from "the agent decided" vs "a human decided".

The consequence: OAA's OAA-R-OVERSIGHT-001 fires as if no oversight ever happened, even when the platform gated the call.

(See companion issue in open-agent-audit — oaa-04-oversight-false-positive-on-adapted-aep.)

Evidence in wasmagent-js

packages/aep/src/types.ts:4-11:

export const CapabilityDecisionSchema = z.object({
  capability: z.string(),
  subject: z.string(),
  resource: z.string(),
  decision: z.enum(["allow", "deny", "ask_user", "dry_run"]),
  reason_code: z.string().optional(),
});

The decision values are all system decisions. There is no analogue for "a human approved this", nor a way to distinguish "gated by policy engine at check-time" from "gate not present at all".

packages/aep/src/emitter.ts has addAction, addCapabilityDecision, addInputRef, addOutputRef, addVerifierResult, setBudgetLedger. There is NO addHumanApproval (or addPolicyGate) helper.

Impact

  1. Any AEP integrator that hooks a permission layer will look "unoverseen" from OAA's perspective — false positives.
  2. Real gaps in human review (a state-changing tool that was NOT gated) get lost in the noise.
  3. Emitters that want to signal "our policy engine caught this at layer X" have no schema-blessed way to say so.

Proposed fix (choose one)

Option 1 (schema addition): add a new field on CapabilityDecisionSchema:

export const CapabilityDecisionSchema = z.object({
  capability: z.string(),
  subject: z.string(),
  resource: z.string(),
  decision: z.enum(["allow", "deny", "ask_user", "dry_run"]),
  reason_code: z.string().optional(),
  /** The layer that made this decision. Adapters may map policy_gate to a
   *  human_approval-equivalent event so downstream oversight rules don't
   *  false-positive when the platform's permission layer already caught it. */
  decided_by: z.enum(["policy_gate", "agent", "human_reviewer"]).optional(),
});

Option 2 (new event type): add an HumanApprovalRecord / PolicyGateRecord alongside ActionEvidence, with fields {approver_id, decision, justification, action_id}. The emitter grows addHumanApproval({...}). This is heavier but maps more cleanly to OAA's canonical human_approval.

Option 3 (docs only): document that CapabilityDecisionSchema.subject conventionally names the deciding layer (agent, human, policy_engine). Zero schema change but leaves everyone using string conventions.

We think Option 1 is the sweet spot — one field, zero new tables, backward compatible (optional), and gives the OAA adapter a clean signal.

Reciprocal work needed downstream

Once one of the options ships, open-agent-audit/packages/adapters/src/aep-v0_2.ts:213-219 should be updated so that capability_decision.decided_by === 'policy_gate' or 'human_reviewer' maps into a canonical human_approval event (rather than only policy_decision). We've filed a companion issue there.


Filed by: CATL Ariba Joule integration team. Our platform gates every state-changing action via a permission layer. We want that fact to survive into OAA's audit; today it doesn't.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions