The fix_outcomes Ledger: SEO Decisions Backed by Verified History
Every fix yeet ever closed out gets a permanent record — and every new fix consults it before it's allowed a confidence score.
Most SEO tooling scores a proposed fix on priors baked into a model at training time. yeet-seo scores it against what actually happened the last time this exact kind of fix was tried — across your workspace and, in aggregate, across every workspace on the platform. That record is the fix_outcomes ledger, and it's the difference between a confidence number that's a guess and one that's a bet with a track record behind it.
This matters more than it might sound like on first read. A static confidence model — "meta description rewrites are usually safe" — can't tell you that your site's meta description rewrites have regressed twice in the last month, or that a competitor's CMS quirk makes schema_markup injections fail silently on dynamic routes. The ledger is what lets yeet's confidence scores actually track reality per workspace instead of shipping a single global assumption to everyone.
What gets written
Every card that reaches a terminal state — verified, regressed, or dismissed — writes one row:
{
"subcategory": "meta_tags",
"confidence": 0.91,
"predicted_delta": "+0.6 CTR pts",
"outcome_state": "verified",
"days_to_terminal": 9
}
outcome_state is set by the same verification pass described in how yeet verifies fixes — a live re-scrape plus GSC/AI-citation corroboration, with verified requiring the fix hit at least 50% of its predicted delta. days_to_terminal is how long it took to know, which turns out to matter as much as the outcome itself: a subcategory that verifies reliably but takes six weeks to know is a very different bet than one that resolves in four days.
Reading it back
Query the aggregate directly:
GET /api/v1/outcomes
Authorization: Bearer yseo_your_key_here
or, from MCP, get_outcome_stats with an optional subcategory filter:
{
"subcategory": "schema_markup",
"workspace": {
"verified_rate": 0.78,
"regressed_rate": 0.04,
"median_days_to_verify": 6
},
"global": {
"verified_rate": 0.71,
"regressed_rate": 0.07,
"median_days_to_verify": 8
}
}
global is a cross-tenant aggregate — rates and medians only, no other workspace's URLs, cards, or content ever surface in this response. It exists so a brand-new workspace with no fix history of its own still gets a meaningful prior on day one, instead of every subcategory starting at some arbitrary default confidence.
How the investigator actually uses this
When the investigator agent (one of the run_agent_job kinds — see MCP setup for the full kind list) scores a newly detected issue, it doesn't set confidence from the strength of the signal alone. It consults outcome_priors for that subcategory first — your workspace's history if there's enough of it, falling back to the global aggregate otherwise — and calibrates the card's confidence against how that class of fix has actually resolved historically. A meta_tags fix in a workspace where meta_tags verifies 90% of the time starts from a higher base than the same signal strength would earn in a subcategory that regresses a third of the time. This is also why the same underlying detection can carry different confidence in different workspaces — the ledger, not just the current page, is part of the input. This is the mechanism that feeds the 0.85 confidence floor gating auto-merge in the auto-apply deep dive — a subcategory with a weak verified history structurally has a harder time clearing that floor, independent of any single card looking plausible.
How an agent should actually use this
If you're wiring an agent to make autonomy decisions — not just react to cards but decide how aggressively to let yeet act — get_outcome_stats is the tool to call before widening scope, not after something regresses. Two concrete patterns:
- Gate risky actions on verified_rate. Before flipping a workspace into
auto_apply_aggressive(which drops the confidence floor to 0.75 for apply, though never for merge), pullget_outcome_statsfor the subcategories you'd be exposing and checkregressed_rate. A subcategory regressing more than roughly 1 in 10 times is a reasonable bar for keeping it out of unattended mode regardless of what the mode nominally allows. - Expected-value math against action credits. A PR costs 2 credits to open, a full investigation costs 3, a hands-off verified fix costs 5 (see the cost table in agentic SEO workflows). If a subcategory's
verified_rateis 0.4, the expected credit cost per verified outcome is roughly cost ÷ verified_rate — for a 5-credit fix at 0.4, that's 12.5 credits per actual win. Compare that against your remaining daily/monthly quota before letting an agent loop spend freely on a low-verified-rate subcategory.
Neither of these requires bespoke tracking on your end — the ledger already has the numbers; the agent just has to ask before it acts, not after.
A worked example
Say a workspace has run 40 meta_tags fixes to a terminal state: 34 verified, 3 regressed, 3 dismissed. get_outcome_stats({ subcategory: "meta_tags" }) returns a workspace.verified_rate of 0.85 and regressed_rate of 0.075. That's right at the auto-merge confidence floor discussed in the auto-apply deep dive — a new meta_tags card in this workspace has real historical grounds to clear 0.85, whereas the same workspace's cannibalization fixes, if they've verified only 6 of 15 attempts, sit at a 0.4 verified_rate that keeps confidence conservative no matter how clean any single new case looks. This is precisely why two structurally similar-looking fixes in the same workspace can land in different autonomy treatment — one auto-merges, the other waits in awaiting_approval for a human.
Quick FAQ
Q: Does days_to_terminal include dismissed cards?
A: Yes — outcome_state: dismissed writes a row too, with whatever elapsed before a human or agent dismissed it, which is its own useful signal for subcategories that tend to get proposed but rejected.
Q: Can I see another workspace's outcome data?
A: No — global is aggregate rates and medians only; there's no path from that response back to another tenant's cards, URLs, or content.
Q: Does a low global verified_rate mean I should never try that subcategory?
A: No — it means confidence for that subcategory starts conservative and the auto-merge floor is harder to clear; it's still worth attempting under propose_only or manual apply, where a human reviews before anything ships. See autonomy modes for how each mode treats exactly this tradeoff.