Skip to content

fix: retain third-party license notices in distributions - #4070

Merged
thymikee merged 26 commits into
mainfrom
fix/third-party-license-notices
Nov 4, 2026
Merged

thymikee merged 26 commits into
mainfrom
fix/third-party-license-notices

Conversation

@thymikee

@thymikee thymikee commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Summary

Keeps bundled dependency license and NOTICE text in the npm package, including jpeg-js's source-file notices. Identical texts are written once. A bundled package missing its license, its license text or its version stops the build. The notices are written next to the bundle output.

  • The build lists bundled modules itself. scripts/lib/bundled-license-notices.ts goes through every module rendered into each output chunk and maps each third-party module to the nearest named package.json. This replaces rollup-plugin-license, which silently skipped any package whose manifest had neither a version nor a license. A module under node_modules with no package.json also stops the build. Root and private workspace packages count as this project's own code. The real artifact is byte-identical to the plugin's: 48,996 bytes, 27 attributions over 23 sections.
  • One module owns the notices format. scripts/size-report-package-notices.mjs imports nothing and holds both the renderer and the reader. The pack gate (scripts/size-report-package.mjs) calls the reader and keeps the package-path policy and the rule that the published CLI must attribute somebody. The Bundle Size lane's base step copies scripts/size-report-package*.mjs, so a measured revision's format goes with its own policy. A test enforces that copy contract. Multiline license or author text is written on a single attribution line.
  • Pack gate. It reads the actual notice bytes from both the packed and the installed trees. Every name@version (license): [n] line must point at a numbered section that has text. Only the header block before the first [n] heading is read for attributions. A grouped SPDX license expression is accepted, and an ; Author: clause cannot stand in for a reference. The empty-bundle output and manifest-only callers are unchanged.
  • Script output. All runEntrypoint output now goes through settleOutput. When stdout fails early, the diagnostic gets the same deadline as final output. If that diagnostic is still unflushed at its deadline while main is pending, the script exits with the failure it kept. Node cannot close stdio, so otherwise a stalled stderr write would hold the process open forever.
  • Test clocks. Android underFakeTimers steps 1 ms per event-loop turn, and Apple's queued readiness no longer spends fake time.

Validation

  • At 50cc9f9f76: check:affected --run --head 50cc9f9f76 — 41 passed / 25 skipped, exactCommit=true, checkoutChanged=false (receipt run-WsFLhG). The earlier push 96505cea47 failed only Fallow (unused exports on the new format module), which 50cc9f9f76 fixes.
  • Red→green for the takeover fixes: the 7 new or changed tests fail with production files at df46a350dd (7 failed / 77 passed) and pass at head (84 passed).
  • pnpm build gives a dist/src/THIRD_PARTY_NOTICES.txt that cmp matches byte for byte against the plugin-built file at df46a350dd, and the pack gate accepts it.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
agent-device-website Ready Ready Preview Nov 4, 2026 6:38am UTC

Request Review

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Size Report

Metric Base Current Diff
Installed (including dependencies) 5.71 MB 5.76 MB +49.0 kB
Package (unpacked) 5.71 MB 5.76 MB +49.0 kB
Package (download) 1.74 MB 1.75 MB +7.3 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.4 ms 20.5 ms -7.9 ms
CLI --help 55.1 ms 40.9 ms -14.3 ms

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 13 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/size-report-package.mjs Resolved
Comment thread tsdown.config.ts Resolved
@thymikee

Copy link
Copy Markdown
Member Author

I reviewed 7410a5b and found no problems that must change in the code. Not blocking, and fine to take or leave: sort the dependencies by ${name}@${version} in the notice template (scripts/lib/bundled-license-notices.ts#L13) so the file stays identical if chunks render in a different order; drop private:true from the fixture root package.json in the test so the absence check exercises the cwd exclusion that production relies on; and optionally print identical license texts once after the name@version list, keeping one entry per distinct text, since the size report shows +92.7 kB unpacked and +9.2 kB download.

Three open threads from cubic-dev-ai still apply: the missing assertBundledLicenseNotices declaration in the d.mts file (#4070 (comment)) and the duplicated dist/src output path in tsdown.config.ts (#4070 (comment)). One lower-priority thread also still applies, and none of the threads can be resolved yet.

Checks are still running. Smoke Tests is pending, and it runs pnpm check:package (action.yml#L100), which builds with the new plugin and runs the new gate on the real npm pack output. A failure there would likely be related to this change. Coverage passes now, and there are no conflicts.

I did not build or run anything. I did not observe the plugin's ordering, its cwd exclusion, or its use of renderedLength. I also did not check whether the dts pass in tsdown re-renders and overwrites THIRD_PARTY_NOTICES.txt with a partial list. The non-empty tarball assert would not catch that. Please confirm the packed notice file lists all bundled dependencies, including anything pulled in by @agent-device/testmu and @agent-device/proxy.

Before merge, Smoke Tests must pass. Then add the d.mts declaration, use one output-directory constant in tsdown.config.ts, and set entryCount to 21 in size-report-npm-pack.json.

The newer head c803382 deduplicates the license texts in the notice plugin and its test, which covers the last optional note above. It does not touch size-report-package.d.mts, tsdown.config.ts or size-report-npm-pack.json, so the three threads still apply there. The next review will cover c803382.

@thymikee

Copy link
Copy Markdown
Member Author

The code at c803382 looks good apart from the two open review threads below, which still need a fix or an answer. All 35 checks pass, including check:tooling, which runs the build and check:package, the route this diff changes. There are no conflicts.

Not blocking, and you can take or leave these: sort a copy of dependencies by name@version before the loop in scripts/lib/bundled-license-notices.ts, so chunk order cannot renumber the [n] ids (I am not sure the order varies between builds); drop private: true from the fixture root package.json in the test, so the doesNotMatch check exercises the self exclusion and not only the private-package filter; and consider adding author to the compact line, or confirm that every bundled license text has a Copyright line, because the old output also carried the author.

The Cubic threads on the missing d.mts declaration (#4070 (comment)) and the duplicated dist/src path (#4070 (comment)) still apply, and one lower-priority thread on entryCount also still applies. Please fix or answer them before merge. I did not run the build or the tests myself.

@thymikee
thymikee force-pushed the fix/third-party-license-notices branch from c803382 to b90d5ec Compare November 3, 2026 07:58
@thymikee

thymikee commented Nov 3, 2026 •

Copy link
Copy Markdown
Member Author

Addressed the three Cubic threads and the inexpensive follow-ups from the latest thymikee review.

  • Added the assertBundledLicenseNotices(entries: readonly NpmPackEntry[]): void declaration.
  • Both bundle output and notices now derive from one outDir constant.
  • Corrected the npm-pack fixture to entryCount: 21.
  • Sort a copy of dependencies by name@version before assigning text references; reversing the import order produces byte-identical notices.
  • Removed private: true from the fixture root, so the existing exclusion assertion exercises the cwd/self exclusion.
  • Restored author attribution on each compact package line when author metadata exists. This uses the plugin's formatted person value, including email/URL.

Regression evidence against rebased pre-fix 36e3bb179b9b05b09642aeeab8906f3b043ca19c, in a disposable checkout with the new test patch:

  • pnpm exec vitest run --project unit-core scripts/__tests__/bundled-license-notices.test.ts: 2 failed, 3 passed before the fix (missing author; reordered references), 5 passed after it. Together with scripts/__tests__/size-report-package.test.ts: 9 passed.
  • An import-only tsc --ignoreConfig --noEmit --module nodenext --moduleResolution nodenext --target es2022 probe: TS2305 before, exit 0 after.
  • An actual build with the output directory changed to dist/pr4070-output-probe: notices absent there before, present and nonempty after.
  • An independent assert.equal(pack.entryCount, pack.files.length) probe: 20 !== 21 before, exit 0 after.

The declaration-generation test confirms emitted .d.ts files and unchanged JavaScript dependency notices. Separately, an independent generateBundle inventory of retained JavaScript module owners matched all 27 entries in the real build, with no missing or extra packages. After declaration generation (30 top-level .d.ts files), the npm tarball's notice file is byte-identical to the generated file (48,995 bytes). @agent-device/proxy is included; no runtime modules from @agent-device/testmu survive in this build. Its workspace-only dependencies do not add separate bundled third-party packages. Private workspace packages and the root are excluded as configured.

Rebased the three existing commits onto 0763f4d639 without conflicts; reviewed git range-diff and the full diff. The original branch head was c80338229d92a9a35496b91afcb7edb10644bcec. This round used a cold self-review; no independent reviewer was delegated from this implementation sub-agent. The disposable experiment checkout was removed. No device behavior changed.

Validation at b90d5ec:

  • pnpm format: passed across the full repository.
  • pnpm check:affected --run --head b90d5ec00f42b6f1ab1939b6f62d6603484609a0: passed. Receipt run-oNploa: 41 passed, 25 explicitly skipped; exactCommit=true, checkoutChanged=false. This includes the real build/package check, local unit/integration checks, architecture checks, and selected tooling/docs checks. Skipped hosted/native/coverage lanes are not claimed as local passes.
  • Cubic completed at this head with one P3 test-style suggestion. Replied with the existing full-body and declaration-preservation coverage and resolved it; no new behavior bug and no additional push. Apex Sentinel is still reviewing this head. GitHub CI remains pending; no hosted passes are inferred from the local receipt. No labels applied; no merge performed.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="scripts/__tests__/bundled-license-notices.test.ts">

<violation number="1" location="scripts/__tests__/bundled-license-notices.test.ts:163">
P3: This test never checks that the license text reaches the notice file. Add an assertion for the fixture's LICENSE text so a dropped license body fails here.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

@apex-sentinel-staging

apex-sentinel-staging Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Apex Sentinel

Verdict: approve

Since the last review: 3 new · 2 still open · 6 no longer raised

5 finding(s): 1 minor, 4 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/lib/bundled-license-notices.ts:55 🆕 Bundled notices list first-party @​agent-device/proxy as a third-party dependency (ownership test is private === true)
🔵 Nit (4)
Where Finding
scripts/lib/bundled-license-notices.ts:121 jpeg-js special case hard-codes a package name and two LICENSES paths inside the generic notices plugin
scripts/lib/bundled-license-notices.ts:135 🆕 deduplicate keeps the last installed copy of a name@​version
scripts/lib/cli-entrypoint.ts:92 exitAfterOutput awaits a diagnostic that reportStdoutFailure may never assign
scripts/size-report-package-notices.mjs:59 🆕 Pack gate accepts a license-text section whose content is only a placeholder line

Reply under an inline comment with @apex-sentinel-staging note ... or @apex-sentinel-staging pivot ... to re-check that finding.
Comment @apex-sentinel-staging review on this conversation to run a full review.

Earlier reviews

Review of df46a350 · 03.11.2026 23:29 UTC

Apex Sentinel

Verdict: approve

Since the last review: 6 new · 2 still open · 1 no longer raised

8 finding(s): 2 minor, 6 nit. Verdict: approve.

🟡 Minor (2)

Where Finding
scripts/lib/bundled-license-notices.ts:6 🆕 bundledLicenseNotices keeps source in one closure shared by the JS and DTS builds of one config
scripts/lib/cli-entrypoint.ts:65 🆕 runEntrypoint turns any stdout/stderr flush error into exit 1, including a nonzero code main chose
🔵 Nit (6)
Where Finding
scripts/lib/cli-entrypoint.ts:43 🆕 runEntrypoint's stdout diagnostic is dropped silently when stderr cannot accept it
scripts/lib/bundled-license-notices.ts:67 The jpeg-js special case is hard-coded inline in the notice renderer
scripts/lib/bundled-license-notices.ts:75 🆕 jpeg-js version coupling is one-way: bumping only the repo copy never fails until upstream version changes
scripts/lib/bundled-license-notices.ts:88 The renderer's license-section layout is written and parsed in two places with no shared owner
scripts/lib/bundled-license-notices.ts:90 🆕 renderNotices trusts plugin-reported author/license text, so a multi-line field breaks the attribution grammar the pack gate reads
scripts/size-report-package.mjs:189 🆕 Missing-notices error says "npm pack" even when a different tree was checked
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 53539d31 · 03.11.2026 22:46 UTC

Apex Sentinel

Verdict: approve

Since the last review: 2 new · 1 still open · 7 no longer raised

3 finding(s): 3 nit. Verdict: approve.

🔵 Nit (3)
Where Finding
scripts/lib/bundled-license-notices.ts:74 renderNotices hard-codes jpeg-js and its notice-file first-line format
scripts/size-report-package.mjs:108 🆕 readAttribution can read an attribution line out of a license body and count it as a real attribution
scripts/lib/cli-entrypoint.ts:63 🆕 runEntrypoint names one stream's error after the other stream's drain
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 44b59309 · 03.11.2026 21:46 UTC

Apex Sentinel

Verdict: approve

Since the last review: 8 new · 0 still open · 5 no longer raised

8 finding(s): 8 nit. Verdict: approve.

🔵 Nit (8)
Where Finding
scripts/lib/bundled-license-notices.ts:28 🆕 The notices file name is duplicated between the bundler's emit and the publish gate's constant
scripts/lib/bundled-license-notices.ts:42 🆕 Version is trimmed to validate but emitted raw, producing an attribution line the pack gate cannot parse
scripts/lib/bundled-license-notices.ts:75 🆕 jpeg-js version coupling between the shipped LICENSES file and a version range is implicit
scripts/lib/bundled-license-notices.ts:78 🆕 jpeg-js source-notice path and version pattern are hard-coded at their point of use
scripts/lib/cli-entrypoint.ts:3 🆕 isClosedPipe narrows with error.code after a in check instead of a cast
scripts/lib/cli-entrypoint.ts:12 🆕 drainOutput leaves a live 'error' listener and a pending empty write on a stream that already finished
scripts/lib/cli-entrypoint.ts:73 🆕 A write-error diagnostic written after the drain timeout is discarded by the immediate process.exit
scripts/size-report-package.mjs:103 🆕 readAttribution cannot match an attribution line whose license expression contains a parenthesis, so the gate reports 0 attributions and blocks the release
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of a5c849d6 · 03.11.2026 20:48 UTC

Apex Sentinel

Verdict: approve

Since the last review: 3 new · 2 still open · 4 no longer raised

5 finding(s): 5 nit. Verdict: approve.

🔵 Nit (5)
Where Finding
scripts/check-package.ts:104 🆕 check-package reads only packed.filename from parseNpmPackOutput, so the new aggregate-size validation never runs in the publish gate
scripts/lib/bundled-license-notices.ts:18 Notice emission depends on rollup-plugin-license internals under a caret range
scripts/lib/bundled-license-notices.ts:94 🆕 renderNotices output has no trailing newline, unlike the zero-dependency placeholder
scripts/lib/cli-entrypoint.ts:79 🆕 A throwing stderr write on the main() failure path escapes as an unhandled rejection
scripts/size-report-package.mjs:104 assertBundledLicenseNotices hard-codes 'npm pack' in the manifest failure, ignoring the caller's context
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 0fe4cbdd · 03.11.2026 17:20 UTC

Apex Sentinel

Verdict: approve

Since the last review: 3 new · 3 still open · 2 no longer raised

6 finding(s): 1 minor, 5 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/lib/bundled-license-notices.ts:18 🆕 Build silently depends on rollup-plugin-license 3.7.1 internals: synchronous thirdParty.output and hook object shape
🔵 Nit (5)
Where Finding
scripts/lib/bundled-license-notices.ts:28 Duplicated 'THIRD_PARTY_NOTICES.txt' file name in emitter and pack assertion
scripts/lib/bundled-license-notices.ts:53 renderNotices reports a missing version as "missing its license or license text"
scripts/lib/bundled-license-notices.ts:60 Curated jpeg-js notice check is a hard-coded special case that cannot satisfy multipleVersions: true
scripts/lib/cli-entrypoint.ts:56 🆕 Shadowed error in the reportStdoutFailure catch discards the original failure
scripts/size-report-package.mjs:68 🆕 Pack gate passes on the "no third-party dependencies are bundled" placeholder
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 63a8a9a1 · 03.11.2026 17:07 UTC

Apex Sentinel

Verdict: approve

Since the last review: 5 new · 0 still open · 4 no longer raised

5 finding(s): 5 nit. Verdict: approve.

🔵 Nit (5)
Where Finding
scripts/lib/cli-entrypoint.ts:12 🆕 drainOutput leaves its 'error' listener attached and never cancels the in-flight empty write
scripts/lib/bundled-license-notices.ts:18 🆕 dts rebuild rewrites THIRD_PARTY_NOTICES.txt with a byte-identical file every build
scripts/lib/bundled-license-notices.ts:29 🆕 The notices file name 'THIRD_PARTY_NOTICES.txt' is hard-coded on line 29 here, and the publish gate asserts on the …
scripts/lib/bundled-license-notices.ts:58 🆕 renderNotices rejects a missing license or text but not a missing version
scripts/lib/bundled-license-notices.ts:63 🆕 jpeg-js attribution rules are hard-coded inline in renderNotices
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 4e6e8d95 · 03.11.2026 16:42 UTC

Apex Sentinel

Verdict: approve

Since the last review: 2 new · 2 still open · 4 no longer raised

4 finding(s): 1 minor, 3 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/size-report-package.mjs:169 🆕 parseNpmPackOutput validates file entries but not the size fields the size report consumes
🔵 Nit (3)
Where Finding
scripts/lib/bundled-license-notices.ts:61 🆕 jpeg-js version gate only checks the first line, so a bumped jpeg-js whose notice text is stale still passes
scripts/lib/cli-entrypoint.ts:34 runEntrypoint keeps a permanent 'error' listener on process.stdout/stderr that can mask the drain failure with a later one
scripts/lib/cli-entrypoint.ts:46 stdout flush failure diagnostic is silently dropped when stderr is also failing
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 4348d35b · 03.11.2026 16:14 UTC

Apex Sentinel

Verdict: approve

Since the last review: 2 new · 4 still open · 8 no longer raised

6 finding(s): 1 minor, 5 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/lib/cli-entrypoint.ts:7 Sticky stream.errored turns a mid-run transient output error into a failed exit code
🔵 Nit (5)
Where Finding
scripts/size-report-package.mjs:68 🆕 The emitted asset name lives in two hard-coded places with nothing linking them: bundled-license-notices.ts:28 emits …
scripts/lib/bundled-license-notices.ts:19 generateBundle override discards rollup-plugin-license's hook-object ordering metadata
scripts/lib/bundled-license-notices.ts:60 bundled-license-notices.ts:60-73 puts one curated package's rules inside the generic renderer: the literal package na…
scripts/lib/bundled-license-notices.ts:61 🆕 jpeg-js license pairing is duplicated by hand between the fixture file and its own guard
scripts/lib/cli-entrypoint.ts:36 Permanent no-op 'error' listeners on process.stdout/stderr mask stream errors if main() never settles
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 4cad2690 · 03.11.2026 15:19 UTC

Apex Sentinel

Verdict: approve

Since the last review: 9 new · 3 still open · 3 no longer raised

12 finding(s): 2 minor, 10 nit. Verdict: approve.

🟡 Minor (2)

Where Finding
scripts/lib/cli-entrypoint.ts:13 🆕 exitAfterOutput can hang instead of exiting when the write callback never settles
scripts/lib/cli-entrypoint.ts:33 The new drain turns a real write error (EIO, ENOSPC on a redirected file, EBADF) into exit code 1 without saying so: …
🔵 Nit (10)
Where Finding
scripts/check-package.ts:245 🆕 assertBundledLicenseNotices is called twice on the same entries in check-package.ts
scripts/lib/bundled-license-notices.ts:17 🆕 Comment about the plugin's synchronous callback sits on the wrong statement
scripts/lib/bundled-license-notices.ts:19 Wrapper around rollup-plugin-license silently drops its hook metadata and can collide on the emitted asset name
scripts/lib/bundled-license-notices.ts:48 🆕 Dependency id sort recomputes its key on every comparison instead of a precomputed sort key
scripts/lib/bundled-license-notices.ts:61 🆕 jpeg-js source-file notices are not checked against the bundled sources, so attribution can be silently incomplete or stale
scripts/lib/bundled-license-notices.ts:63 jpeg-js source-notice special case is hard-coded inside the shared renderer
scripts/lib/cli-entrypoint.ts:1 🆕 runEntrypoint treats every ERR_STREAM_DESTROYED as a benign closed pipe, hiding real post-destroy write errors
scripts/lib/cli-entrypoint.ts:10 🆕 Comment line removed from runEntrypoint separated the doc from the function it documents
scripts/size-report-package.d.mts:5 🆕 assertBundledLicenseNotices is stated in the .d.mts but absent from the .d.mts-linked runtime's own type contract — the declared API is exported, the type file declares it without asserting the linked runtime actually provides it
scripts/size-report-package.d.mts:17 🆕 parseNpmPackOutput became exported (i.e. public to check-package.ts) and now returns { ...pack, files } — every…
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 02ff19e5 · 03.11.2026 12:43 UTC

Apex Sentinel

Verdict: approve

Since the last review: 3 new · 3 still open · 6 no longer raised

6 finding(s): 1 minor, 5 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/lib/cli-entrypoint.ts:16 Drained output write errors are now silent — runEntrypoint's prefix line is unreachable for them
🔵 Nit (5)
Where Finding
scripts/lib/bundled-license-notices.ts:18 🆕 Notice emitter depends on rollup-plugin-license's internal hook shape and hook order
scripts/lib/bundled-license-notices.ts:25 THIRD_PARTY_NOTICES filename duplicated between plugin and pack guard
scripts/lib/bundled-license-notices.ts:54 jpeg-js extra-notice handling is hard-coded in a generic loop
scripts/lib/bundled-license-notices.ts:58 🆕 jpeg-js source-file notice version guard passes on a blank first line when the file starts with CRLF
scripts/size-report-package.mjs:68 🆕 size-report base-measurement set is fragile: assertBundledLicenseNotices was added to the size-report-package.mjs module, not just the policy function
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 74a7b5b4 · 03.11.2026 10:25 UTC

Apex Sentinel

Verdict: approve

Since the last review: 5 new · 4 still open · 1 no longer raised

9 finding(s): 2 minor, 7 nit. Verdict: approve.

🟡 Minor (2)

Where Finding
scripts/check-package.ts:95 🆕 check-package.ts reads npm's raw size field without the validation the same file's owner applies elsewhere
scripts/lib/cli-entrypoint.ts:5 Flush swallows every stream error, not just the closed-pipe ones
🔵 Nit (7)
Where Finding
scripts/lib/cli-entrypoint.ts:5 🆕 The exit flush resolves immediately on an empty write, so it does not prove the piped output was handed off
scripts/check-package.ts:34 check-package.ts imports the assert and its type from a size-report module whose shipped-imports guard forbids
scripts/lib/bundled-license-notices.ts:5 🆕 bundledLicenseNotices has no return type and no doc comment
scripts/lib/bundled-license-notices.ts:14 🆕 Notice file has no trailing newline except in the no-dependencies case
scripts/lib/bundled-license-notices.ts:18 THIRD_PARTY_NOTICES.txt path is spelled out in four places with no shared owner
scripts/lib/bundled-license-notices.ts:40 🆕 The license check only hashes the first line of LICENSES/jpeg-js.txt, so a stale body or a missing Apache text still ships
scripts/lib/bundled-license-notices.ts:40 jpeg-js is special-cased with hard-coded names and paths inside the generic notice plugin
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of 05a76ca8 · 03.11.2026 09:44 UTC

Apex Sentinel

Verdict: approve

Since the last review: 3 new · 2 still open · 2 no longer raised

5 finding(s): 1 minor, 4 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/lib/cli-entrypoint.ts:5 🆕 runEntrypoint: a failed flush now rejects unhandled instead of reporting one prefixed line
🔵 Nit (4)
Where Finding
scripts/lib/bundled-license-notices.ts:13 The notices filename and its packed path are duplicated across two owners
scripts/size-report-package.mjs:68 New pack gate is satisfied by the "No third-party dependencies are bundled." placeholder
scripts/check-package.ts:103 🆕 packTarball now asserts publish contents as a side effect, duplicating the check in assertPublishPackageContents
scripts/lib/bundled-license-notices.ts:38 🆕 The jpeg-js source-notice rule is hardcoded inside the generic notices plugin
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of fbd18153 · 03.11.2026 08:39 UTC

Apex Sentinel

Verdict: approve

Since the last review: 2 new · 2 still open · 3 no longer raised

4 finding(s): 1 minor, 3 nit. Verdict: approve.

🟡 Minor (1)

Where Finding
scripts/lib/bundled-license-notices.ts:15 Empty bundle writes a non-empty notices file, so the pack gate accepts a build with zero notices
🔵 Nit (3)
Where Finding
scripts/lib/bundled-license-notices.ts:12 Notices filename and path are spelled in three places that must drift together
scripts/lib/bundled-license-notices.ts:32 🆕 Missing-license error reports an unknown version and hides which of the two checks failed
scripts/lib/bundled-license-notices.ts:40 🆕 Missing LICENSES/ files surface as a raw ENOENT instead of the actionable refresh message
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out
Review of b90d5ec0 · 03.11.2026 08:06 UTC

Apex Sentinel

Verdict: approve

5 finding(s): 5 nit. Verdict: approve.

🔵 Nit (5)
Where Finding
scripts/lib/bundled-license-notices.ts:15 The "no dependencies bundled" placeholder passes the non-empty notices gate
scripts/lib/bundled-license-notices.ts:26 Notice ordering uses localeCompare, so the generated file is not byte-stable across ICU locales
scripts/lib/bundled-license-notices.ts:37 jpeg-js notice file hardcodes version 0.4.4 with nothing tying it to the installed dependency
scripts/lib/bundled-license-notices.ts:37 jpeg-js is hard-coded inside the generic license plugin
scripts/size-report-package.mjs:69 The notices path is spelled out in three places
Not in the reviewers' prompt (1 file)
  • pnpm-lock.yaml: generated or a lockfile, so its lines were left out

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 5 new findings on b90d5ec. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/size-report-package.mjs Resolved
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Pushed fbd1815 for the final bot round.

  • Use code-unit ordering for name@version; reject jpeg-js when its bundled version differs from the first-line version in LICENSES/jpeg-js.txt.
  • Regression proof at b90d5ec: 2 failed, 5 passed before the fixes; 11 license/package tests passed afterward. pnpm format passed.
  • pnpm check:affected --run --head fbd18153e9f24616c2511c877727ed7f760e9f6d passed: receipt run-exdk4f, 41 passed / 25 explicitly skipped, exactCommit=true, checkoutChanged=false.
  • Replied to and resolved all five Sentinel threads. The placeholder, jpeg-js special case, and path declarations remain with reasons.

Coverage diagnosis: the successful exact-base main step took 11m27s. The b90d5ec attempts failed after 10m47s / 9m06s, before the 30-minute timeout. The one retry reports failure and only exit code 1, with no test failure, threshold diagnostic, timeout, or runner-loss annotation. Changed-line checks were skipped; new scripts are excluded from the global coverage include set. Unchanged b90d5ec full coverage passed locally: 18,367 tests, 93.06% statements / 94.75% lines, plus 11 fuzz tests. Hosted cause remains unproven; this is not a confirmed unrelated flake. No more retries of that job.

Fresh-head CI/bot reviews are pending. Later nits receive replies without another push. No labels or merge.

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 2 new findings on fbd1815. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Coverage follow-up at 05a76ca:

Confirmed reporting defect: runEntrypoint forced process.exit while stdout/stderr still had pending pipe writes. The gate forwards output; there is no retained hidden report. A regression exercising the real entrypoint received 65,536 of 4,194,327 stdout bytes before the fix. Both return-code and thrown-error cases failed; both pass after draining stdout/stderr. Exit codes 7 and 1 remain unchanged. The command runner now uses that shared drain instead of its duplicate.

The license tests now invoke tsdown in a child process. Native bundler/compiler state stays outside the instrumented Vitest worker; failed builds propagate their nonzero status and stderr. The existing minification, exclusion, deduplication, declarations, and jpeg-js mismatch assertions remain.

Reproductions used OUTPUT_ECONOMY_BASE=0763f4d639243aafaeacae55989fbe801a86c0d4, including full-output controls with the license test included and excluded. Both controls passed on macOS with CI's Node 24.13.1 / pnpm 11.17.0. The expected missing-license build also left process.exitCode unchanged in the direct probe. The original Linux exit-1 trigger was not reproduced locally; this is not evidence of an unrelated flake or a proven exitCode leak. The confirmed reporting bug is fixed, and the new native build work is isolated.

Validation at this exact head:

  • pnpm format: passed.
  • Focused suites: 13 passed; output-drain regression 2 failed before → 2 passed after.
  • pnpm check:affected --run --head 05a76ca8204613ac2d1004b170ea937667e51762: passed, receipt run-cHyowq, 41 passed / 25 explicitly skipped, exactCommit=true, checkoutChanged=false.
  • OUTPUT_ECONOMY_BASE=0763f4d639243aafaeacae55989fbe801a86c0d4 pnpm gate unit-ci, with Node 24.13.1 / pnpm 11.17.0, run in the foreground through a pipe with pipefail: passed. Complete ending report retained: 2,084 instrumented files, 18,371 tests passed / 1 skipped; 93.06% statements, 94.74% lines, then 11 fuzz tests passed.
  • pnpm gate coverage --base 0763f4d639243aafaeacae55989fbe801a86c0d4: passed, 0 applicable changed executable lines; no threshold change or waiver.

Cold diff review completed; no independent reviewer delegated from this implementation child. The disposable checkout was removed. Fresh hosted Coverage is still required. No CI reruns, labels, or merge.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 6 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/lib/cli-entrypoint.ts Resolved

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 2 new findings on 05a76ca. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/cli-entrypoint.ts Resolved
Comment thread scripts/check-package.ts Resolved
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Follow-up at 74a7b5b:

  • Fixed Cubic r4452988149 / Sentinel r4453035822. Drains resolve for unavailable streams, write-callback errors, and one-shot stream errors; the command's exit code stays authoritative. A real consumer closes stdout before main runs, synchronized by IPC. On 05a76ca: 2 failed / 2 passed with exits changed to 1 and unhandled EPIPE. Fixed: 4 passed, preserving 0 and 7 with empty stderr, plus complete-output behavior.
  • Addressed r4453035836: packTarball only packs/returns entries. The verification flow explicitly invokes the shared notice guard; its policy remains implemented once.
  • pnpm format and all 15 focused tests passed.
  • pnpm check:affected --run --head 74a7b5b4be5a94b2c5f1728637d44ca73bef24c9: passed, receipt run-AR1jLU, 41 passed / 25 explicitly skipped, exactCommit=true, checkoutChanged=false.

Slow-test investigation: the complete 05a76ca Coverage job log has no Slow-test marker. The requested license-only coverage run retained the slow-test reporter and used Node 24.13.1 / pnpm 11.17.0:

Test Duration
Minified license/NOTICE 206 ms
Missing license 120 ms
Deduplication/import ordering 242 ms
Code-unit ordering 126 ms
Declaration preservation 571 ms
jpeg-js 0.4.4 124 ms
jpeg-js mismatch 115 ms

All seven tests passed. That deliberately partial coverage invocation exited 1 on repository-wide coverage floors, not a failed or slow test. The CI timing hypothesis remains unconfirmed. No slow-test budget, pin, or allowlist changed.

Cold diff review completed; no independent reviewer delegated. The experiment checkout was removed. One push; no old-job rerun, labels, or merge. Fresh hosted Coverage/reviews remain pending.

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 4 new findings on 74a7b5b. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/cli-entrypoint.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

The code at 74a7b5b has no blocking issue, but merge readiness still needs complete Linux Coverage evidence and a successful Smoke Tests finish. Since the earlier review, the shared pack-entry type, output-directory consistency, fixture count, deterministic ordering, supplement version checks, and closed-stdout handling are fixed. No conflicts are known.

Coverage is reported as FAILURE, but the supplied excerpt ends during test output without a terminal diagnostic or suite summary, so its cause remains unassigned. Its route runs through the gate runner, output/exit forwarding, and test:coverage:ci across all non-fuzz-worker projects, including both new unit-core suites, then fuzz-worker; this overlaps the change. Smoke Tests is IN_PROGRESS and also covers affected build/package checks and registered gates. Earlier macOS passes and unchanged-base success do not establish an unrelated failure. Before merge, obtain the complete exact-head Linux Coverage outcome, diagnose its first failing diagnostic on that route, fix the responsible code and rerun; Smoke Tests must also finish successfully.

Not blocking: could both application and validation entrypoints call the existing exitAfterFlush owner, after extending it for the demonstrated real-pipe cases while preserving bounded completion and the regression assertions, then delete the nested exitAfterOutput implementation and keep error-prefix formatting in runEntrypoint; this consolidation suggestion can be taken or left, and the answered jpeg-js table and independent package-path questions stay closed.

The non-EPIPE policy thread still applies as an answered policy tradeoff, and two lower-priority style threads still apply. The remaining threads can be resolved: shared entry type now uses NpmPackEntry; output destinations use one outDir; fixture count matches its entries; body assertions separately cover full license and NOTICE text; empty notice is valid when no third-party code survives and retained dependencies fail closed; ordering uses code-unit comparisons with regression coverage; version drift is rejected; supplement table adds no demonstrated correctness benefit; independent paths support standalone copying and fail closed on drift; entry normalization has no evidenced malformed producer or false rejection; missing-file errors identify the file and fail the build; unavailable stdout has guards and real closed-pipe assertions; standalone imports do not prevent independent execution; flush completion waits behind prior writes and has exact 4-MiB length and tail assertions, with no demonstrated current failure; supplement body loss is not demonstrated because complete tracked texts are read and version drift is rejected.

I read the code and the check output; I did not build, pack or run tests, so the notice file contents and test counts are unverified. No live device proof is required for these build, test, and validation changes.

@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Fixed in 5c64e009d4ede1b71a310923d4620a0276248676 (one push).

The failed Coverage job had one actual test failure: owner-files-no-leak.test.ts. The raw job-log API contains the complete summary; gh run view --log omitted its tail. Recover it with gh api --allow-escape-sequences repos/callstack/agent-device/actions/jobs/125853841894/logs. The slow-test gate did not fail.

Coverage starts without dist. The notice plugin ignored --out-dir and created shared dist/src/THIRD_PARTY_NOTICES.txt. Local setup builds masked that write. Notices now use the bundler's asset emission, which follows the resolved output directory.

Evidence:

  • Existing clean-dist test: red→green on macOS and Ubuntu amd64; new output-override regression also red→green.
  • EIO and malformed npm-entry sizes: five red cases, then 23 focused tests passed. Closed-pipe exit codes remain preserved. Actual jpeg-js decoder/encoder notices, Ritter attribution, and full generated jpeg-js/Apache bytes were checked.
  • Linux license coverage profile: all eight tests passed in 1.12–2.55 seconds each; no slow-test pins, budgets, or allowlists changed. Its partial-suite global-floor failure is expected and is not a validation pass.
  • pnpm format: passed. Affected receipt run-e5v7YS: 41 passed, 25 skipped, exactCommit=true, checkoutChanged=false.
  • Clean-dist macOS CI=true GITHUB_ACTIONS=true OUTPUT_ECONOMY_BASE=0763f4d639243aafaeacae55989fbe801a86c0d4 pnpm gate unit-ci 2>&1 | tee ...: passed, 18,381 tests plus 11 fuzz-worker tests. Node 24.13.1 / pnpm 11.17.0.
  • Clean-dist Ubuntu 24.04 amd64 full-output pnpm gate unit-ci: passed, 18,322 tests plus 11 fuzz-worker tests; coverage floors passed. Four-CPU/16-GiB Colima via Rosetta; only dot→verbose changed, with every gate reporter preserved.

Cold diff review completed; fresh hosted checks and bot review remain required. No CI rerun, labels, or merge.

@thymikee
thymikee force-pushed the fix/third-party-license-notices branch from 5c64e00 to 02ff19e Compare November 3, 2026 12:26
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Rebased onto main c8ad8fb1257787ce0973cf74271cb9a4fafb51d9 and pushed once at 02ff19e52411db4cf7ba30ad55eb2fab73333b58 with an explicit lease against 5c64e009d4ede1b71a310923d4620a0276248676.

git range-diff shows all nine commits patch-identical. No unresolved review threads or new actionable finding remained. Prior optional consolidation suggestions stay closed. Independent read-only review of the rebased code and smaller-design question found no regressions.

Fresh validation: frozen install, build, full-repository format, and 23 focused tests passed. pnpm check:affected --run --head HEAD passed at the pushed SHA: receipt run-V6j1qS, 41 passed / 25 explicitly skipped, exactCommit=true, checkoutChanged=false. This includes the selected unit, integration, and packaging checks; skipped/full hosted checks remain CI-owned.

Earlier red/green and Linux/macOS coverage evidence retains its original SHA attribution. Fresh hosted checks and reviews are pending. No merge or labels.

`underFakeTimers` advanced the clock 10 ms per awaited step, and each step is a real
event-loop turn. A fill-verification sample crosses a real loopback socket to the fake
helper session, so the turns it needed burned fake time: the 150 ms sample interval came
out at 230-250 ms, and the 1500 ms deadline was reached with a sample still owed. The
late-value test then saw seven captures instead of eight and reported a mismatch, at
three in ten runs on an unloaded host.

One millisecond per step keeps a socket reply inside its own interval: the interval is
met at 150-160 ms and the test's sample count is no longer a function of host speed.
The budgets and every assertion stay as they were.
`rollup-plugin-license` types `Dependency.version` as optional and reports `null` for a module
whose `package.json` has none, which is what an unresolvable or path-linked module looks like to
it. `renderNotices` tested only the license and its text, so an otherwise fully licensed
dependency was emitted as `included@null (MIT): [1]` — a shipped notice attributing code to a
release that never existed — and a dependency missing both fields was reported as a license
problem, pointing at a LICENSE file instead of at the version-less module.

The version is now checked first, so every later diagnostic can name the package the way the
notice file does. No shipping package is affected: the current build resolves a version for all
27 bundled dependencies.
`assertBundledLicenseNotices` read the `npm pack` manifest, which can only say that
`dist/src/THIRD_PARTY_NOTICES.txt` exists and how many bytes it is. The bundler writes a 41-byte
"No third-party dependencies are bundled." notice when it reports zero dependencies, and a
dependency that carries neither a version nor a license identifier is reported as none — with its
code still bundled. Either way the publish gate cleared a tarball shipping unattributed code,
which is the failure this PR exists to prevent.

The published CLI always bundles, so its notices must carry at least one `name@version (license)`
attribution and one referenced license text. The gate now reads those bytes: from the tree it just
packed, and again from the clean-installed tree a user receives. A project that genuinely bundles
nothing still gets the generic notice — that policy is the published package's, not the
bundler's — and a caller holding only the manifest keeps the manifest-only check, which is what
the size lane's base measurement of an older tree needs.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 8 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/size-report-package.mjs Resolved
Comment thread scripts/size-report-package.mjs Resolved
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Published a5c849d6ccb14f82553d778a1600a8e6463c6a7e (fast-forward from 0fe4cbdd29). Follow-up delta: 8 files, +244/−19, four commits — two test-clock repairs and the two accepted script hardenings, each self-contained.

Exact-head gate pnpm check:affected --run --head HEAD: exit 0, 41 passed / 25 skipped, exactCommit=true, checkoutChanged=false, unit 17205 passed / 0 failed / 1 skipped. Receipt run-z50Uib. Not hidden: the first attempt at this range failed the fallow gate (unused export, renderNotices complexity) — evidence kept as 4070-gate-fallow-fail-{fallow.log,3420-pre-receipt.json}; both were repaired at source (path constant made module-private, validation extracted to assertBundledDependencyAttributable), not baselined, and the audit now matches base's warn-only state.

Red→green against published head (0fe4cbdd29 sources, final test files): 5 failures — 2 missing-version (Missing expected rejection; head emitted included@null (MIT): [1]) and 3 placeholder cases (Missing expected exception; the 41-byte "No third-party dependencies are bundled." cleared the gate with a healthy manifest entry). Green at this head: 39/39. The content policy counts name@version (license) attributions and [n] license-text references in the bytes (real artifact: 27 / 23; placeholder: 0 / 0; attributions without text: 1 / 0), so it is not a byte-size equality check, and expectations are written out rather than produced by the plugin. Generic empty-bundle emission is pinned unchanged, and base's own size-report-package.mjs carries no notices assertion, so the size lane's base-measurement copy contract is intact.

Clock repairs, candid scope. Android: underFakeTimers advanced 10 ms per real event-loop turn, so a fill-verification reply crossing the loopback socket burned fake time and the polling helper's own interval could fire ahead of the reply it awaited. A 2×2 attribution matrix isolated the helper, not the fixture or production code; the visible symptom was the deadline being reached with a sample owed — 7 captures where the test asserts 8. At 1 ms the focused test passed 8/8 repeats here, and the unit lane cost 133.5 s → 137.2 s with the same 17186 passing. These are finite samples on one host, not proof the failure is impossible. Apple: a delayed-readiness probe showed the queued request settling at 605000 ms of fake elapsed, and a finer drain step did not help — only spending no fake time did, which is what the one-line change does. --project apple-runner 888 passed locally; that project runs in no local lane, so hosted Coverage owns it.

No push-side source edits were made after review approval. Independent Apex review approved 0fe4cbdd29..a5c849d6cc with no blocking findings.

Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/size-report-package.mjs Resolved
The published-notices gate counted the two kinds of line a notices file
holds: at least one `name@version (license)` attribution and at least one
`[n]` marker. A file of bare headings satisfies both counts and carries no
license terms at all, so `pkg@1.0.0 (MIT): [1]` over an empty `[1]` cleared
the gate, as did a reference to an id the file never writes.

The gate now reads the sections: every reference between a package's colon
and its optional `; Author:` clause must name a numbered section with text
in it, and the failure names the attributions that do not. Deduplicated and
multi-reference files, author clauses, and license bodies with blank lines
inside them are the shapes the real build writes, so they stay accepted, and
a tree that genuinely bundles nothing still gets the generic notice.
The generic notice for a bundle with no third-party code ended in a newline
and the populated one did not, so the file a user reads ran its last license
block against the shell prompt. Every other text file the build writes ends
one line, and the notice is 49 kB of text people read with `cat`.

One newline after the joined sections, and the existing populated fixture now
pins that the file ends in exactly one.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/size-report-package.mjs Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 3 new findings on 44b5930. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/cli-entrypoint.ts Resolved
Comment thread scripts/lib/cli-entrypoint.ts Resolved
Comment thread scripts/size-report-package.mjs Resolved
The published-notices gate matched the license field with `[^)]+`, so an
attribution whose license is a grouped SPDX expression — `renderNotices`
wraps whatever the plugin reports, and the plugin passes a parenthesized
`license` field through, producing `pkg@1.0.0 ((MIT OR Apache-2.0)): [1]` —
was not recognized at all. A notices file holding only such a line reported
zero attributions, so the gate rejected bytes a healthy build produced;
mixed with a valid line, the unparseable one was skipped, so a grouped
attribution with a dangling or missing reference cleared the gate unseen.

The field now ends at the first `):` boundary, the only place a nested
expression can close, and the reference list stops at the renderer's
optional `; Author: ` clause. That also drops a disclosed phantom: a
package whose author field carried `[7]` read that footnote as a second
license reference, failing valid bytes that referenced only `[1]` (and
passing a file whose sole "reference" was such a footnote).

The new cases run the real license plugin over a fixture tree, so they
assert on bytes `renderNotices` emits, not on hand-written shapes: a
grouped expression and an author footnote each pass the pack gate, and a
grouped line whose reference is dangling or absent still fails naming it.
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

Evidence at 53539d311c — grouped SPDX field and the author reference boundary

Bytes, not hand-written shapes: two new tests build a fixture tree with the repo's own fixture build (scripts/__tests__/fixtures/bundled-license-build.mts, the real bundledLicenseNotices plugin under tsdown) and read the file the plugin emits.

What the renderer writes for supported input:

  • included@1.2.3 ((MIT OR Apache-2.0)): [1]; Author: … — a parenthesized license field plus renderNotices' own parentheses
  • distinct@1.2.3 (MIT): [1]; Author: Bracket author [7] <b@example.test> — an author field carrying a bracketed footnote

Old reader (\([^)]+\)) at 44b59309e8, both directions:

  • file holding only such a line → rejected: found 0 attributions and 1 license texts
  • file mixing a valid simple attribution with a grouped one → accepted, with the grouped line never inspected: … (MIT OR (Apache-2.0 AND CC0-1.0)): [9] (no such section) and … (MIT OR Apache-2.0): (no reference) each cleared the red run
  • author footnote → rejected: unresolved: distinct@1.2.3 (MIT) -> [1], [7]

New reader: both real-bytes files pass; the two mixed files above now fail naming the grouped attribution (-> [9], -> no reference); an author clause contributes no reference in either direction. Bare [n] headings, dangling ids and missing references keep failing. Published artifact still accepted: dist/src/THIRD_PARTY_NOTICES.txt, 48,996 B, 27 attributions over 23 sections, LF-terminated.

Counts: focused size-report-package.test.ts 4 failed / 30 passed at 44b59309e8 → 34 passed at 53539d311c (45 passed with bundled-license-notices.test.ts). pnpm check:affected --run --head HEAD at 53539d311c: 41 passed / 25 skipped, exactCommit=true, checkoutChanged=false (.tmp/validation/run-35sFJ1/receipt.json).

Scope kept honest: the license field is read as opaque producer text, not audited for SPDX validity.

Comment thread scripts/size-report-package.mjs Resolved
The pack gate scanned every line of THIRD_PARTY_NOTICES.txt for an
attribution, but the renderer emits the attributions as one block before
the first numbered section and copies each section's license or NOTICE
text verbatim after it. Prose there belongs to the upstream package: a
contact line or a pointer to "section [9]" shaped like an attribution
made the gate reject bytes the bundler legitimately wrote, and one such
line let a file that attributes nobody clear the gate that exists to
catch exactly that. Scope attribution parsing to the header block the
producer writes; the section scan, the reference checks and the reject
directions stay as they were.

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 1 new finding on df46a35. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/cli-entrypoint.ts Resolved
@thymikee

thymikee commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

At df46a35, the earlier review's notice checks are stronger, but two issues remain. CI reports 36 passing checks. There are no conflicts.

The package gate can pass when a bundle contains a valid licensed dependency and another dependency with neither license nor version: the plugin omits the latter from its report, so it is never validated. Published code can therefore lack required notices. The build must independently enumerate every bundled third-party module and require attributable metadata and license text for each; please cover a mixed valid and omitted dependency through build and publish validation.

Could early stdout errors leave the script hanging when stderr is backpressured and main remains pending? This code is unchanged from the earlier review, and its drain deadline starts only after main settles. One output-completion owner should bound diagnostics in both paths; please cover pending main with an unread stderr pipe.

Could the notice-format reader move beside its renderer, with build-time module enumeration enforcing completeness before formatting? Extracting a dependency-free format owner, including it in the standalone size-script copy contract, and replacing the existing parser would let the gate call that contract while retaining package-path policy.

Two lower-priority threads still apply: format ownership and multiline metadata. The other twelve threads no longer apply at this head.

Before merge, please close the bundled-dependency completeness gap, bound early diagnostics, and consolidate the notice-format parser with its owner. I did not run tests or reproduce the reported timings and artifact sizes. No live device run is needed for these build and package changes.

thymikee and others added 2 commits November 4, 2026 07:35
The build now walks every module rendered into the bundle and attributes
each third-party one to its package itself, replacing rollup-plugin-license.
That plugin dropped a package whose manifest named neither a version nor a
license, so a bundle beside a licensed neighbor shipped a partial notices
file the pack gate accepted. A module with no package.json under
node_modules now stops the build too. The real artifact is byte-identical
(48,996 bytes, 27 attributions over 23 sections).

The notices format moves into scripts/size-report-package-notices.mjs: one
dependency-free owner of the renderer and reader. The pack gate calls its
reader and keeps the package-path policy; the size lane's base step copies
size-report-package*.mjs so the measured revision's format travels with its
policy. Multiline license and author text renders on one attribution line.

runEntrypoint routes both output paths through settleOutput. An early
stdout-failure diagnostic still unflushed at its deadline exits with the
retained failure while main is pending, since Node cannot close stdio and a
stalled stderr write would otherwise hold the process forever.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The empty-bundle notice is internal to the renderer, and the hand-written
size-report-package-notices.d.mts joins the existing size-report-package.d.mts
Fallow entry: its consumers resolve it through tsc, not an import edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread scripts/lib/bundled-license-notices.ts Resolved

@apex-sentinel-staging apex-sentinel-staging Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apex Sentinel: 3 new findings on 50cc9f9. The verdict and the full list are in the summary comment.

Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/size-report-package-notices.mjs Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 13 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
Comment thread scripts/lib/bundled-license-notices.ts Resolved
@thymikee

thymikee commented Nov 4, 2026 •

Copy link
Copy Markdown
Member Author

Response to the review at df46a35, now at head 50cc9f9f76 (takeover; fixes in 96505cea47 and 50cc9f9f76).

Red/green method: I put the new tests on top of the production files from df46a350dd with git checkout df46a350dd -- <prod files> on a WIP commit, plus that revision's lockfile so rollup-plugin-license was installed. Result: 7 failed / 77 passed. At head: 84 passed. Gate on the exact pushed head: pnpm check:affected --run --head 50cc9f9f76…, receipt run-WsFLhG, 41 passed / 25 skipped, exactCommit=true, checkoutChanged=false. The first push 96505cea47 failed only Fallow, on unused exports in the new module (receipt run-bzLo8I); 50cc9f9f76 fixes that. Hosted CI at 50cc9f9f76: 26 success / 10 skipped, Cubic included.

Item Change Evidence (red at df46a35 → green at head)
1. Bundled-dependency completeness bundledLicenseNotices now goes through every module rendered into each output chunk (renderedLength > 0) and maps it to its nearest named package.json. Each third-party package must have a version, a license, and license text. A module under node_modules with no package.json stops the build. rollup-plugin-license is removed (−365 lockfile lines). The real dist/src/THIRD_PARTY_NOTICES.txt is cmp-identical to the plugin build: 48,996 bytes, 27 attributions / 23 sections. Build: bundling rejects an unattributable module beside a licensed one: package with neither license nor version and …: module with no package.json (scripts/__tests__/bundled-license-notices.test.ts). Publish: a licensed dependency cannot carry an unattributable one past build and pack gate (scripts/__tests__/size-report-package.test.ts): the build fails, and the gate on that tree reports npm pack has no dist/src/THIRD_PARTY_NOTICES.txt. All three red → green.
2. Early diagnostics bounded settleOutput owns output completion on both paths. The early stdout-failure diagnostic gets the same deadline. If it is still unflushed at that deadline while main is pending, the script exits with the failure it kept, typed as OutputDeadlineExceeded. Destroying stdio cannot be used here: Node's stdio destroy() never closes the fd, which I checked in a probe where the PipeWrap stayed active. an early diagnostic behind an unread stderr pipe cannot keep a pending command alive (scripts/lib/cli-entrypoint.test.ts): real unread stderr pipe holding 4 MiB, pending main. Red: the leak guard fired (99 !== 1). Green: exits 1. The existing final-path tests still pass.
3. Format ownership scripts/size-report-package-notices.mjs imports nothing and owns renderLicenseNotices and readLicenseNotices. The gate calls the reader and keeps the package-path and attribute-somebody policy. The private parser is deleted. The base step in size.yml copies scripts/size-report-package*.mjs. the package policy and its notices format run from the standalone size-report copy and base measurement uses the measured revision package asset policy: red → green.
Thread r4460264638 (format ownership) Settled by item 3; the reader keeps header-block-only reading. Same tests as item 3 (reply).
Thread r4460823396 (multiline metadata) The renderer collapses whitespace in the license and author fields. multiline license and author metadata render as one attribution line: red (line split) → green (reply).

Apex Sentinel opened four threads at 50cc9f9f76, and Cubic opened three that repeat them. I answered all seven in their threads and resolved them without a push. Each one is either the plugin's earlier behavior, kept byte-identical, or outside the gate's structural scope: jpeg-js table, @agent-device/proxy attribution, duplicate-release dedupe, placeholder license text, first-of-several license files (1 of 798 installed packages ships two, and it is not bundled). No device run is needed; these are build and package changes only.

@thymikee

thymikee commented Nov 4, 2026

Copy link
Copy Markdown
Member Author

At 50cc9f9, the code looks good, and the three earlier findings are fixed. The build now lists every bundled module itself, so a dependency the license plugin skips can no longer get past the gate. Early output diagnostics are bounded while main is still running. The notices format has one owner that both the renderer and the package gate use, with attribution whitespace normalized and only consumed exports kept.

All 36 checks pass, including the build, package and script routes this touches. The review threads are already resolved, and no conflicts are known.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Nov 4, 2026
@thymikee
thymikee merged commit 26a7106 into main Nov 4, 2026
36 checks passed
@thymikee
thymikee deleted the fix/third-party-license-notices branch November 4, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants