Automate the frontend third-party licence inventory - #390
Merged
Merged
Conversation
THIRD_PARTY_LICENSES.txt was the last hand-maintained link in the dependency chain. The Rust inventory has had generate-rust-licenses.mjs for some time; the frontend equivalent did not exist, so every frontend bump required a human to hand-edit version headers before check-dependency-inventory.mjs --frontend would pass. That tax is what issues #349, #350 and #351 describe, and it was paid by hand twice today. scripts/generate-frontend-licenses.mjs reads pnpm licenses list --prod --json, resolves each package's own LICENSE file from its install path, groups packages sharing byte-identical licence text, and writes the file in the existing format. It is wired as license:generate:frontend, mirroring the Rust wiring, plus a license:generate that runs both. Regenerating revealed two defects in the hand-maintained file: - lucide-react's header underline was 20 dashes under a 19-character header, left over from an earlier version bump. Precisely the class of drift this replaces. - The react/react-dom/scheduler group cited facebook/react. react and react-dom now both declare react/react in their published package.json; only scheduler still says facebook. The generator takes the first-alphabetical member's URL and warns at generation time when a group disagrees, rather than silently choosing. Determinism proven by running twice and comparing md5. A simulated lucide-react bump regenerated and passed license:all with no hand-editing. Corrupting the generated file still fails check-dependency-inventory with a precise drift message, so the checker is not made vacuous. package.json is pinned; the compatibility surface was resealed in the documented order and a confirming rehash reports zero changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This was referenced Sep 15, 2026
…nse-generator # Conflicts: # THIRD_PARTY_LICENSES.txt # docs/tally/compatibility/compatibility-matrix.json # docs/tally/compatibility/compatibility-surface.json
The previous commit on this branch pushed unresolved conflict markers in THIRD_PARTY_LICENSES.txt: the merge left that file conflicted alongside the two compatibility manifests, and only the manifests were resolved before staging. Regenerating with the generator this branch adds resolves it outright -- which is the point of the change. license:all now passes on 5 frontend and 384 Rust locked components, and two consecutive generator runs produce a byte-identical file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Removes the last hand-maintained link in the dependency chain.
The choke point
The Rust inventory has been automated for some time (
scripts/generate-rust-licenses.mjs→license:generate:rust). The frontend equivalent never existed, soTHIRD_PARTY_LICENSES.txtwas hand-maintained: every frontend dependency bump required a human to hand-edit version headers beforecheck-dependency-inventory.mjs --frontendwould pass.That is exactly the tax issues #349/#350/#351 describe, and it was paid by hand twice today — once in #386's consolidated bump, once before that.
What this adds
scripts/generate-frontend-licenses.mjsreadspnpm licenses list --prod --json(behind the samenpm_execpathguard the checker uses, so it must run through a pnpm script), resolves each package's own LICENSE file from its install path, groups packages sharing byte-identical licence text, and writes the file in the existing format — no redesign.Wired as
license:generate:frontend, mirroring the Rust wiring, plus alicense:generatethat runs both.Regenerating found two real defects in the hand-maintained file
lucide-react's header underline was 20 dashes under a 19-character header — stale from an earlier version bump. Precisely the class of drift this exists to eliminate.react/react-dom/schedulergroup citedfacebook/react.reactandreact-domboth now declarereact/reactin their publishedpackage.json; onlyschedulerstill saysfacebook. The generator takes the first-alphabetical member's URL and warns at generation time when a group disagrees, rather than silently picking one:react, react-dom, scheduler share license text but report different repository URLs (...); using react's: ...Those are the only two differences from the committed file. Both are the generator being right and the hand-maintained file being stale.
Verification
diffclean, md5 identical (603729bc…both runs). Also covered by an automated test.lucide-react^1.41.0 → ^1.46.0, ranpnpm install --lockfile-only,license:generate:frontend, thenlicense:all→ passed with zero hand-editing. Reverted afterwards.scheduler 0.27.0→9.9.9) still fails withfrontend third-party inventory drift (missing: scheduler 0.27.0; stale: scheduler 9.9.9).license:allpasses: 5 frontend and 383 Rust locked components.node --test scripts/*.test.mjs), including 8 new tests.package.jsonis pinned; surface resealed in the documented order, confirming pass reports 0.Fallbacks are loud, not silent
All 5 current production dependencies resolved cleanly from their own bundled LICENSE files. For future ones: an
A OR Bexpression with no per-licence file falls back to the package's generic LICENSE with aconsole.warn; a compoundANDexpression, or a package with no matching licence file at all, makes the generator throw with a specific diagnostic. It never emitsUNKNOWNfor a package that actually has a licence.Pre-existing gap flagged, not fixed
ci.yml'sbundlepath filter never includedgenerate-rust-licenses.mjs, and now also omitsgenerate-frontend-licenses.mjs— so a change to either generator does not trigger the bundle job. Noted indocs/proposed-license-automation.mdalong with proposed CI wiring for a Dependabot auto-regenerate step. Neither applied here;.github/was out of scope for the agent that wrote it.Refs #349, #350, #351
🤖 Generated with Claude Code