Skip to content

Fix: the last entry in a mapping file swallowed the file's closing tables (22 junk mappings, 22 junk registry controls) - #90

Merged
emmanuelgjr merged 7 commits into
mainfrom
fix/generate-last-entry-bleed
Sep 15, 2026
Merged

emmanuelgjr merged 7 commits into
mainfrom
fix/generate-last-entry-bleed

Conversation

@emmanuelgjr

@emmanuelgjr emmanuelgjr commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Plan ID: — (data integrity; found while scoping #35) Ticket: — Wave: —
Constraints honored:

  • C4: only rows parsed from non-mapping tables are removed. No authored mapping, relationship or severity changes.
  • C2: docs/data.js, backlinks.js and frameworks-registry.js are regenerated, and one hard-coded number in the About paragraph is updated. Approval needed.
  • C1: the About paragraph edit is the mapping count only; attribution untouched.

#37 has merged; this PR now targets main directly.

The bug

extractSection() ended an entry's section only at the next ### heading. The last entry in each mapping file therefore ran on through the file's ## closing sections: the summary matrix, references, changelog. Two kinds of non-mapping data got stored:

1. 22 mapping rows on DSGAI21, all from dsgai-2026/DSGAI_AIUC1.md and dsgai-2026/DSGAI_NHI.md:

Framework control_id values stored as controls
AIUC-1 (9) Primary DSGAI entries, DSGAI02, DSGAI03, DSGAI06, …, DSGAI05, DSGAI20, DSGAI12, DSGAI19, DSGAI20, … Date, 2026-03-27
OWASP NHI Top 10 (13) DSGAI entries most affected, DSGAI21, DSGAI03, DSGAI06, DSGAI16, … Date, 2026-03-27

Plus the 352 backlink lines built from them.

2. Cross-references on the three last-in-file entries. LLM10 claimed LLM01–LLM09, ASI10 claimed ASI01–ASI09, and DSGAI21 claimed 18 extra DSGAI ids, all mentioned only in the closing summary. No other entry changes, which fits the cause.

The same 22 junk rows had been extracted into the registries as kind: control items: 9 in aiuc-1.json, 13 in owasp-nhi.json. They're removed here where no mapping references them. inventory_completeness.included follows, and matches what inventory-completeness.js computes. Each registry gets a changelog entry.

Impact

Measured against main after #37, #94 (AISVS 1.0, 26th framework) and #96:

main this PR
mappings 3,803 3,781 (-0.6%)
DSGAI-2026 mappings 1,788 1,766
controls (kind: control) 1,563 1,541 (-1.4%)
registry items 1,705 1,683

Both moves are under the playbook's 2% escalation threshold and fully explained above. Webapp: the About paragraph's hard-coded count becomes "26 industry frameworks with 3,781 individual control mappings" (was 3,803 on main) (C2). The first version of this PR said 3,497 → 3,475; #94 has since added a framework, so the merge commit recomputed it from the data.

Guard

New test in scripts/generate.test.mjs: no mapping or registry control_id may be shaped like a changelog or summary cell (Date, Version, an ISO date, a list of entry ids, "… entries most affected"). It reports 44 such rows/items against the previous data and 0 now.

Found, not fixed

Deliberately NOT changed

Verify

$ node scripts/validate.js     0 error(s), 84 warning(s), 312 passed  (before: same)
$ npm run stats:check          ✓ current
$ npm test                     52/52 ×2  (was 51; +1 guard)
$ node scripts/generate.js && git diff --exit-code (CI path list)  → clean after commit
diff of data/entries by (framework, control_id): 22 removed, 0 added, all on DSGAI21

🤖 Generated with Claude Code

https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR

emmanuelgjr and others added 2 commits August 30, 2026 10:10
Every docs/*.js bundle carried a `// Generated: <today>` header. Nothing
else in the generator output varies between runs, so that one line was
the whole reason a regenerate on a different day showed four dirty
files — and, since #30, the reason the Generator reproducibility job and
the `committed entries match a fresh generation` test could only pass on
the day the bundles were last committed.

- generate.js: drop the run-date lines; the Source header now reads the
  version from package.json instead of a hard-coded, stale `v1.5.2`.
- generate.test.mjs: assert no bundle header names a run or carries a date.
- validate.yml: the reproducibility job now diffs every generated
  artefact (backlinks.json, backlinks.js, frameworks-registry.js too).
- CONTRIBUTING.md: document the build contract — generated files, the
  determinism requirement, and why the bundles are committed (Pages
  serves docs/ from main; no deploy workflow exists).

Determinism only. No structural, route, layout or logo change (C2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0147wBugcuzLkswKPqgofcke
…bles

extractSection() ended an entry's section only at the next `### ` heading,
so the LAST entry in each file ran on through `## ` closing sections. Two
kinds of data that were never mappings got stored as if they were:

- 22 control mappings on DSGAI21, from the dsgai-2026 AIUC-1 and NHI
  files' summary matrices and changelog tables: control ids "Date",
  "2026-03-27", "DSGAI02, DSGAI09, DSGAI14", "DSGAI entries most affected"
  — and 352 lines of backlinks built from them.
- Cross-references: LLM10, ASI10 and DSGAI21 (each the last entry in its
  files) claimed every sibling id mentioned in the closing summary, e.g.
  LLM10 → LLM01..LLM09. Only those three entries change.

The 22 rows had also been extracted into the registries as 22 "controls"
(9 AIUC-1, 13 OWASP NHI). They are removed where no mapping references
them; inventory_completeness.included follows (and agrees with
inventory-completeness.js).

Counts: mappings 3,497 -> 3,475 (-0.6%); controls 1,372 -> 1,350 (-1.6%).
The webapp About paragraph's hard-coded "3,497" is updated to match (C2).

New test fails on any mapping or registry id shaped like a changelog or
summary cell; it reports 44 such rows/items against the previous data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR
#23 moved the registry url to the final publication's DOI but did not
re-run inventory-completeness.js, so inventory_completeness.source kept
the previous csrc URL. The script writes source = url; re-running it
changes exactly this line. The previous commit on this branch restored
the stale value on the mistaken belief the script was reverting #23 —
it was correcting it. Both URLs resolve to the final SP 800-218A.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR
emmanuelgjr and others added 2 commits September 14, 2026 15:44
Conflicts were only in the four generated webapp bundles, and only in
their header lines: #92 regenerated them with a `// Generated:` date
that this branch removes. Resolved by re-running scripts/generate.js on
the merged sources; a second run is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR
Generated files re-run on the merged sources; a second generation is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR
Base automatically changed from fix/T-ENG03-deterministic-webapp-build to main September 14, 2026 21:08
emmanuelgjr and others added 2 commits September 14, 2026 21:12
One hand-resolved conflict, docs/index.html: #94 changed the About
paragraph to "26 industry frameworks with 3,803 individual control
mappings" while this branch had changed the same sentence to 3,475.
Kept #94's sentence and set the count to the merged data's own total,
3,781 (= 3,803 minus the 22 junk rows this branch removes, all on
DSGAI21; 0 rows added). Generated files re-run on the merged sources;
a second generation is byte-identical. validate 0 errors; stats current.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR
Generated files re-run on the merged sources; a second generation is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SfR2YzLxRH54DAVzDk8gR
@emmanuelgjr
emmanuelgjr merged commit 7c81f8a into main Sep 15, 2026
6 checks passed
@emmanuelgjr
emmanuelgjr deleted the fix/generate-last-entry-bleed branch September 15, 2026 01:30
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