# Public readiness decision rules

**Version:** 1.1.0

**Status:** design-only; no company result is asserted here

This file turns the catalog's check descriptions into repeatable decisions. It does not add points or change the 40/25/25/10 weights. It answers a narrower question: given a valid public observation, when is the check `pass`, `partial`, `fail`, `unknown`, or `N/A`?

The scorer still owns the hard gates. Any unresolved applicable check produces diagnostic lower/upper bounds and prevents `final_score` and ordinary rank eligibility. A measurement's existence never earns a pass by itself.

## Public-scope exclusions

The source URL-only audit distinguishes two kinds of exclusion:

- `N/A` applies only to a conditional capability that does not fit the site's observed public job. It needs an observed public-purpose reference and a reason.
- `not_publicly_verifiable` applies when the public job may fit but the evidence would require an owner, authenticated response, private product behavior, a write/commit action, or a limit/error probe outside the safe URL-only boundary. It is excluded from the public score denominator and listed as an owner-only follow-up. It is not a pass, fail, or N/A.

Unknown remains the status for feasible public evidence that has not been collected. This distinction prevents private product behavior from becoming an unfinishable universal checklist while preserving unresolved public work.

## Evidence context

Every performance observation records a `decision_rule_id` and, where numerical, one or more `measurements` in `scoring-schema.json`:

- `metric`: the measured quantity;
- `value` and `unit`;
- `source`: `field` or `lab`;
- `aggregation`: `p75`, `median`, `p90`, `max`, or `single`;
- `sample_count`;
- `conditions`: route, viewport, browser/device profile, network, cache state, authentication state, and interaction script.

`field` means real-user or CrUX-style data with a stated period, URL/origin scope, device segment, and percentile. A field Core Web Vital classification uses the 75th percentile. It is the source for a statement about users.

`lab` means a controlled browser trace. It is useful for repeatable diagnosis and comparison, but it is not field experience. A lab result may make a check known when the run protocol is complete, but the report must call it a lab diagnostic and must not call it a field Core Web Vital or claim user impact from it.

For a lab trace, use one warm-up followed by five recorded runs at each required viewport, with a fixed browser version, device/network preset, locale, logged-out state, route, and reduced-motion setting. Record run count and cache state. Fewer than three recorded runs, a changing route, a changing device/network preset, or an unexplained outlier makes the check `unknown`; do not fill the gap with a fabricated average. Five runs is the preferred minimum for a lab classification.

When a field p75 exists, use it for the field classification and keep the lab trace as diagnostic context. When field data is absent, use a complete lab trace only as a labeled lab classification. The source label must stay attached to the evidence and finding.

## Performance rules

The Core Web Vitals thresholds below are the current Google/web.dev good and poor bands. Good is the inclusive lower band. Poor is strictly above the poor boundary. Values between the two are `partial` / “needs improvement.”

| Check | Good | Partial | Poor / fail | Decision value |
| --- | ---: | ---: | ---: | --- |
| `reliability.performance.ttfb` | ≤800 ms | >800 and ≤1,800 ms | >1,800 ms | Field p75; lab median with the stability guard below |
| `reliability.performance.lcp` | ≤2,500 ms | >2,500 and ≤4,000 ms | >4,000 ms | Field p75; lab median with the stability guard below |
| `reliability.performance.cls` | ≤0.10 | >0.10 and ≤0.25 | >0.25 | Field p75; lab p90 across recorded runs |
| interaction component of `reliability.performance.transfer_interaction` | ≤200 ms INP | >200 and ≤500 ms INP | >500 ms INP | Field p75; lab median of scripted interaction runs |

These bands are not a new Lighthouse score. Google describes LCP, INP, and CLS as field metrics classified at the 75th percentile, and describes TTFB’s 800 ms / 1,800 ms bands as a rough guide rather than a Core Web Vital. See [Core Web Vitals thresholds](https://web.dev/articles/defining-core-web-vitals-thresholds), [TTFB](https://web.dev/articles/ttfb), and [INP](https://web.dev/articles/inp).

The lab p90 stability guard used below is an internal conservative rule for small repeated samples. It is not a Google field threshold and must remain labeled as lab evidence.

### TTFB: `reliability.performance.ttfb`

`pass` requires the applicable decision value in the good band and a complete measurement context. For lab data, the median must be ≤800 ms and the p90 must not exceed 1,800 ms. `partial` applies when the decision value is in the middle band, or when the median is good but the p90 is poor and the route is unstable. `fail` applies when the field p75 or lab median is above 1,800 ms. A single curl timing, a response duration without cache/redirect context, or a test with no stated route is `unknown`.

TTFB includes redirects, connection setup, TLS, request time, and response start. Report whether the result was cold, warm, or mixed cache. A higher TTFB can coexist with a good LCP on a server-rendered page; the TTFB rule is therefore a diagnostic quality rule, not an architectural verdict.

### LCP: `reliability.performance.lcp`

`pass` requires field p75 ≤2,500 ms or a complete lab proxy with median ≤2,500 ms and p90 ≤4,000 ms. `partial` applies to the middle band or to a good median whose p90 crosses the poor boundary. `fail` applies when field p75 or lab median is >4,000 ms. A trace that never identifies an LCP candidate, a trace with fewer than three recorded runs, or a trace whose page meaning differs across runs is `unknown`.

Do not turn the Lighthouse LCP metric's presence into a pass. The evidence must include the value, route, viewport, browser conditions, run count, and whether the largest element is stable and represents the page's primary content.

### CLS: `reliability.performance.cls`

`pass` requires field p75 ≤0.10 or lab p90 ≤0.10 across five runs. `partial` applies to the middle band or to a lab p90 in the middle band while the median is good. `fail` applies to field p75 >0.25 or lab p90 >0.25. Record the layout-shift sources when available: injected banners, images without dimensions, fonts, ads, embeds, or route transitions. A lab result that observes no meaningful page content or does not run through the initial loading state is `unknown`.

### Transfer and interaction: `reliability.performance.transfer_interaction`

This catalog check intentionally combines two kinds of evidence. It earns a status only when both the transfer side and the interaction side are decided:

- **Transfer side:** A pass requires a documented page/resource budget when one exists, no material render-blocking or oversized resource against that budget, reserved media dimensions, and evidence that the page reaches its primary content without an avoidable blocking resource. A site with no documented budget does not get an automatic pass from a byte count; use the observed blocking behavior and a stated local comparison rule. One recoverable heavy resource is `partial`. A dominant blocking resource, unbounded media/JS payload, or timeout/obstruction is `fail`.
- **Interaction side:** Use the INP bands above. A lab must execute at least three representative read-only interactions (for example, open navigation, focus/type into a search field, and activate a non-committing control). A page-load trace with no interaction is `unknown`; Total Blocking Time can explain a problem but is not an INP substitute.

Composite decision:

- `pass`: transfer side and interaction side both pass;
- `partial`: neither side fails and at least one side is partial;
- `fail`: either side fails;
- `unknown`: either side is unknown, including transfer measured without a defensible budget/behavior decision or no scripted interaction;
- `N/A`: only when the public page has no interaction or meaningful resource delivery at all, with a public-purpose reason. A normal article, directory, or homepage is not N/A merely because it has no form.

## HTTP, identity, and rendering rules

These rules apply to the catalog checks whose names can otherwise reward presence alone.

| Rule ID | Checks | Pass | Partial | Fail | Unknown / N/A |
| --- | --- | --- | --- | --- | --- |
| `http.core_contract` | `seo.http.preferred_origin`, `seo.http.core_page` | Preferred HTTPS origin resolves without a loop; representative page is 2xx with the intended content type and meaningful body. | One ordinary redirect or content-type wrinkle with a usable final page. | Persistent 5xx, unintended auth/challenge wall, wrong successful content type, or core page unavailable. | Transient/network-blocked result after safe retry is unknown. |
| `http.missing_contract` | `seo.http.not_found`, `reliability.errors.http_status`, `reliability.errors.404_noindex` | Deliberate missing URL returns 404/410, noindex where appropriate, and an actionable next step. | Correct status but vague recovery, or one missing noindex signal with no indexable content. | Soft 404, 200 error page, redirect to unrelated marketing page, or an unsafe/ambiguous error. | Missing URL was not requested or response was unavailable. |
| `http.machine_error` | `seo.http.machine_errors` | Applicable API/asset miss preserves status and a typed, useful error. | Status is correct but body is generic or missing retry detail. | HTML marketing page or 200 masquerades as a machine error. | N/A only when no public API/asset contract exists. |
| `canonical.identity` | `seo.canonical.*` | One canonical per applicable page, target is reachable, and hosts/variants/schema/OG/sitemap/Markdown agree. | A recoverable redirect or one non-material optional representation gap with no contradiction. | Multiple contradictory canonicals, canonical to a different fact/host, systematic variant conflict, or non-200 target. | A representation not exposed is N/A for that sub-surface; inability to inspect it is unknown. |
| `graph.discovery` | `seo.graph.*` | Representative applicable pages are linked from an indexable parent with bounded parameters and no silent orphan set. | One secondary route or boundedness edge case has a working fallback. | Core pages are orphaned, only reachable through noindex pages, or filters create an unbounded crawl space. | No sitemap/crawl comparison was performed. |
| `sitemap.policy` | `seo.sitemap.*` | robots/sitemap syntax, canonical URLs, membership, lastmod, child maps, and search/user/training policy agree. | One incomplete or stale optional map with core discovery intact. | Systemic omission, empty child map, contradictory canonical origin, or policy conflation affecting search retrieval. | Optional sitemap capability genuinely does not fit the site; otherwise unobserved is unknown. |
| `render.initial` | `seo.render.*` | Meaningful title, heading, facts, and links are present before or after reliable rendering with parity. | One non-critical fact or link is hydration-only but recoverable. | Core meaning is absent, contradicted, image/canvas-only, or blocked by an interstitial. | JavaScript/rendering state was not tested. |

## Answerability and public truth rules

| Rule ID | Checks | Pass | Partial | Fail |
| --- | --- | --- | --- | --- |
| `truth.public_contract` | `aeo.truth.*` | Identity, capability, availability, material commercial terms, support/trust, freshness, and cross-surface facts needed by the business type are explicit and agree. | One secondary fact or optional support surface is incomplete, with a clear fallback. | A material contradiction, unsupported capability, stale price/availability, or missing required public decision fact misleads a reasonable evaluator. |
| `schema.semantic_parity` | `aeo.schema.*` | Syntax parses, type/IDs fit the page, and all material structured facts match visible content and current destinations. | Syntax is valid but a non-material field is stale/incomplete, or an optional FAQ/HowTo surface is only partly aligned. | Structured data is invalid, uses the wrong entity/product, invents/stales price/date/author facts, or contradicts visible text. Presence of JSON-LD alone is not a pass. |
| `machine.reference_contract` | `aeo.machine.*` | Applicable representation is linked/fetched, correctly typed, canonical-linked, cache-safe, current, fact-parity checked, and errors correctly. | Representation exists but one metadata, cache, parity, or error detail is incomplete while HTML remains usable. | Broken link, HTML disguised as Markdown/API, contradictory facts, unsafe cache mixing, or empty manifest that merely repeats the homepage. |
| `discovery.map_contract` | `aeo.discovery.*` | Applicable map is bounded, links to useful canonical reference surfaces, and every sampled link resolves. | Map is useful but incomplete/stale in one section. | Broken/contradictory links or a map that sends retrieval toward obsolete/non-canonical content. Absence is N/A only when the capability is not useful for the public purpose. |

## Rendered operability rules

| Rule ID | Checks | Pass | Partial | Fail |
| --- | --- | --- | --- | --- |
| `a11y.core_path` | `agent.a11y.*` | All controls in the representative read-only path have valid roles, names, labels, focus order, visible state, mobile reachability, and announced dynamic changes. | Secondary control has a recoverable label/focus/state defect. | Core control is unnamed/unreachable, focus is trapped/lost, error state is inaccessible, or an overlay blocks the path. |
| `stability.layout` | `agent.stability.*` | Repeated observation/click does not move or obscure core controls; dimensions are reserved; loading and reduced motion are understandable. | A non-core shift or motion issue has a usable fallback. | Core control moves, disappears, overflows, or is obstructed during the safe path. A CLS number alone does not establish this check. |
| `task.public_boundary` | `agent.task.*` | Applicable task states intent, inputs, auth, cost/limits, result/error behavior, confirmation, idempotency, and escalation before commit. | One non-critical detail is unclear with a safe human fallback. | Side effect/commit boundary is ambiguous or unsafe, required input/auth/error behavior is hidden, or confirmation is missing. A read-only content site may mark the whole task group N/A with evidence. |
| `capability.contract` | `agent.capabilities.*` | A real applicable capability has a discoverable, non-empty contract with useful operations, versions/scopes/limits/errors, and confirmation boundaries. | Contract is public but incomplete or only partly usable. | Tool/API/skill is broken, empty, marketing-only, undocumented at the action boundary, or points to an unsafe/unverified path. A keyword or registration alone is not a pass. |
| `registration.ledger` | separate `observed_tool_registration` signal | `registered` only when a bounded runtime observation sees the registration event and records tool identity/timing. | N/A. | `not_registered` only after the same bounded observation. No runtime observation is `not_measured`, never fail. | This signal does not prove invocation, execution, adoption, or conversion. |

## Reliability, freshness, and error rules

| Rule ID | Checks | Pass | Partial | Fail |
| --- | --- | --- | --- | --- |
| `cache.representation` | `reliability.cache.*` | Cache directives fit public/personalized content, `Vary` separates negotiated forms, and challenges/errors keep their status and type. | One static/cache optimization is imperfect but does not mix users or representations. | Personalized data is publicly cacheable, negotiated forms mix, or a challenge is served as a successful document/API. A `Vary` check is N/A only when no negotiation exists. |
| `freshness.provenance` | `reliability.freshness.*` | Effective dates, current commercial/compatibility state, provenance, and replacement/deprecation state are explicit where applicable. | One non-material freshness/provenance field is missing with a usable fallback. | Material fact is stale, date is fabricated, source/author/methodology is misrepresented, or deprecated surface has no safe replacement. |
| `errors.recovery` | `reliability.errors.*` | Status, explanation, safe next step, auth distinction, and retry behavior are correct for the applicable error class. | Correct status but vague recovery or one secondary retry detail missing. | Soft status, misleading next step, unsafe auth disclosure, or silent temporary/rate-limit failure. Auth/rate-limit checks are N/A only when no such public capability exists. |

## Applying a rule to a check

1. Select the most specific `decision_rule_id` and record it on the check.
2. Decide applicability before inspecting whether the feature exists. If the business type makes the capability relevant, absence is a measured gap or unknown; it is not N/A.
3. Capture the direct evidence and test context. A grader or browser trace can corroborate, but a grader's presence check cannot substitute for fact parity or task evidence.
4. Assign `pass`, `partial`, or `fail` only when the rule's evidence conditions are met. A metric value without a route, source, aggregation, conditions, or adequate sample count is `unknown`.
5. For `unknown`, preserve the reason and use an empty evidence-reference list when no observation was made. Do not invent a placeholder URL or timestamp.
6. Keep the existing point weights. The scorer maps pass/partial/fail to 1/0.5/0, removes justified N/A from the denominator, and retains unknown as unresolved.
7. Final scores and ordinary rank eligibility require every applicable weighted check to be known and all critical evidence gates to be known. Otherwise emit only explicit lower/upper bounds and qualitative findings.

## Sources read for these rules

The numeric performance bands and field/lab distinctions are based on a small current primary-source set:

- [Google/web.dev: Core Web Vitals thresholds](https://web.dev/articles/defining-core-web-vitals-thresholds) — LCP ≤2.5 s good and >4 s poor; INP ≤200 ms good and >500 ms poor; CLS ≤0.1 good and >0.25 poor; 75th-percentile field classification.
- [Google/web.dev: TTFB](https://web.dev/articles/ttfb) — TTFB is foundational in both lab and field, ≤0.8 s is a rough good guide, >1.8 s is poor, and it is not a Core Web Vital.
- [Google/web.dev: INP](https://web.dev/articles/inp) — field p75 is preferred; lab INP depends on the interactions scripted, and TBT is only a proxy when no INP is available.
- [Chrome for Developers: Lighthouse performance scoring](https://developer.chrome.com/docs/lighthouse/performance/performance-scoring) — metric values, not diagnostics alone, determine the Lighthouse score; lab results fluctuate with conditions. This protocol does not import Lighthouse's overall score.

These references justify the bands and the distinction between measurement and quality judgment. They do not provide a company score or evidence about any Lazyweb row.
