# Cartography — Feature Coverage + Screenshots reference (Mode 4, artifact 3 of 3)

The method + template for `evaluation/feature-coverage.md` (the **coverage gate**) and the screenshot
contract for `dimensions/session/captures/screens/`. Spine in [`cartography.md`](cartography.md);
siblings [`cartography-ia.md`](cartography-ia.md), [`cartography-flows.md`](cartography-flows.md).

This is the reconciliation the dimension collectors don't do: take **every** feature the product
_claims_ (website + docs + community/changelog), locate it in the actual product by **route + depth**
(from `information-architecture.md`), and record whether the run **walked** it. It turns "we clipped the
feature page" into "we know which features are real, where they live, and which we never actually saw."

---

## Assembling the claimed-feature catalog

The rows of the matrix are the **union** of every feature claim across:

- `dimensions/website/raw/features.md` (+ `editions-pricing.md` for edition-gated features),
- `dimensions/docs/raw/*` (a documented capability is a claim),
- `dimensions/community/raw/changelog-digest.md` (a shipped-changelog feature is a claim).

De-duplicate to one row per distinct capability. Each row is then **located** (against the IA surface
cards) and **walked** (did this run actually navigate/observe it).

## The disposition taxonomy (a `claimed-but-not-located` row is NEVER blank)

Every feature that is claimed but not located **must** be dispositioned into exactly one — each routes
differently:

| Disposition | Meaning | Routes to |
| --- | --- | --- |
| **deeper-than-looked** | it exists in this product; we just didn't navigate deep enough | the **re-walk queue** (Mode 5 walks it read-only) |
| **edition/plan-gated** | not in this tenant's edition/plan (e.g. a trial) | a recorded gap — **not** re-walked (re-walking can't find what isn't shipped here) |
| **roadmap-not-shipped** | marketed but not yet a shipped capability | a recorded gap |
| **marketing over-claim** | the claim overstates what the product does | an `evaluation/product-features.md` **over-claim flag** |
| **not-yet-reachable** | it exists and is shipped, but the surface **does not render at all** until a state change (deploy / connect / upgrade / first-run) that this run did not perform | the **gated re-walk queue** — NOT the read-only auto-loop |

### `not-yet-reachable` vs `deeper-than-looked` — the distinction that matters

These two look identical in a coverage matrix and route **oppositely**, so get it right:

- **`deeper-than-looked`** — the surface **is in the DOM right now**; the run just didn't navigate to it.
  More read-only walking finds it. Routes to the **read-only re-walk queue**, which Mode 5's auto-loop
  works for free.
- **`not-yet-reachable`** — the surface **is not in the product yet.** No amount of navigation reveals it,
  because the affordance is created by a state change. Routes to a **separate gated queue** that the
  auto-loop must **not** touch: closing it costs money, mutates state, or needs a third-party consent, so
  it goes through the explicit-confirmation gate (ingestion §7 rule 4) or is recorded as a standing gap.

**Mis-filing a `not-yet-reachable` row as `deeper-than-looked` is a Cartography defect**, because it sends
the auto-loop hunting for something that does not exist, and it reads as "we were sloppy" when the honest
statement is "the product's IA is state-dependent and this run stayed inside its authorization."

**A run that never performs the state change cannot map a state-dependent IA — say so rather than
scoring it as a coverage miss.** Note it beside the scorecard the same way `experiential_gated` annotates
a policy deferral: it documents, it does not inflate.

> (Origin: Emergent — a deployment executed mid-run **created** two whole surfaces: the entire *Manage
> Publishing* panel with five sub-tabs, and the `db_tool` database-console affordance, which opened a
> separately-hosted plugin on a second auth system. Neither exists in the toolbar of an undeployed project.
> The pre-deploy coverage doc had filed the publish family under *deeper-than-looked* and queued it for a
> read-only re-walk that could never have succeeded. Note also the **two-factor gate**: the cohort flag
> `db_tool: true` was necessary but **not sufficient** — an entitlement flag reading `true` does not mean
> the surface is reachable.)

**Where this bites generally:** any PaaS or marketplace with deploy/publish-gated management surfaces; any
product whose integrations render only after an OAuth connect; any tier-gated admin console; any
first-run/onboarding-gated dashboard. Treat an entitlement flag as **necessary, not sufficient** — check
whether the *state* it depends on has also been reached.

---

## The output template — `evaluation/feature-coverage.md`

```markdown
# <Target> — Feature Coverage
---
coverage_scorecard:
  features_claimed: <N>            # union of website + docs + changelog feature claims
  features_located: <N>            # found in the live product (route known)
  features_walked: <N>             # actually navigated / observed this run
  feature_location_rate: <0-100>   # located ÷ claimed
  flow_coverage: <0-100>           # primary journeys diagrammed ÷ journeys identified
  ia_surfaces_mapped: <N>          # surface cards written in information-architecture.md
  ia_nav_complete: <true|false>    # every top-level nav destination has a surface card
  screenshot_coverage: <0-100>     # primary surfaces with a screenshot ÷ all primary surfaces
  features_not_yet_reachable: <N>  # shipped, but behind a state change this run did not perform
---
## Coverage matrix
| Feature (claimed) | Source | Located? | Route · depth | Walked? | Edition-gated? | Confidence | Note |
|---|---|---|---|---|---|---|---|
| <feature> | website,docs | ✅ | #/…/x · D2 | ❌ not-walked | — | high | promoted on site, 2 levels deep, not exercised this run |
| <feature> | website(AI) | ⚠ edition? | — | ❌ | maybe-not-in-trial | low | marketed flagship, not located in this tenant |
## Located-but-not-walked   (the within-run re-walk queue — Mode 5 walks these read-only, no cost)
## Not-yet-reachable        (the GATED queue — shipped surfaces behind a deploy/connect/upgrade this run
                             did not perform. Mode 5 must NOT auto-walk these; each names the exact state
                             change that would unlock it and its cost/consent class)
## Claimed-but-not-located  (each row dispositioned per the taxonomy above — NEVER left blank)
## Buried flagships          (marketing-promoted features sitting ≥D3 — the positioning-vs-product gap,
                             drawn from information-architecture.md's promoted-vs-buried headline)
## Unmarketed depth          (powerful surfaces the marketing never mentions — the inverse finding)
```

## How the scorecard is scored (the Mode-5 handoff)

`cartography.md` describes _why_ the scorecard exists; here is _how each field is computed_, so Mode 5's
experiential / IA coverage axis (`self-correction.md` rubric, axis 2, max 20) is reproducible:

- `feature_location_rate` = `100 × features_located ÷ features_claimed`.
- **`not-yet-reachable` rows stay in the `features_claimed` denominator** — they are real, shipped,
  unlocated capabilities and hiding them would inflate the rate. But they are **dispositioned**, so they
  never draw the −2 un-dispositioned penalty, and the scorecard carries `features_not_yet_reachable` so a
  reader can tell a *policy/authorization* boundary from a *thoroughness* failure.
- `flow_coverage` = `100 × (primary journeys diagrammed in ux-flows.md ÷ journeys identified)` — **read both
  numbers from `ux-flows.md`'s `journeys_diagrammed` / `journeys_identified` frontmatter**
  (`cartography-flows.md`), never re-derive the denominator here. A reconstructed denominator lets the
  scorer pick the number that flatters the score, and makes the metric non-reproducible across runs.
- `screenshot_coverage` = `100 × (primary surfaces with a screenshot ÷ all primary surfaces)`.
- `ia_nav_complete` = every D0 nav destination has a surface card.

Mode 5 axis 2 = `8 × location_rate/100 + 6 × flow_coverage/100 + 4 × screenshot_coverage/100 + 2 ×
(ia_nav_complete ? 1 : cards÷destinations)`, **−2** per `claimed-but-not-located` row left
un-dispositioned. The `located-but-not-walked` list is the re-walk queue Mode 5 iterates.

---

## Screenshots — `dimensions/session/captures/screens/`

> **The scorecard metric is a FLOOR, not the capture rule.** `screenshot_coverage` asks a narrow question —
> did each *primary surface* get one image — because Mode 5 needs something countable. **The actual capture
> obligation is `ingestion.md` §5.5: screenshot generously, including states within a surface, for the human
> who reads this corpus later.** Satisfying this metric and stopping is the failure mode; the metric is
> nearly always already met by a real walk. Nothing here caps how much you capture, and extra images never
> hurt the score.

One annotated screenshot per **primary** surface (each D0 nav destination + each materially distinct
detail / editor surface). **Redact visible PII before saving** (user names, emails, customer data,
partner identifiers). Name `<nn>-<surface>.png`; write a one-line `screens/_index.md` mapping each file
→ surface → route → depth.

**Screenshots are the Mode-4 deliverable that the session playbook's textual surface-map only
_substitutes_ for** (ingestion §9.5) — capture them live here; fall back to the textual map (and record
the `screenshot_coverage` gap) only when the runtime genuinely cannot screenshot (no image tool, or the
app never reaches page-idle because of a persistent connection).

> **Re-test the capability before recording `screenshot_coverage: 0` — an environment limitation claimed
> by a PRIOR run is `verify:`, never inherited.** Take one screenshot with `save_to_disk: true` in the
> current session and record the literal tool result. Only an actual failure in *this* session justifies
> the textual fallback. (Origin: Emergent — a run recorded `screenshot_coverage: 0` because "`save_to_disk`
> returned no persistable path in this environment", and the harness codified that as an accepted
> fallback. In the same environment it works and returns a real path; the next run captured 13 images. An
> unverified environment claim had become a standing excuse that suppressed the most heavily-weighted
> rubric axis.)
>
> **Validate the coverage doc by READING THE FILE, not by trusting a delegated agent's return.** Because
> Mode 5 scores this file's `coverage_scorecard` frontmatter directly, a **stale** `feature-coverage.md`
> is worse than a missing one — it scores silently, against the *previous* run's numbers. Assert (a) the
> file's mtime is newer than the dispatch and (b) its frontmatter matches this run's facts
> (`write_side_observed`, `screenshot_coverage`, the credit figure). (Origin: Emergent, second run — a
> sub-agent delivered its IA doc and silently dropped this one; the untouched Pass-1 file would have
> scored a full write-side run at `screenshot_coverage: 0` / `write_side_observed: false`.)

The screenshots live under the
`session` dimension (it owns the browser captures); `information-architecture.md`'s surface cards
reference them by path.
