Eight forbidden effects

Every side effect on an external app passes through one gate. The rules are declared before the run, enforced in code, and counted. A blocked attempt is traced as attempted → BLOCKED → reason, never silently dropped. The Stripe disputes API submits by default; the agent always stages first and reads the validator before it ever sends submit=true.

SUBMIT_WITHOUT_HUMAN_APPROVALno Stripe submission without the Slack click
DUPLICATE_FILING_SAME_DISPUTEa dispute is filed once, ever
UNCITED_CLAIMS_IN_PACKETthe writer produced a claim with no source record
EDITED_CE3_PREFILLED_FIELDStripe pre-fills IP and product description; editing them breaks eligibility
SECOND_NOTICE_TO_CUSTOMERone notice per dispute, text or email, never both
SECOND_CALL_TO_CUSTOMERone call per dispute, ever
NOTICE_WITHOUT_FILINGthe customer is never told about a filing that did not happen
REFUND_OUTSIDE_SCOPEthe agent may never refund

The policy table

Reason code × evidence → verdict. Deterministic, unit-tested, and tightened only in one direction by the harness: a lost filing adds the evidence that was absent to the requirement, and nothing is ever removed without a human edit.

reasonrequired to SUBMITCONCEDE if
product_not_received / physicalorder record · tracking delivered · (signature image or address match)already refunded
product_not_received / digitalorder record · access logalready refunded
fraudulent / physicalorder record · CE3.0 prior transactions · CE3.0 elements match · (delivered or address match)already refunded · no CE3.0 path
duplicateorder record · distinct ordersalready refunded
product_unacceptableorder record · customer communication · (refund policy shown or delivered)already refunded
credit_not_processedorder record · refund policy shown · customer communicationalready refunded
subscription_canceled / digitalorder record · access log · refund policy shownalready refunded
unrecognizedorder record · (delivered or customer communication)already refunded
anything else—HOLD to a human