Skip to content

Fix #112: recover 328 replacement characters in the registries, and guard the data files - #114

Merged
emmanuelgjr merged 2 commits into
mainfrom
fix/registry-encoding
Sep 18, 2026
Merged

emmanuelgjr merged 2 commits into
mainfrom
fix/registry-encoding

Conversation

@emmanuelgjr

Copy link
Copy Markdown
Contributor

Closes #112. Item 2 of the three follow-ups.

What was wrong

Fourteen files under data/frameworks/ carried 328 U+FFFD replacement characters, across 176 fields:

"description": "ML SBOM as software asset inventory <U+FFFD> all agent components (tools, MCP servers, models)"

These ship — the registries go out in the npm package, in docs/frameworks-registry.js, and in the OSCAL/STIX exports.

Recovered, not guessed

The registries were extracted from the mapping rows, so the same sentence usually survives intact in Markdown. For each corrupted string the text on either side is looked up in the mapping files and entry JSON, and the character that sits between them is read off the source. A character is accepted only when every match agrees.

Two refinements were needed, both visible in the run:

  • the window narrows from 24 → 14 → 8 characters, because a registry description concatenates several rows and a wide window can straddle the join;
  • when only one word follows the character, the raw slice is kept rather than trimmed to a word boundary — otherwise the window collapses and a recoverable character looks unrecoverable. That alone accounted for the last 4.

Result: 328 of 328 resolved, every one to U+2014 (—). Nothing was assumed, and no other character was found in any position.

Guard

The encoding guard from T-ACC06 only ever read Markdown — which is exactly why this sat unnoticed through every prior run. It now also reads data/frameworks/*.json, data/entries/*.json and data/*.json, checking for U+FFFD only: the arrow heuristic ("A ? B") is tuned for prose and diagrams and would misfire on JSON.

Negative-tested: a replacement character injected into soc2.json fails validation with the file and line number; removing it passes.

Verification

  • node scripts/validate.js: 0 errors, 87 warnings, 327 passed (one new check).
  • npm run test:scripts: 85 passed, 0 failed.
  • Zero U+FFFD and zero control bytes remain anywhere under data/.
  • docs/frameworks-registry.js regenerated; stats current.

🤖 Generated with Claude Code

…uard data files

Fourteen registry files carried 328 U+FFFD replacement characters, mostly
where an em dash belongs. They ship: the registries go out in the npm
package, in docs/frameworks-registry.js and in the OSCAL/STIX exports.

Every character is recovered from the source text rather than assumed. The
registries were extracted from the mapping rows, so for each corrupted string
the text either side of the character is looked up in the Markdown and the
entry JSON, and the character that sits between them is read off. A character
is taken only when every match agrees; the window narrows from 24 to 8
characters when a description concatenates several rows, and keeps the raw
slice when only one word follows, since trimming to a word boundary could
otherwise empty it.

All 328 resolved, every one to U+2014. None was guessed, and no other
character was found.

The guard from T-ACC06 only ever read Markdown, which is why this sat
unnoticed. It now also reads data/frameworks/*.json, data/entries/*.json and
data/*.json, checking for U+FFFD alone — the arrow heuristic is tuned for
prose and diagrams, not JSON. Negative-tested: a replacement character
injected into soc2.json fails validation, naming the file and line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@emmanuelgjr
emmanuelgjr merged commit f136bd3 into main Sep 18, 2026
6 checks passed
@emmanuelgjr
emmanuelgjr deleted the fix/registry-encoding branch September 18, 2026 15:26
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.

345 U+FFFD replacement characters in data/frameworks — the encoding guard covers Markdown only

1 participant