Defining Acceptance Criteria for AI Features That Ingest Web Content
Web-ingestion pipelines need acceptance criteria beyond model quality alone.

Acceptance criteria for AI features that ingest web content need to cover four things traditional AC never had to touch: freshness thresholds, content fidelity, structured extraction accuracy, and fallback behavior when the ingested content is stale or broken. Skipping that layer means the AI's output quality can't be tested, no matter how good the underlying model is.
Traditional acceptance criteria and AI features that read the web
Most engineering teams have lived through some version of the same story: a launch slips because "done" meant different things to different people. Someone thought the login flow just needed to work on desktop. Someone else assumed mobile was in scope. Nobody wrote it down, so nobody could point to the sentence that settled it. That's the whole reason acceptance criteria exist, to pin down, in advance, what "done" actually means so the argument never happens.
Acceptance criteria are usually anchored to a simple idea: predefined conditions a user story has to satisfy before it counts as complete, stated in terms of outcomes rather than implementation. That framing holds up fine for a login form or a checkout flow, because the inputs are known and the system's behavior is deterministic. Feed it X, get Y, within bounds Z.
AI features that read the web break that model, because correctness now depends on two things at once: how well the model reasons, and how fresh, clean, and complete the content it was handed actually turned out to be. A user story written purely in terms of outcomes has no way to express that second variable. The web-data layer sits between the story and the model's reasoning, invisible to a spec that was never built to describe it, and that's the gap this piece is about closing.
Failure modes a model-only evaluation cannot catch
Three separate failure modes live inside the web-ingestion stack, and each one produces the same symptom: a confident, plausible, wrong answer. They sit at different layers of the pipeline, so fixing one does none of the work of fixing the others. A team that tests only the model's reasoning, on clean inputs, will miss all three, since each failure mode operates at a different layer of the stack and catching one does not catch the others.
The first is a fetch-layer failure. If the tool can't get past anti-bot protection or render a JavaScript-heavy page, the model never sees the content at all. The pipeline hands back nothing, or partial HTML, and the model reasons over a gap it has no way to detect.
The second is a format-layer failure. Raw HTML is dense with tokens that carry no meaning: scripts, stylesheets, navigation menus, ad markup. That noise distracts the model and drives up API costs, and if the content wasn't converted into clean Markdown or structured JSON before it reached the model, reasoning quality degrades even when the fetch itself worked perfectly.
The third is staleness, and it's the hardest one to catch because it produces no visible signal. A stale search result at least carries a publish date the user can see. A stale product page fed into an LLM carries no such marker, so the model reasons over three-month-old information without any way to flag the uncertainty, and neither does the person reading its answer.
A fourth failure sits one layer over, at the schema boundary rather than the fetch or format boundary, and the clearest illustration of it comes from a documented case involving BAML. A leading LLM, asked to extract structured data from an invoice, returned "quantity": 1 under a structured-output schema when the real value was 0.46 kilograms. The same model, asked the same question in free-form text, got it right. The JSON passed validation. The invoice total did not. Schema conformance and semantic accuracy are different properties of the same output, and criteria that check only the first will wave through failures the second would have caught.
Nightly re-indexing and static freshness assumptions
Freshness has usually been treated as background maintenance, something a scheduled job takes care of while the product team focuses on features. That's a design decision with consequences, and by 2026 it counts as a documented failure mode rather than a safe default.
Most RAG systems still re-index overnight. Neither cost is theoretical anymore. Both are the direct, measurable consequence of a schedule chosen for convenience rather than for how fast the underlying content actually changes.
The web itself doesn't run on a nightly clock. Automated traffic now exceeds human traffic across the web, and content that matters, pricing, inventory, availability, can shift multiple times inside a single day.
Freshness has also started to matter beyond the answer itself. Content published more recently earns substantially more citations from AI search engines than older pages does. So a stale ingestion pipeline doesn't just risk a wrong answer, it risks the system's sources losing credibility with the downstream AI systems that decide what gets cited and what gets ignored.
The freshness TTL contract needs an owner, product, data engineering, or the model team, and a gate that enforces it before a feature ships. Leaving that question out of the AC means the answer defaults to whatever the infrastructure team happened to configure first, almost always a fixed schedule chosen for reasons that have nothing to do with how the content actually decays.
What the agentic web demands from static websites
The web is turning into a dual-audience medium, built to serve two consumers with genuinely different, sometimes incompatible needs. That changes what "acceptable" content even means, and it means AC has to specify which version of a page the feature is actually reading.
Human visitors want a page that feels intuitive and holds their attention. AI agents want the opposite: structured, machine-readable data they can consume at scale without the layout, styling, or narrative flourish getting in the way. A single website increasingly has to satisfy both audiences at once, and it rarely does so with a single representation of the content.
That split gets harder to manage once bot traffic itself stops being trustworthy by default. Automated traffic makes up most of all web traffic, and WP Engine's 2025 Website Traffic Trends Report found that 76% of bot traffic comes from unverified sources. An ingestion pipeline with no way to tell verified, structured, agent-ready content apart from unverified or dynamically assembled content is pulling in noise right alongside whatever signal it's actually after.
Even the idea of "the page" has stopped meaning one thing. Browsers reorder content, inject AI summaries, generate adaptive views, and modify layouts dynamically, so the rendered experience increasingly diverges from the source code. An acceptance criterion that says "fetch the page" has to specify which page: the raw source HTML, the rendered DOM after scripts run, or a structured data layer meant for machines. Those are three different objects, and a pipeline built against the wrong one will pass its own tests while still feeding the model something no one intended.
The stakes climb further as AI agents move from making recommendations to executing full transactions on a person's behalf. An agent reasoning over stale pricing or a malformed product listing isn't just risking a bad answer anymore. It may be one bad fetch away from completing the wrong purchase, at the wrong price, on someone else's behalf.
The four dimensions that web-data AC must explicitly specify
Every failure mode described so far maps to something an acceptance criterion can actually name and test. Acceptance criteria for AI features that ingest web content need to cover four distinct dimensions, fetch fidelity, content quality, freshness, and structured extraction accuracy, because each one corresponds to a failure the model itself has no way to report. A model that got a blank page, a noisy page, a stale page, or a mis-extracted field will answer with the same confident tone regardless, so the criteria have to catch what the model can't.
Fetch fidelity comes first, specifying that the tool retrieves the target content under real production conditions, including pages rendered dynamically and pages sitting behind anti-bot defenses. "The page was fetched" isn't a sufficient bar to clear if what came back was partial HTML or a block page dressed up as content. Content quality follows close behind it, covering whether the raw material was converted into a clean, low-noise format, stripped of scripts, styling, and navigation clutter, before it ever reached the model.
Freshness is the third dimension, and the criterion needs to name an actual maximum-age threshold for each content type, along with what happens once that threshold is crossed: re-fetch, invalidate the cached chunk, or surface a confidence flag to whatever consumes the answer. The right unit for this is a per-topic TTL rather than one blanket number, since a live-web RAG architecture governs the choice between fetching fresh and reusing a cached chunk exactly this way, on the logic that a pricing page decays far faster than an About page ever will. Structured extraction accuracy has to separate schema conformance from semantic accuracy: a JSON response that validates against the schema is not evidence that the extracted value is correct, and the BAML quantity-and-weight case is the canonical failure here.
Fallback behavior and escalation paths as first-class acceptance criteria, not edge cases
A feature whose AC only describes what happens when ingestion succeeds has quietly handed engineering a decision it shouldn't be making alone: what to do when the web-data layer fails. Different engineers, faced with that gap, will fill it differently, and the result is a product where one feature degrades gracefully and another fails silently, with no design reason for the difference.
Traditional software treats errors as binary: the system returns a result, or it returns an error code, and either way something legible happened. AI systems don't offer that courtesy. Whether that answer is right or wrong is the only place the failure appears, since they produce an answer regardless of what they were fed and raise no signal on their own. Criteria silent on escalation paths, confidence thresholds, fallback behavior, and user-facing disclosures are a decision. They're a decision, just one nobody made on purpose, and the predictable outcome is inconsistent behavior across features that all rely on the same ingestion stack.
Monitoring belongs inside the AC too, not off to the side as an infrastructure concern to sort out later. If a source page matters enough to ingest in the first place, the feature needs a defined trigger for re-ingesting or invalidating that content when the source changes. The web is now a dual-audience medium, and AI features ingesting it operate in an environment built for two consumers with incompatible requirements, so AC must specify which audience's version of the content the feature is ingesting. The older model alerts a human, who then decides what to do with the notification. The newer model treats a detected change as an event inside a data pipeline, consumable directly by other systems or agents without a person in the loop. Those two models carry different latency, different reliability guarantees, and different failure surfaces, so the AC has to name which one a given feature actually uses rather than leaving that as an implementation detail.
A workable fallback hierarchy gives engineering somewhere concrete to land: try re-fetching from the source first, then fall back to cached content paired with a staleness disclosure, and only after both of those fail, decline to answer with an explanation of why. The AC needs to pick a fallback hierarchy and write it down, because leaving it implicit produces different fallback behaviors across different features built on the same pipeline.
Assigning ownership of web-data criteria across teams
None of the four dimensions above enforces itself. Each one needs an owner, and web-data AC tends to fail in practice because ownership was never assigned. It's that no one on the team was ever assigned the web-data layer, so it falls into the space between product's user story and engineering's implementation ticket.
Product tends to own AC for user-facing behavior, the what of the feature, but a freshness TTL, a fetch fidelity bar, or a fallback policy needs to be named explicitly in a story written purely in terms of user outcomes. Nobody's being negligent here. The ingestion contract just sits below the altitude a typical user story is written at.
Data engineering owns the pipeline itself but is usually absent when the user story gets defined, so the team ends up building to whatever the existing infrastructure allows rather than to what the feature genuinely needs. That's a structural gap, not a communication failure, and it closes only when someone puts data engineering in the AC conversation before the ticket gets written, not after.
The model team faces its own version of the same blind spot. It can evaluate whether an output is good or bad, but it has no way to tell whether a bad output originated in the model's reasoning or in the content the model was handed, unless an ingestion contract already exists to check against. Without that baseline, debugging a quality complaint becomes a guessing game between teams, each pointing at a different layer of the stack.
Web-data AC should be reviewed and signed off before a feature enters implementation, not after QA discovers that the model's answers are wrong and the root cause turns out to be the ingestion layer. That review doesn't require inventing a new format from scratch. The case for a standalone "web-data contract," sitting next to the user story, is that it doesn't replace Given/When/Then or checklist-style AC, which still do their job fine. It adds a layer that names the ingestion-side conditions those formats were never built to express.
Writing web-data AC in practice: patterns for freshness, fidelity, extraction, and fallback
None of this requires new tooling or a new AC format. Web-data AC fits inside the Given/When/Then and checklist structures teams already use for everything else. The extension is naming the ingestion-layer preconditions and outcomes those formats have always left blank.
A freshness criterion might read: given that the source page was last fetched more than the defined TTL ago, when the feature is invoked, then the system re-fetches before grounding its answer in that content. The TTL is a specific, reviewable number attached to the ticket, decided deliberately rather than defaulted to by some infrastructure script.
A fetch fidelity criterion might read: given a JavaScript-rendered target page, when the ingestion tool fetches it, then the returned content includes the named elements the feature actually needs and excludes navigation, script, and style noise. That's testable on its own, with no model involved, before the content ever reaches the reasoning layer.
A structured extraction criterion might read: given a schema-defined extraction of a specific field, when the extracted value is validated, then its semantic accuracy gets checked against the source text itself, not only against whether the JSON conforms to the schema. That second check is what would have caught the BAML quantity-and-weight failure before it ever reached a customer's invoice.
A fallback criterion might read: given that the fetched content is older than the defined TTL and a re-fetch attempt fails, when the feature generates an answer, then it surfaces a staleness disclosure, declines to answer with an explanation, or escalates to a human review queue, whichever behavior the team named in advance.
Managed scraping infrastructure narrows how much of this work a team has to build by hand. A dedicated scraping API can absorb headless browser management, proxy rotation, and anti-bot handling, all the plumbing that used to eat a sprint on its own. But even the best managed API doesn't remove the need to write these criteria. The AC still has to say what the feature requires from that API's output, because the API's contract and the feature's contract are two different documents, and confusing them is how a well-built pipeline still ships a feature nobody specified.


