Optimize
Optimizations
Page-level recommendations checked against the sources AI engines actually cite, and what to do with one.

What this is for
Citation gaps tells you which pages are losing a prompt to a competitor and why. Optimizations picks up from there: for a gap with a page of yours behind it, it reads the pages AI engines are actually citing instead of yours, works out the specific thing they have that yours does not, and writes a draft for that one page, grounded in at least one real quote taken from the page it is based on, not an invented one. Ask an engine "what's the best invoice software for a small agency?" and it answers from a handful of pages it trusts; a generic on-page checklist (title tags, word count, keyword density) has nothing to say about why one of those pages got picked and yours did not. Optimizations names the specific fix that page cluster is actually rewarding.
What an optimization is
An optimization is a diagnosis, not an edit. DiscoveredBy reads your page
and its cited competitors and decides what your page is missing: an FAQ
block, a comparison table, a statistic, structured data, a fresher date, a
clearer heading. What actually gets checked is narrower than the resulting
draft. A proposed change survives only if at least one of its cited
evidence quotes is a real phrase, at least sixteen characters, that
appears in the scraped text of the source page it claims to come from once
case and extra whitespace are ignored; a change with no such quote is
dropped before you ever see it
(api/services/optimization/service.py#_verified_quotes,
_verified_changes). That check covers the evidence, not the draft body:
the Markdown, HTML, or JSON-LD text you would actually paste in is never
itself compared against a source, so read a draft as a starting point to
check, not as text already verified word for word. The quote behind a
change is not shown on the screen either, only the change itself is. A
regulated topic (medical, legal, financial, security) gets a review flag;
the flag does not change the wording of the draft. You read the draft,
decide whether to use it, and paste it into your own page yourself, on
your own schedule; nothing here reaches your site without you putting it
there.
What you see
Optimizations opens on the single highest-scoring page still waiting on a fix, its opportunity score, and a "Write the fix" button if nothing has been drafted for it yet. When a target or a required source page has not been scraped yet, a banner says so instead of showing a draft. Filter tabs split the rest into active, ready (a draft exists and nothing has happened to it yet), applied, stale, dismissed, and all. Each row shows the opportunity score, the page, its status, three signals (expected impact, implementation effort, confidence), and whichever of Generate, Mark applied, Measure, or Dismiss applies to it. A drafted recommendation renders inline under its row: each proposed change shows its kind, priority, a review flag on anything regulated, the rationale, the anchor it targets on your page, and a copy button over the draft text. Only one draft format ever shows per change: Markdown first if the writer produced it, then plain replacement text, then HTML, then a stringified JSON-LD block, in that order, even when more than one exists underneath.
How it works
Every optimization starts as a citation gap opportunity
(Citation gaps) with a page of yours
identified as the one that should be winning the prompt. A gap with no page
of yours behind it never becomes an optimization at all
(api/services/optimization/service.py#_upsert_opportunity_from_gap). The
fix for that gap is a page that does not exist yet, which is what
Articles is for. Scoring it here
weighs the gap's own impact score most heavily, adds a bonus for the extra
prompts the same fix would help, capped at five extra prompts (the "N
prompts affected" line), adds for how far under your visibility is, how
commercial the prompt looks, how consistent the evidence pattern is, and
the opportunity's own confidence value (see "What the numbers mean" below
for what feeds it), then subtracts a penalty for a harder fix
(api/services/optimization/service.py#_opportunity_score). This feature
is not on every plan, and how many opportunities a run works through is
bounded by your plan too; see Pricing for what each plan
includes (_optimization_limit). On a plan that includes it, the weekly
on-page optimization run scores your project's opportunities and then
drafts only for the ones with no live recommendation already behind them,
without you pressing anything
(api/tasks.py#_run_optimization_batch_for_project,
api/services/optimization/service.py#opportunity_ids_needing_recommendation).
A recommendation counts as live, and holds the run off that opportunity, in exactly two cases:
- It is open: a draft you have neither applied nor dismissed.
Generating would reuse that same row in place, replacing its changes
and resetting its status and outcome
(
api/services/optimization/service.py#_open_recommendation_for_opportunity), so a draft you had read and were about to paste in would be gone with no notice. - It is applied and still inside its measurement window, which runs
for the same 30 days a firm verdict waits for
(
api/services/optimization/service.py#MEASUREMENT_WINDOW_DAYS,#still_measuring_clause). Generating does not reuse an applied row, it inserts a second one, so the applied record, its outcome and its Measure button would drop out of view before the verdict they exist for could be read.
The other two statuses a recommendation can hold never block a run. Stale exists precisely so a draft written against a page that has since changed gets rebuilt, and dismissed means you asked for something else, not for the opportunity to go quiet. An applied recommendation whose 30 days have elapsed does not block one either. Nothing here is silenced for good: an opportunity the run skipped comes back the moment you decide on its draft or the measurement window closes.
The screen orders the same way. Where an opportunity has more than one
recommendation, the row shows an applied one still inside its measurement
window if there is one, and otherwise the most recent recommendation that
has not been dismissed
(api/routers/optimizations.py#_latest_recommendation). Once the window
closes the newest row wins again, so a long-settled applied record does
not sit on top of current work. Dismissing a draft with the X on its row
still marks only that one draft dismissed
(api/services/optimization/service.py#mark_recommendation_dismissed).
The "Refresh" button on the screen re-scores on demand, under the same
plan bound, but leaves drafting to you, one "Write the fix" click at a
time (api/routers/optimizations.py#refresh_project_optimizations).
A draft cannot be written until DiscoveredBy has actually read the pages
involved. Your own target page has to be scraped first, and so do at least
the top few cited source pages behind the opportunity: two of them by
default, or all of them if fewer than two exist
(api/services/optimization/service.py#_REQUIRED_SOURCE_PAGES, drawn from
up to five cited URLs kept per opportunity,
api/services/optimization/service.py#_SOURCE_URL_LIMIT). When a required
page has not been fetched yet, nothing gets written this run: the missing
pages are queued for the scraper instead, and the next run, weekly or your
next manual "Write the fix", tries again once they are in.
The writer's own output can also fail outright, or produce nothing that
verifies. Either way, a fallback writes one change instead: it takes a
real sentence, verbatim, from the top source page and puts it in that
change's evidence, but the drafted text itself is a fixed template naming
the source and telling you what to cover, not page content you can paste
as written
(api/services/optimization/service.py#_fallback_recommendation_payload,
_fallback_markdown). If nothing can be grounded even that way, the run
produces no recommendation at all rather than an unverified one.
You mark a fix applied yourself; doing so captures the trailing month of
visibility for its prompts as the baseline to compare against later, and
immediately shows the outcome as "pending more data"
(api/services/optimization/service.py#mark_recommendation_applied).
"Measure" later compares your current visibility for those same prompts
against that baseline and writes one of four labels
(measure_optimization_outcome, _classify_outcome). Your click is no
longer the only thing that can run that comparison. A fan-out job selects
every applied recommendation whose first checkpoint is now reachable and
whose verdict is still unsettled, meaning its outcome is either empty or
reads "pending more data", and queues the same measurement for each one
(api/services/optimization/service.py#recommendation_ids_due_for_measurement,
api/tasks.py#_enqueue_optimization_outcomes,
api/cli.py#cmd_enqueue_optimization_outcomes). A row drops out of that
selection by itself, because from the month mark the verdict is always
firm. That job and the daily schedule written for it,
discoveredby-cli@enqueue-optimization-outcomes.timer, were added to the
codebase in the same change; a schedule only starts firing once a
deployment installs it, so pressing Measure yourself is still the step
that always works.
One more transition here happens without you: if your target page's
content changes after a draft was written, the next day's stale check catches it
and flips the recommendation to stale
(api/services/optimization/service.py#mark_stale_optimizations), on a
real daily timer (discoveredby-cli@mark-stale-optimizations.timer).
A stale draft is one written against a page that has since moved on; the
next weekly run reopens it with a fresh draft.
What the numbers mean
Opportunity score, out of 100, ranks pages against each other for this
project; read it as a relative order, not a percentage of anything.
"Opportunity score" also names a different figure on
Articles, which orders topics rather than pages
(api/routers/articles.py#list_article_topics): the two are separate
numbers that happen to share a name, so a figure here does not translate
into one there. The
confidence shown on a row belongs to the opportunity, not to any one
proposed change: it blends the citation gap's own confidence label, how
much evidence and how many source pages back the pattern, and the gap's
impact score
(api/services/optimization/service.py#_upsert_opportunity_from_gap).
Effort (low, medium, high) is a fixed estimate per fix type, not a
measurement of your specific page.
Outcome starts as "pending more data" the moment you mark a fix applied,
and stays there until you check it. Under a week since applying, checking
it always reads "pending more data" again: too little has run since to say
anything. From a week to a month, it stays "pending" unless your
visibility has already jumped by ten points or more, in which case it
reads "positive" early. At the month mark, "positive" needs five points or
more, "negative" needs five points or more the other way, and anything
smaller than that either way reads "neutral", meaning nothing definitive
happened yet, not necessarily that the fix failed
(api/services/optimization/service.py#_OUTCOME_MIN_DAYS,
_OUTCOME_FIRM_DAYS, _OUTCOME_POSITIVE_DELTA, _OUTCOME_NEGATIVE_DELTA,
_OUTCOME_STRONG_DELTA). For how to read a change against a baseline in
general, see
How to read any number here.
If a page you expected to see cited still is not, start with
Troubleshooting: No citations yet.
Related
- Citation gaps: where an optimization opportunity comes from, and how it is scored before this page ever sees it
- Articles: the other route from a citation gap, for when the page that should win the prompt does not exist yet
- Domains: what an owned page is, including the prompt mapping the page an optimization targets can come from
- Metrics defined: the citation concept behind the visibility numbers compared here
- Troubleshooting: No citations yet: what to check before expecting a fix to change anything
- Pricing: which plans include on-page optimization
Last verified 2026-08-11