Rendered from specs/F31-schema-outline.md — edit it there, not here.
F31 — Schema Outline
Overview
A schema of any size is read by scrolling. VS Code's Outline view and breadcrumbs — the two affordances that make a large file navigable — show nothing useful for a JSON Schema, because the built-in JSON symbol provider reports the document's structure (properties, then a key, then type) rather than the schema's (a property, its type, whether it is required).
This spec contributes a document-symbol provider that reads a schema the way its author thinks about it: the root, its properties nested by containment, array element schemas, and the $defs/definitions block as a sibling section — each symbol carrying its effective type and required-ness as detail. That single provider lights up the Outline view, the breadcrumb bar, "Go to Symbol in Editor" (Ctrl+Shift+O), and the sticky-scroll header, at no additional UI cost.
User Stories
- As a schema reader, I want an outline of the contract's shape so I can jump to a property without scrolling past three levels of
$ref. - As a schema author, I want required properties visually distinguished in the outline so I can audit the required set at a glance.
- As a reviewer, I want breadcrumbs that say
address → streetrather thanproperties → address → properties → street.
Functional Requirements
- F31-FR-01 The outline builder MUST produce a tree of symbols from schema source text, with the document root as a single top-level symbol named by the schema's
titlewhen present, else the file's base name. - F31-FR-02 Each entry under
propertiesMUST become a symbol named by the property, nested under the symbol of the schema that declares it. Nested object schemas MUST nest correspondingly; array element schemas MUST contribute their element properties under the array's own symbol. - F31-FR-03 A symbol's detail MUST carry the property's effective type as rendered by the shared
describeTypehelper (F01's fallback renderer), and MUST mark required properties distinctly from optional ones. - F31-FR-04 A property whose schema is a
$refMUST show the reference target as its detail rather than an empty type, and MUST NOT be expanded in place — following it is F13's job, and expanding would make a recursive schema's outline infinite. - F31-FR-05
$defs/definitionsMUST appear as one top-level section whose children are the definitions, each expanded like any other object schema, so a definition-heavy schema is navigable without scrolling to find them. - F31-FR-06 Composition keywords (
allOf/anyOf/oneOf) MUST contribute their branches' properties at the position of the composing schema, so a property declared in a branch is reachable from the outline of the object it effectively belongs to. - F31-FR-07 Every symbol MUST carry a full source span and a selection span (the property key), so selecting it reveals the right range.
- F31-FR-08 Symbol kinds MUST be assigned from the schema's type so the outline's icons are meaningful (object, array, string, number, boolean, null), falling back to a neutral kind when no type is declared.
- F31-FR-09 The provider MUST be registered only for documents recognised as schemas (F01's detection), so data files keep the editor's own JSON outline.
Non-Functional Requirements
- F31-NFR-01 The outline builder MUST be a pure,
vscode-free module (symbol kinds expressed as a local enum the provider maps) with ≥ 80 % coverage (Article V). - F31-NFR-02 The builder MUST be total and bounded: unparsable text yields an empty outline, and recursion MUST be depth-capped so a self-referential or pathologically nested schema cannot hang the extension host (S03).
- F31-NFR-03 Building the outline for a schema MUST stay well inside VS Code's symbol-provider budget; it MUST NOT resolve refs, read other files, or touch the network.
Out of Scope
- Expanding
$reftargets inline (F31-FR-04), and cross-file outlines. - A bespoke tree view in the side bar. The document-symbol contribution reuses four existing surfaces; a custom view would duplicate them.
- Editing from the outline (drag-to-reorder, rename-in-place) — rename lives in F30.
Acceptance Criteria
- A schema with
properties.address.properties.streetoutlines asroot → address → street, not throughpropertiesnodes. - A required property's detail is visibly distinct from an optional one's.
- A property whose value is
{"$ref":"#/$defs/Address"}shows the ref as its detail and contributes no children. - A schema with
$defsshows a$defssection listing each definition. - A self-recursive schema outlines without hanging and within the depth cap.
Relation to Existing Specs
- F01 (preview) shares
describeTypefor the detail string and its schema detection for provider registration. - F13 (
$refnavigation) is how a$refsymbol is followed; F31 deliberately does not expand refs itself. - F24 (
$refgraph) gives the cross-definition view; F31 gives the in-document one.
History
- 2026-09-02 — Initial specification.
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 |
|---|---|---|---|
F31-FR-01 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-02 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-03 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-04 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-05 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-06 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-07 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-08 | implemented | src/schemaOutline.ts | buildOutline(): pure symbol tree from the linter AST; unit-tested for JSON and YAML |
F31-FR-09 | implemented | src/SchemaOutlineProvider.ts | registered for schema documents only; unit-tested |
F31-NFR-01 | implemented | src/schemaOutline.ts | pure; kinds are a local enum the provider maps |
F31-NFR-02 | implemented | src/schemaOutline.ts | depth-capped and total; fast-check covered |
F31-NFR-03 | implemented | src/schemaOutline.ts | no ref resolution, no I/O, no network |
Documented in
Documentation sections tagged spec:F31 (or one of its requirement ids) — 146 of ~397 expected words (37%). The expectation comes from this spec's evaluated complexity of 9.9 (12 documentable requirements — 9 FR, 3 NFR — weighted by kind and by each definition's length).
Changes over time
56cbbde2026-09-02feat(specs): schema tests, refactorings, outline, agent tools & AI authoring