Scoring

Four separate outputs: readiness, coverage, confidence and assessment scope. Inferred, agent and experimental observations never enter readiness.

Definitions

For each scored rule r with weight w(r) ∈ {1, 2, 3}, applicable subjects are partitioned (after removing not-applicable ones) into direct passes, direct fails, inferred, and unassessed (not_evaluated or error). Each site-level rule contributes one site subject.

p(r)       = |D_f(r)| / |D(r)|                       for r with direct evidence
Readiness  = 100 · Σ w(r)·(1 − p(r)) / Σ w(r)         over rules with direct evidence
Coverage   = 100 · Σ w(r)·|D|/(|D|+|I|+|U|) / Σ w(r)  over applicable rules
Confidence = 100 · Σ w(r)·|D|/(|D|+|I|) / Σ w(r)      over rules with direct or inferred evidence
  • Readiness is null with no direct evidence; coverage is 0 with no applicable rules; confidence is null with nothing evaluated.
  • Unrounded throughout; display rounds half away from zero, once.
  • Sub-checks. A fail on any evaluated sub-check fails the subject. All evaluated sub-checks passing while one applicable sub-check could not run is not_evaluated (incomplete_subchecks). A rule never passes on part of its own checks.
  • Blocker cap. Readiness is capped at 40 when direct evidence shows robots.txt answering 5xx, a search crawler blocked site-wide against declared policy, or every route noindex. The uncapped value and the reason are kept.
  • Display. Coverage ≥ 80% shows readiness; 50–80% shows it as provisional; below 50% (or no readiness) shows “insufficient evidence” and no number.

The score range

Every report also gives each rule one result: Clear, Problem, Can’t tell or Not applicable. From those, the CLI and the agent plugins calculate one score with one formula, shown as a range. The low end assumes every Can’t tell rule is a problem, and the high end assumes every one is clear. The CLI puts this in the report’s summary. Readiness below stays stricter: a rule never passes on part of its checks.

If bot protection or a firewall answers instead of the site, the report says it was blocked, sets blocked, and passes or fails nothing about the site.

Worked examples

These are executable fixtures in packages/geopilot/test/fixtures/scoring. The test suite recomputes every number below from them on every change, and this page reads the same files, so the prose cannot drift from the engine.

Example A — Static site without a renderer

Three route groups (/, /about, /contact). GEO-016 is evaluated from raw HTML: without a renderer the raw response is the server HTML, so a plain <a href> in it is server-link evidence. GEO-003 and GEO-002 need a renderer and are not evaluated. GEO-004 and GEO-101 are not applicable by fixture precondition: no JSON-LD and the web profile.

ReadinessCoverageConfidenceCap
9683%100%—
Readiness = 100 × (19 − 2 × 1/3) / 19 = 96.49 → 96; Coverage = 100 × 19 / 23 = 82.6% → 83%; Confidence = 100%

Example B-APP — Example B with the app profile

As example B, with the app profile on and GEO-101 passing (weight 2).

ReadinessCoverageConfidenceCap
40100%100%GEO-013: robots.txt answered 5xx
Uncapped = 100 × (27 − 3.5) / 27 = 87.04; capped at 40

Example B — Next.js, four groups, robots.txt 503 and one canonical issue

All web-profile rules are evaluated directly (this fixture assumes a renderer and was collected with --ignore-robots for a named target, because a 5xx robots.txt otherwise stops GEO fetching pages). GEO-013 fails site-wide with a 5xx, which caps readiness at 40. GEO-015 fails on one of four groups. GEO-101 is not applicable in the web profile.

ReadinessCoverageConfidenceCap
40100%100%GEO-013: robots.txt answered 5xx
Uncapped = 100 × (25 − 3 − 2 × 1/4) / 25 = 86; capped at 40; Coverage and Confidence 100%

Example C — Unreachable target

Every applicable evaluation errors. Readiness is null, coverage 0%, confidence null; the assessment is incomplete and the display is “insufficient evidence”. Without --fail-on-incomplete the scan still exits 0, but never with “passed” wording.

ReadinessCoverageConfidenceCap
insufficient evidence0%n/a—
No direct evidence: Readiness null, Coverage 0%, Confidence null

Example D — Repository only, recomputed under the sub-check rule

Next.js repository, four groups, no --url, web profile. GEO-009 and GEO-017 are fully evaluable from source and pass. GEO-012 fails on one group. GEO-003 is inferred. GEO-013, GEO-014, GEO-015 and GEO-011 pass their static sub-checks but cannot run their HTTP sub-checks, so they are not_evaluated (incomplete_subchecks). GEO-016 and GEO-002 need a URL. The same numbers are produced end-to-end by test/fixtures/example-d.

ReadinessCoverageConfidenceCap
insufficient evidence30%70%—
Readiness = 100 × (7 − 0.5) / 7 = 92.86 (hidden); Coverage = 100 × 7 / 23 = 30.4% → 30%: below 50%, so “insufficient evidence”; Confidence = 100 × 7 / (7 + 3) = 70%

Example E — Mixed evidence (URL + repository)

GEO-014 (weight 3): three direct subjects, two pass and one fail, plus one inferred. GEO-012 (weight 2): four direct passes. All sub-checks are available because a URL was supplied.

ReadinessCoverageConfidenceCap
8085%85%—
Readiness = 100 × (3 × 2/3 + 2) / 5 = 80; Coverage = Confidence = 100 × (3 × 3/4 + 2) / 5 = 85%

Comparing runs

  • A numeric readiness delta is shown only when the scoring version, ruleset major.minor and the set of direct (rule, subject) pairs are identical. Otherwise the comparison says “assessment scope changed” and lists what entered and left.
  • A finding that moves from fail to not_evaluated or error is not resolved.
  • Cross-site scores are not rankings. Even with an identical scope it is a technical readiness comparison, not AI performance.

Eleven properties are checked on 10,000 generated cases each: bounds, null-iff-no-direct-evidence, fail→pass monotonicity, unassessed evidence never improving coverage or readiness, inferred evidence never moving readiness, order and sample invariance, cap invariance, and the comparability rules above.