Rendered from specs/F23-schema-coverage.md — edit it there, not here.
F23 — Schema Coverage (unused-in-data lens)
Overview
Validation (F03) answers "is this data valid against the schema?". This spec answers the mirror question: "which parts of the schema does my data actually exercise?". Given a data file and its bound schema, it computes which schema-declared properties never appear in the data and highlights them — a coverage lens over a schema, the natural sibling of F20's workspace validation. It surfaces dead or aspirational schema surface (properties nothing populates), optional fields a fixture forgot, and drift between a schema and the shape of real documents.
User Stories
- As a config author, I want to see which schema options my example config never sets, so I can trim dead schema surface or add missing examples.
- As a schema author, I want a coverage number for a representative data file, so I can tell how much of the contract my fixtures actually touch.
- As a reviewer, I want the unused properties highlighted on the schema itself, so I can see the gaps where I read the schema.
Functional Requirements
Command & Resolution
- F23-FR-01 A command
jsonschema.schemaCoverageMUST be available when the active editor is a supported data file (JSON/JSONC/JSONL/YAML/TOML — F03/F11). - F23-FR-02 The command MUST resolve the data file's schema from, in order, its inline
$schema(F10/F11) then a settings binding (F04); when none resolves it MUST show an actionable notice and stop. A remote schema that is not cached locally (F08) MUST offer to cache it rather than fetching inline. - F23-FR-03 For a JSONL data file every record MUST be treated as a separate instance and coverage MUST be the union across records (a property is exercised if it appears in any record).
Coverage Computation
- F23-FR-04 The analyser MUST collect the set of properties the schema declares — every named entry under
properties, recursing through nested object schemas, arrayitems, theallOf/anyOf/oneOfbranches, and same-document$reftargets ($defs/definitions), guarding against reference cycles. Each declared property is keyed by its data-space path (dotted, with[]for array descent, e.g.address.street,items[].tag). - F23-FR-05 The analyser MUST collect the set of property paths present in the data (array indices collapsed to
[]), and MUST classify each declared property as exercised when its path is present in any instance, or unexercised otherwise. - F23-FR-06 The result MUST report a total, an exercised/unexercised split, and a coverage percentage (100 % when the schema declares no properties). The analyser MUST be pure (no
vscodeimport) and MUST NOT throw on malformed schema or data — an unparsable input yields an empty, safe result the command reports as a notice.
Highlighting & Report
- F23-FR-07 When the resolved schema is a local file, each unexercised property MUST be highlighted on the schema document as an
Information-severity diagnostic on the property definition's source span (located via F13's pointer locator), taggedUnnecessaryso editors can dim it. Re-running MUST replace the previous run's coverage diagnostics (no stale accumulation). - F23-FR-08 The command MUST finish with a summary notification (exercised / total, percentage) offering a "Copy report" action that produces a Markdown report listing the unexercised property paths, suitable for pasting into a PR.
Reconciliation (undeclared-in-schema)
- F23-FR-09 The analyser MUST also compute the inverse direction: the property paths present in the data that the schema does not declare. Coverage alone answers "what does my data not use?"; reconciliation answers "what does my data use that my schema does not know about?" — the direction that finds drift after a producer starts emitting a new field. A path whose parent is not a declared object schema MUST NOT be reported on its own (the parent is the real gap), so one new nested object yields one finding rather than one per leaf.
- F23-FR-10 Each undeclared path MUST be reported with a type inferred from the observed values at that path (the F06 inference engine over the collected values), so the report names what the declaration would be. The command MUST report both directions in one summary and one copyable Markdown report.
Non-Functional Requirements
- F23-NFR-01 The analyser (
collectSchemaProperties,collectDataPaths,computeCoverage,renderCoverageReport) MUST be pure, unit-tested modules with ≥ 80 % coverage (Article V); only the thin command/diagnostic wiring may be VS Code-bound. Property-based tests withfast-check(never throws on arbitrary JSON pairs) are RECOMMENDED. - F23-NFR-02 Coverage is a heuristic presence analysis, not a validation: it does not resolve remote
$ref, evaluate conditional applicability (if/then,dependentSchemas), or reason aboutpatternProperties/additionalPropertiesbeyond named properties. The report MUST state this so an unexercised entry is read as "worth a look", not "provably dead".
Out of Scope
- Whole-workspace coverage aggregation across many data files (F20 owns discovery; this command measures the active file, or the active file's records for JSONL).
- Value/enum coverage (which enum members are used) — that is F25's territory.
- Coverage of
requiredvs optional distinctions, or branch (oneOf) selection coverage.
Acceptance Criteria
- A data file
{ "a": 1 }bound to a schema declaring propertiesaandbreports 50 % coverage and one unexercised propertyb. - Running the command on that data file places one
Informationdiagnostic, taggedUnnecessary, onb's definition line in the schema file; re-running after addingbto the data clears it. - A JSONL file whose records collectively populate every declared property reports 100 % and opens no diagnostics.
- A schema with a nested object and an array of objects reports unexercised nested paths using
parent.child/items[].fieldnotation. - The copied Markdown report lists each unexercised path and states the heuristic caveat.
Relation to Existing Specs
- Sibling of F20 (workspace validation) — same "measure my repo" family, but the inverse question (unused schema surface, not invalid data).
- Reuses F13's pointer locator (
locatePointerTarget) to place highlights, F10/F11/F04 for binding resolution, and F08 for cached remote schemas. - Honours S02 (no remote fetch in untrusted workspaces — cached/local only) and S05 (no telemetry; data never leaves the machine).
History
- 2026-09-02 — Added F23-FR-09/10: reconciliation, the inverse direction. Coverage answers "what does my data not use?"; reconciliation answers "what does my data use that my schema does not declare?" — the direction that finds drift after a producer starts emitting a new field. Only the shallowest gap in a chain is reported, so one new nested object yields one finding rather than one per leaf, and each is reported with a type inferred from the observed values.
Requirement traceability
Status of each requirement in specs/traceability.json. Test coverage is auto-discovered from [ID] tags in test titles and is not listed here.
| Requirement | Status | Implementation | Note |
|---|---|---|---|
F23-FR-01 | implemented | src/SchemaCoverageCommand.ts, src/extension.ts | command registered; palette/context menu gated to data files |
F23-FR-02 | implemented | src/SchemaCoverageCommand.ts | resolves inline $schema then settings binding; offers to cache uncached remote |
F23-FR-03 | implemented | src/SchemaCoverageCommand.ts, src/schemaCoverage.ts | JSONL parsed to one instance per record; coverage is the union |
F23-FR-04 | implemented | src/schemaCoverage.ts | collectSchemaProperties: properties/items/allOf/anyOf/oneOf/$ref with cycle guard |
F23-FR-05 | implemented | src/schemaCoverage.ts | collectDataPaths + computeCoverage classify exercised/unexercised |
F23-FR-06 | implemented | src/schemaCoverage.ts | CoverageResult total/split/percent; pure and non-throwing |
F23-FR-07 | implemented | src/SchemaCoverageCommand.ts | Information/Unnecessary diagnostics on schema spans; cleared each run |
F23-FR-08 | implemented | src/SchemaCoverageCommand.ts, src/schemaCoverage.ts | summary notification + Copy report -> renderCoverageReport markdown |
F23-FR-09 | implemented | src/schemaCoverage.ts, src/SchemaCoverageCommand.ts | reconcile(): the undeclared-in-schema direction with inferred types; unit-tested |
F23-FR-10 | implemented | src/schemaCoverage.ts, src/SchemaCoverageCommand.ts | reconcile(): the undeclared-in-schema direction with inferred types; unit-tested |
F23-NFR-01 | implemented | src/schemaCoverage.ts | pure analyser modules, unit-tested incl. property-based |
F23-NFR-02 | implemented | src/schemaCoverage.ts | renderCoverageReport states the heuristic-presence caveat |
Documented in
Documentation sections tagged spec:F23 (or one of its requirement ids) — 215 of ~579 expected words (37%). The expectation comes from this spec's evaluated complexity of 14.5 (12 documentable requirements — 10 FR, 2 NFR — weighted by kind and by each definition's length).