Skip to content

fix(docs): validate the singular framework field and collapse five framework maps into one - #430

Open
eugenia-scandit wants to merge 3 commits into
mainfrom
fix/framework-frontmatter-ssot
Open

fix(docs): validate the singular framework field and collapse five framework maps into one#430
eugenia-scandit wants to merge 3 commits into
mainfrom
fix/framework-frontmatter-ssot

Conversation

@eugenia-scandit

Copy link
Copy Markdown
Collaborator

Closes the ticket on the unvalidated framework field.

The problem

docs-schema.yml defined the plural frameworks array with an enum but did not
define the singular framework field at all. The schema allows undeclared
fields, so it got no validation and its values drifted. Components that read it
silently found no match and fell back to their default — always iOS. No crash,
no error.

What this changes

  • The enum in docs-schema.yml is the single source of truth; singular and
    plural share one vocabulary.
  • 117 pages normalized: 92 spelling drifts (netIos / netAndroid / react),
    and 25 express / bolt pages which were never frameworks — they live under
    docs/hosted/, so they are now framework: hosted with the value they carried
    moved to product:.
  • Five copies of the framework list replaced by one registry
    (src/constants/frameworks.ts), which UNRELEASED_FRAMEWORK_SLUGS also
    derives from.

Three things the ticket could not have known

  • kmp — 37 pages, more than any row in the ticket's table. Not drift: it is
    the published /sdks/kmp/ tree, it is first-class in the code, and
    "unreleased" is already tracked by a separate mechanism. The enum was stale, so
    kmp was added to it rather than the pages being rewritten.
  • FW_TO_PJ does not exist — PR feat: add ProductChooser ("Choose this if / not for" + framework matrix) #417 is unmerged. There were five copies,
    not three.
  • A live bug: FRAMEWORK_MAPPING had no linux entry, so
    parseSdksRoute() resolved no framework at all for every /sdks/linux/ page.

Why nobody ever saw an error

docs-gate is a ratchet over changed files. Those 37 pages had been violating
the plural enum for months and were never once reported. So
yarn verify:frameworks (in CI) runs over the whole corpus and over the
registry: a value outside the enum is an error, and the registry and schema are
compared in both directions.

Plus yarn test:frameworks — 9 assertions pinning the membership of every
derived map, because the risk in this refactor is not a crash but a map quietly
gaining or losing an entry.

Separately

docs-gate now skips prose checks for files whose body did not change. The
mechanical frontmatter pass across 117 files dragged in 233 pre-existing Vale
findings (measured: 233 before, 233 after). Any file with a body edit is still
checked in full — verified by adding one sentence and watching both new findings
appear while the other 116 stayed skipped.

The ticket's test: a page set to framework: netIos fails the gate before
normalization and passes after — verified in both directions.

eugenia-scandit and others added 3 commits August 19, 2026 12:12
…values

docs-schema.yml defined the plural `frameworks` array with an enum but
never defined the singular `framework` field at all. With
additionalProperties: true it got no validation, and its values drifted.

Canonical set: the enum is now the single source of truth for framework
identifiers, and singular and plural share it - a page states one
platform or several, never two different vocabularies.

Normalized 117 pages:
- netIos -> net-ios (31), netAndroid -> net-android (31),
  react -> react-native (30). Pure spelling drift; the canonical
  spellings already existed on other pages.
- express (13) and bolt (12) were never frameworks. Those pages live
  under docs/hosted/, so they are `framework: hosted` with the value
  they were carrying moved to `product: express` / `product: id-bolt`,
  both of which already exist in the product enum.

Two things the ticket's count could not have seen:

- `kmp` is a sixth non-canonical value, on 37 pages - more than any
  entry in the ticket's table. It is not drift: those pages are the
  published /sdks/kmp/ tree, kmp is first-class in FRAMEWORK_MAPPING,
  QUERY_FRAMEWORK_TO_PATH and SkillsCallout, and being unreleased is
  already tracked by UNRELEASED_FRAMEWORK_SLUGS. The enum was stale, so
  kmp is added to it rather than the pages being rewritten.
- FW_TO_PJ does not exist on main - PR #417 is unmerged. The real
  duplication is five maps, not three.

Why nobody ever saw an error: docs-gate is a ratchet over changed files,
so 37 pages violating the *plural* enum went unreported for months. Step
2 of the ticket alone would not have caught the other 117 either.
scripts/verify-frameworks.cjs (yarn verify:frameworks, wired into CI)
therefore checks the whole corpus, and the code maps as well:

  CONTENT   no page may declare a framework outside the enum
  DRIFT     no code map may key off a framework the enum does not define
  COVERAGE  enum slugs missing from a map are reported as known gaps

It found one live bug on its first run: FRAMEWORK_MAPPING had no `linux`
entry, so parseSdksRoute() resolved no framework at all for every
/sdks/linux/ page. Fixed here.

Ticket acceptance test: a page set to `framework: netIos` fails the gate
and passes after normalization - verified both ways.

Step 4 (consolidating the five maps onto one) is deliberately NOT in this
commit. Those maps have different membership, so merging them changes
runtime behaviour - the linux gap above is exactly that kind of change -
and it deserves its own reviewable diff. The DRIFT check above is what
stops them diverging further in the meantime.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Step 4 of the ticket. The framework set was written out five times -
FRAMEWORK_MAPPING, QUERY_FRAMEWORK_TO_PATH and HOMEPAGE_FRAMEWORK_ALIASES
in components/utils/frameworks.ts, plus FRAMEWORK_URL_PATH and
FRAMEWORK_SLUG in SkillsCallout - each keyed slightly differently and
nothing tying them together. That is why `linux` could be missing from one
of them, and only that one, for the entire life of the page.

src/constants/frameworks.ts is now the single registry. Each entry carries
slug, display name, route segment, whether an Agent Skills page exists,
whether it is unreleased, and any alternate spellings. All five maps are
derived from it, as is UNRELEASED_FRAMEWORK_SLUGS.

`slug` is the same string as the docs-schema.yml enum value, so the
frontmatter vocabulary and the code vocabulary are now one vocabulary.
yarn verify:frameworks compares the registry against the enum in both
directions and fails on any difference - a slug in the registry the schema
does not allow, or a slug the schema allows that no component can resolve.

The risk in this refactor is not a crash, it is a map silently gaining or
losing an entry, so scripts/test-frameworks.cjs (yarn test:frameworks,
wired into CI) pins the expected membership of every derived map against
what the hand-written maps held before. It loads the real modules through
the TypeScript API rather than re-deriving anything, so it fails if the
registry changes shape. 9 assertions, all passing, including the .NET
two-segment route, the homepage aliases, and the Agent Skills exclusions.

One deliberate membership change: `hosted` now counts as a framework
without Agent Skills. It is not a homepage selector slug, so nothing
passes it to frameworkHasAgentSkills - inert today, and correct if it ever
is passed.

Retargeting the gate also surfaced the same parser trap twice: anchoring
on the first `{` or `[` after a name reads the TYPE annotation, not the
value, and yields zero entries while reporting success. Both readers now
anchor on the assignment and fail loudly on zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The ratchet checks a whole file as soon as a PR touches one line of it.
That is right for prose someone is actually editing, and wrong for a
mechanical metadata pass: normalizing `framework:` across 117 pages
dragged in 233 pre-existing Vale findings the change neither caused nor
altered (measured identical before and after: 233 either way).

Nobody writes prose in frontmatter, so when a file's body is byte-identical
to the ratchet base there is no prose to review. Those files now skip Vale
and cspell. Schema and link checks still run on every changed file, and a
file with any body edit is checked in full - verified by adding one
sentence to one file and watching both new findings appear while the other
116 stayed skipped.

Line endings are normalized before comparing: `git show` returns the repo
blob with LF while the Windows working copy has CRLF, which made every
file compare as changed and the skip silently never fire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1

🚀 View preview at
https://Scandit.github.io/data-capture-documentation/pr-preview/pr-430/

Built to branch gh-pages at 2026-08-20 16:30 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant