decision_log is append-only. Every state transition an agent makes writes a row, and rows are never deleted. Each row carries the reasoning, the evidence behind it, and the cost confidence the agent had at the moment it acted. Learn to read one row and you can read the whole system.

An illustrative row
{
"id": "a1b2c3d4-9f2e-4b7a-8c1d-6e5f4a3b2c1d",
"store_id": "0f8e7d6c-…",
"product_id": "5b4a3c2d-…",
"variant_id": null,
"from_state": "ACTIVE",
"to_state": "OPTIMIZING",
"trigger": "roas_below_floor",
"evidence": {
"action": "optimize_loser",
"roas_30d": 0.8,
"reasoning": "ROAS under the store floor for 30 days; rewrite before discounting."
},
"cost_tier_at_decision": "B",
"applied_to_shopify": true,
"status": "applied",
"acted_at": "2026-06-24T06:12:40Z"
}The fields that matter
- evidence.action — the verb. Actions come from a fixed enum (keep, scale_winner, optimize_loser, discount_test, draft, vault, revive_seasonal, flag_orphan, wait); there is no free-form verb.
- from_state / to_state — the lifecycle transition, which must exist in ALLOWED_TRANSITIONS or the row would be an operator-review row instead.
- trigger — the condition that fired the planner. Machine-readable, so you can query 'everything roas_below_floor did this month'.
- evidence — the metrics snapshot and reasoning that drove the decision, frozen at decision time.
- cost_tier_at_decision — the cost confidence the agent actually had at decision time, not what's known now.
- status and applied_to_shopify — whether the row is a dry-run proposal, pending, applied, skipped, or failed, and whether it actually hit Shopify.
Following the evidence
The evidence field is a snapshot, not prose alone — the metrics windows, cost data, and reasoning the agent saw, frozen at decision time. Click through in the dashboard and you see the exact numbers behind the row. This is what makes the log an argument, not an assertion: the reasoning is reconstructable from the recorded inputs.
How a revert works
There is no stored undo script. The revert executor derives the inverse from evidence.action at revert time — a draft or vault republishes the product, a discount_test restores the original price, an optimize_loser restores the previous content version — then stamps reverted_at and reverted_by on the row and rolls the lifecycle state back. The reversal is itself an auditable fact, which is exactly why finance teams trust the table.
If you can't reconstruct why an action happened from its row alone, the row is wrong. That's the standard the schema is built to.
