Repository navigation
fix(packaging): trim the package to 24.99 MB and give size budgets headroom - #3568
Merged
Merged
Conversation
Trim @adcp/sdk from 26,862,556 to 24,993,401 packed bytes (8,529 to 8,176 files) without changing runtime behavior: - Exclude compliance/cache/*/domains/ from the package. Every bundle's domains/ is a byte-identical copy of protocols/, the only directory the storyboard loader reads (-286,556 bytes, -353 files). - Emit one-line ESM facades for the type-only generated declarations (types/core.generated, types/tools.generated, registry/types.generated), as types/schemas.generated already did, instead of copying 5.7 MB of declarations (-1,174,602 bytes). - Store the self-contained MCP tools/list request projections as compact JSON. They are only ever JSON.parse'd (-407,997 bytes). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The packed-byte and file-count budgets were pinned to the measurement, so two PRs that each added a file or a few KB passed alone and broke main together (#3510 + #3541, #3564 + #3559/#3560). Replace the pins with: - Ceilings with headroom in scripts/package-size-budget.json: @adcp/sdk 26,000,000 packed bytes / 135,000,000 unpacked / 8,450 files against a measured 24,993,401 / 129,047,083 / 8,176 (about 4%), and @adcp/reporting-storage 490,000 / 2,250,000 / 150. A warning fires when less than two PRs' allowance of headroom is left. - A per-PR growth check. main caches each commit's measurement; a pull request compares itself with the record for its exact merge base and may add 100 KB / 25 files to the SDK (15 KB / 8 files to the storage package). More needs the same PR to raise the ceiling, so the growth is reviewed in the diff. A missing record skips only this comparison. - The focused-client eager graph count becomes a ceiling (230, measured 216) instead of an exact pin; the named heavy-module exclusions stay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — package-size tooling PR with no blocking findings.
Checked:
- scripts/check-package-size.mjs rewrite: fail-closed on size ceilings (L913), fail-open on stale baselines (L924) — correct posture.
- Compaction + domains/ removal are wire-neutral and guarded by new structural invariants in checkSdkContents (L792, L802).
- Storage content-check coverage preserved via the workspace check.
- CI records on push and compares PRs by commit-matched cache.
- Changeset present (patch) and appropriate for build-tooling scope; no wire-shape or export impact.
- Well-tested (new tests for size check, dmts declarations, client-core public export).
Decision table: no critical/high/medium findings; high_risk false; gated_paths false; no no-auto-approve team match. Rows 1–8 do not fire → row 9 approve. (review_decision is REVIEW_REQUIRED, but gated_paths is false, so row 2 does not apply.)
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.
Why
scripts/check-package-size.mjspinned the packed-byte cap and the packed file count to almost exactly the current measurement. Two PRs that each added a file or a few KB passed alone and brokemaintogether: #3510 + #3541 went over the byte cap, and #3564 + #3559/#3560 went one file over. Each fix bumped the cap (#3548, #3565). The cap crept from about 26,000,000 to 26,910,000 bytes, and the storage package budget and the focused-client eager-graph count used the same exact-pin pattern.Budget design
All numbers live in
scripts/package-size-budget.json. The policy is in the header ofscripts/check-package-size.mjs.Ceilings with headroom. Each package has packed-byte, unpacked-byte and file-count ceilings set at the measured size plus deliberate headroom:
@adcp/sdk@adcp/sdk@adcp/sdk@adcp/reporting-storage@adcp/reporting-storage@adcp/reporting-storageWhen less than two PRs' allowance of headroom is left, the check prints a warning, so someone raises the ceiling or trims the package before parallel PRs can collide on it. The storage package gets proportionally more room because a single module is a large share of it.
Per-PR growth against the merge base. On
main,library-checkssaves each commit's measurement to the Actions cache (package-size-<os>-<sha>). On a pull request, it restores the record for the merge commit's first parent, which is the exact merge base, and checks this PR's delta. Allowances:@adcp/sdk+100,000 packed bytes / +25 files,@adcp/reporting-storage+15,000 / +8. Recent feature PRs added 30 to 60 KB and 4 to 20 files, and the 3.3.0-beta.3 adoption added about 230 KB and 21 files. Growth over the allowance fails unless the same PR raises the matching ceiling, so the reviewer sees and approves the decision in the diff. Two PRs that both raise a ceiling conflict on the same line and get merged one after the other. The delta table goes in the job summary. If there is no record for the merge base (main's run hasn't finished yet, or the cache was evicted), only this comparison is skipped and the step prints a notice; the ceilings still apply. I chose this over a ratchet that CI commits back tomain, because that would need write permissions and bot commits.Structural checks without headroom (unchanged or new): no source maps, no skill snapshots, no expanded bundled schemas, required schema/compliance roots, no compliance
domains/alias,protocols/present in every compliance root, and exact one-line ESM facades for the four large generated declarations. The bundled-schema archive limit goes from 2,048,000 to 2,200,000 bytes (measured 2,001,269). That leaves room to swap a protocol version and still catches the old expanded bundles, which were tens of MB.The focused-client eager graph count in
test/lib/client-core-public-export.test.jsgoes from an exact<= 216to a ceiling of230. The named heavy-module exclusions remain the real guard: an accidental barrel import adds hundreds of modules (index.mjsreaches 605).The storage package's budgets moved into the shared budget file, and
check:package-sizenow checks both packages in one process.Trim: 26,862,556 → 24,993,401 packed bytes (-1,869,155, -7.0%); 8,529 → 8,176 files
I diffed the 17.0.0-beta.11 tarball against 17.0.0-beta.4 (25,749,296 bytes) and 16.6.1 (20,788,430). The growth since beta.4 is real content: the 3.3 schema and compliance bundles (+1.0 MB of schemas, +0.7 MB of test vectors, unpacked), reporting subscriptions, and per-tool types. 16.6.1 → 17 added a fourth protocol version. Nothing stale was riding along. All the waste was duplication:
compliance/cache/*/domains/**(package.jsonfiles)domains/is byte-identical toprotocols/(diff -rq). The storyboard loader (listBundles,resolveStoryboardsForCapabilities) reads onlyprotocols/<id>and never usesindex.domains. Nothing insrc/orbin/readsdomains/. Loading every bundle from the extracted tarball gives the same bundle and storyboard counts as the repo for all four versions (63/125/213/237 storyboards).types/core.generated.d.mts,types/tools.generated.d.mts,registry/types.generated.d.mtsexport * from './<name>.js', the patternschemas.generated.d.mtsalready shipped. The generator refuses to emit a facade if one of those files gains a value declaration, a default export, or a package import. attw, publint,check:adopter-types(+narrow),typecheck:examplesand the packed-artifact checks pass.tools/listrequest projections (scripts/copy-schemas-to-dist.ts)JSON.parses them andtools/listserves the parsed object, so the wire output is unchanged.Things I checked and kept:
schemas-data: the runtime validator uses them.types/*.d.tsslices: they are the./types/*entry points.verify-package.mjsassertsMIGRATION-v8.mdships. They are about 130 KB compressed.Verification
npm run check:package-size: ceilings pass. With a baseline of the beta.11 measurement, the delta table shows -1,869,162 bytes / -353 files. A synthetic +150 KB baseline fails with the remediation message, and a mismatchedPACKAGE_SIZE_BASE_SHAskips with a notice.npm run check:package, which runs runtime-compat, the packed-artifact checks, publish-protocol-artifacts,typecheck:examples, publint --strict, and attw for both packages.npm run check:adopter-typesandcheck:adopter-types-narrow.npm run verify:package: a clean-room install with peer floors, Node + Bun, MCP negotiation, the MCP schema smoke, and a browser bundle.createAdcpServer, conformance schema loader, the d.mts generator (new facade and type-only guard cases), the budget logic (new), client-core, the CI artifact contract, and publish-protocol artifacts.npm run lint:workflows.A changeset (
@adcp/sdkpatch) records the user-visible file changes. Thedomains/→protocols/note is the only one an adopter could notice.🤖 Generated with Claude Code