docs: attribute the adapted opencode sources and the models.dev snapshot - #3325
Draft
Astro-Han wants to merge 3 commits into
Draft
docs: attribute the adapted opencode sources and the models.dev snapshot#3325Astro-Han wants to merge 3 commits into
Astro-Han wants to merge 3 commits into
Conversation
Astro-Han
force-pushed
the
chore/models-dev-refresh-catalog-snapshot
branch
3 times, most recently
from
August 20, 2026 14:44
31a9326 to
8aa2935
Compare
Astro-Han
force-pushed
the
docs/3270-third-party-attribution
branch
2 times, most recently
from
August 20, 2026 17:43
3eab973 to
e4bdb38
Compare
Astro-Han
force-pushed
the
chore/models-dev-refresh-catalog-snapshot
branch
from
August 20, 2026 17:48
7e64456 to
8dca808
Compare
Astro-Han
force-pushed
the
docs/3270-third-party-attribution
branch
from
August 20, 2026 17:49
e4bdb38 to
78dbe2c
Compare
Base automatically changed from
chore/models-dev-refresh-catalog-snapshot
to
main
August 20, 2026 18:34
Astro-Han
force-pushed
the
docs/3270-third-party-attribution
branch
from
August 20, 2026 18:35
78dbe2c to
c34f687
Compare
…tice `@ai-sdk/provider-utils` ships no license file, so the generator substitutes the root LICENSE as its Apache-2.0 text. It substituted the whole file. LICENSE also carries Maka's THIRD-PARTY COMPONENTS section, so 140 lines describing trycua/cua and the bundled fonts were filed under one npm package in both release notices — attributing material to a party that has nothing to do with it, in the artifact whose only job is to attribute correctly. Truncate at the section header. `split` returns the whole string when the delimiter is absent, which would silently reinstate this exact bug and leave `--check` demanding that the wrong output be committed, so the delimiter is required rather than assumed. The regression test comes from PR #2907, which found this defect and carries the same fix. It deliberately matches the package without its version, so a routine dependency bump does not fail it. Both are repeated here because this branch adds LICENSE entries, and without the fix those entries would ship inside the provider-utils notice. Whichever lands second drops the duplicate. The test is wired into `check:release` and into the CI planner's CLI-package inputs, so it runs on pull requests that touch it rather than only at release preparation. Generated-by: Claude Code
The code origin audit (#2907) found two pieces of third-party material that ship in every release and appear in none of Maka's attribution surfaces, both for the same structural reason: neither is an npm package, and the notice generator walks npm production dependency trees only. `tool-output.ts` and `edit-replace.ts` already said in prose that they adapt opencode, and the adaptation is real — the escape-normalizing replacer's regular expression and its nine branches are carried over unchanged. Prose is not the notice MIT requires. Each file now records source, revision, license, and copyright, and LICENSE carries the permission notice so it travels with the release. Both files are mixed-origin, so the notice is scoped to the adapted material and neither gets a whole-file SPDX identifier. The LICENSE entry records the whole upstream chain rather than stopping at the nearest one. opencode's edit.ts credits cline diff-apply and the gemini-cli editCorrector; that file is Copyright 2025 Google LLC under Apache-2.0, and its escape set shares its first eight branches, in order, with the replacer here. opencode added the ninth and changed the pattern. Whether that shared sequence carries protected expression is deliberately not adjudicated in LICENSE — it is recorded so a licensing review can see the chain and decide. The models.dev entry states what is copied instead of characterising it. The earlier draft said the individual records were uncopyrightable facts and that what came from upstream was the selection and arrangement. Both halves were wrong: `sync-model-metadata.mjs` copies `model.name` and `model.description` verbatim, and the snapshot carries 1779 upstream-authored descriptions; while the selection and the ordering are the generator's, not upstream's. The entry now lists the fields actually carried and leaves the legal conclusion out. The MIT text was already reproduced in full, so nothing about the obligation changes — only the accuracy of the sentence next to it. Upstream copyright lines were checked against the upstream LICENSE files rather than transcribed: both are `Copyright (c) 2025 opencode` and `Copyright (c) 2025 models.dev` verbatim. The generated headers name their payload. models.dev serves a rolling document with no version, so the generator stamps the sha256 of the data it extracted — not of api.json, which would move whenever any of the ~40 dropped providers changed and produce header-only diffs that say nothing about the file they sit in. Hashing after extraction makes the digest describe what ships and keeps the generator a pure function of its input: the same api.json produces the same bytes on any day. Verified by running it twice against the pinned payload. Closes the first exit criterion of #3270. Generated-by: Claude Code
…ifact notice The generator's inventory is the npm production dependency closure. That is a complete answer to "which npm packages ship" and a structurally blind one to "what else ships": vendored, adapted, and generated-from-upstream material is not a package, so no amount of checking the walk's output can reach it. The previous commit's LICENSE entries were guarded by nothing — an attribution header could be dropped in a routine edit and both notices would still regenerate clean. The obvious move is to declare that material in the generator. The first draft did, and it was wrong: it restated the repository, revision, copyright, and license text that LICENSE already holds, creating a second authority that could disagree with the first while every check passed. Passing a deliberately wrong repository and revision to that validator was accepted without complaint, and the notice printed them. LICENSE is the authority. ASF release policy puts the notice there, it ships with every artifact, and a human reviews it — and its THIRD-PARTY COMPONENTS section is already structured. So the generator parses it and reproduces the entries verbatim into both notices, and this module keeps only the one fact LICENSE cannot express: which file carries each entry and what text must still be in it. That inverts what the check is worth: - All four entries reach the notices, not the two a hand-written list happened to name. Geist is OFL-1.1 and was structurally undeclarable before, because the draft could only emit MIT. - License text is LICENSE's own, not rebuilt from a template around a hand-transcribed copyright line. - The check runs both ways. A LICENSE entry with no carrier decision fails, so #2907's Vercel AI SDK and Astryx entries cannot land unanchored; a carrier decision with no LICENSE entry fails too. - `trycua/cua` is now anchored. `palette.ts` calls itself a "faithful 1:1 port" — a stronger claim than the opencode adaptation — and nothing had been holding it. The notices' own prose no longer implies more than it delivers: it says the entries are LICENSE's section reproduced and checked against their carriers, not that everything vendored has been found. Discovering undeclared material is a scan whose allowlist is #2907's audit table, and it belongs there. Validation takes its inputs as arguments, so every failure path is exercised against fixtures rather than by editing tracked source. `non-npm-sources.mjs` and both test files are wired into the CI planner's CLI-package inputs, the Windows release path filter, and `check:release`, so changing the guard runs the guard. Generated-by: Claude Code
Astro-Han
force-pushed
the
docs/3270-third-party-attribution
branch
from
August 20, 2026 20:32
c34f687 to
8311320
Compare
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.
Summary
The code origin audit (#2907) found two pieces of third-party material that ship in every release and appear in none of Maka's attribution surfaces — the adapted opencode sources and the generated models.dev snapshot. Both are missing for the same structural reason: neither is an npm package, and
scripts/generate-third-party-notices.mjswalks npm production dependency trees only.Three commits, each independently green:
1.
fix(scripts)— keep Maka's own third-party section out of a package notice.@ai-sdk/provider-utilsships no license file, so the generator substituted the root LICENSE as its Apache-2.0 text — the whole file, so 140 lines describing trycua/cua and the bundled fonts were filed under one npm package in both notices. This is a prerequisite, not a drive-by: the defect scales with LICENSE, so commit 2's entries would otherwise ship inside the provider-utils notice. The fix is fail-closed —splitreturns the whole string when the delimiter is absent, which would silently reinstate the bug. The regression test comes from #2907, which carries the same fix; whichever lands second drops the duplicate.2.
docs— attribute the two sources. Both runtime files record source, revision, license, and copyright, scoped to the adapted material, with no whole-file SPDX identifier because both files are mixed-origin.3.
build(scripts)— render the LICENSE third-party section into every artifact notice. The generator now parses LICENSE'sTHIRD-PARTY COMPONENTSsection and reproduces the entries verbatim into both notices;scripts/non-npm-sources.mjskeeps only the one fact LICENSE cannot express — which file carries each entry and what text must still be in it.What changed after adversarial review
Two independent reviewers (Codex and a Claude subagent, isolated worktrees) returned findings I verified myself. Three changed the design:
The models.dev rights claim was wrong in both halves, and is gone. The earlier draft asserted the individual records were uncopyrightable facts and that what came from upstream was "selection and arrangement". But
sync-model-metadata.mjs:266copiesmodel.nameandmodel.descriptionverbatim — the snapshot carries 1779 upstream-authored descriptions like'Reasoning-first Gemini preview for agentic coding and complex problem solving'— while the selection and ordering are the generator's own (localeCompare, and a hand-maintained provider list). The entry now states which fields are carried and leaves the legal conclusion out. MIT was already reproduced in full, so the obligation never depended on that sentence; only its accuracy did.The attribution chain stopped one hop early. opencode's
edit.tscredits cline diff-apply and the gemini-cli editCorrector. I checked gemini-cli'seditCorrector.ts: Copyright 2025 Google LLC, Apache-2.0, and its escape set shares its first eight branches in order with the replacer here (n, t, r, ', ", \, \, \n`); opencode added a ninth and changed the pattern. LICENSE now records the whole chain and explicitly does not adjudicate whether that shared sequence carries protected expression — that is a licensing call, not mine.The declared-inventory design created a second authority. The first draft restated repository, revision, copyright, and license text that LICENSE already holds. Codex demonstrated the consequence: passing a deliberately wrong repository and revision to the validator was accepted, and the notice printed them. LICENSE is the authority — ASF policy puts the notice there, it ships with every artifact, a human reviews it, and the section is already structured. Parsing it instead of restating it inverts what the check is worth:
trycua/cuapalette.tscalls itself a "faithful 1:1 port"The both-ways check matters for what lands next: #2907's Vercel AI SDK and Astryx entries cannot land unanchored, because a LICENSE entry with no carrier decision fails generation.
Also fixed from review: the digest now hashes the extracted payload rather than api.json (hashing api.json moved it whenever any of the ~40 dropped providers changed, producing header-only diffs), and the wall-clock date is gone, so the generator is a pure function of its input — same api.json, same bytes, any day.
non-npm-sources.mjsand both test files are wired into the CI planner's CLI-package inputs, the Windows release path filter, andcheck:release.Upstream copyright lines were checked against the upstream LICENSE files rather than transcribed: both are
Copyright (c) 2025 opencodeandCopyright (c) 2025 models.devverbatim.Refs #3270 — closes its first exit criterion. Refs #2907.
Review focus
Known limits
adapted from|ported from|vendored|copied fromcurrently hits 68 lines across 20 files.Verification
Both notice targets regenerated and both check scripts verified — running only the desktop one passes locally while the CLI one drifts.
Both-ways validation falsified end to end against the real tree, each reverted immediately after:
tool-output.tsno longer contains its attribution marker "fc80874f…"LICENSE declares "example/new" but scripts/non-npm-sources.mjs does not say which file carries itdeclares carriers for entries LICENSE does not have: Geist and Geist Mono (bundled webfonts)THIRD-PARTY COMPONENTSheaderLICENSE has no THIRD-PARTY COMPONENTS section header to truncate atGenerator determinism checked, not asserted: two runs against the pinned
api.jsonproduce byte-identical output. The two generated files differ frommainby +6 lines, −0 — header only, bodies byte-identical — so the header is a pure addition carrying no data change.Not run: the full repository suite. Nothing here changes runtime behavior — the two runtime files gain comments only, and the generated modules gain six header comment lines.
AI use
Tool(s) and scope: Claude Code drafted the attribution text, LICENSE entries, the LICENSE parser and carrier map, and the tests, ran every check reported above, and coordinated two adversarial reviews (Codex, Claude subagent) whose findings drove the redesign described above. The upstream comparison against opencode
fc80874and gemini-cli, the license determinations, and the decision to leave copyrightability unadjudicated were reviewed by the contributor of record.Checklist
Does this PR entail a change in behavior?