Skip to content

chore(vscode-ide-companion): sync third-party notices and guard against future drift - #7161

Merged
wenshao merged 5 commits into
QwenLM:mainfrom
wenshao:chore/sync-vscode-notices
Jul 18, 2026
Merged

chore(vscode-ide-companion): sync third-party notices and guard against future drift#7161
wenshao merged 5 commits into
QwenLM:mainfrom
wenshao:chore/sync-vscode-notices

Conversation

@wenshao

@wenshao wenshao commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Regenerates the VS Code companion's third-party notices so they once again list every dependency shipped with the extension, aligns a single lockfile entry with the version pinned in the CLI package, adds a CI guard that fails whenever the committed notices drift out of sync with the dependency tree, and fixes a latent cross-platform bug in the notices generator so its output is identical on macOS and Linux. It also removes a stale script entry that pointed at a file that does not exist.

Why it's needed

The notices file is a generated artifact built by walking the dependency tree and collecting each package's license. It had fallen out of sync: a dependency added to the core package late last year — together with its own transitive licenses — was never reflected in the generated output. Because the generator reaches the core package's dependencies through workspace links, the shipped notices were missing a set of third-party license texts, which is a gap from a licensing-compliance point of view.

The deeper problem was that nothing enforced the file staying in sync, so it had silently rotted across several dependency changes. Beyond regenerating it, this adds a CI check — mirroring the guard already used for the settings schema — that regenerates the notices and fails the build if the committed file differs, printing the exact command to fix it. Drift now becomes self-correcting on every pull request instead of relying on contributors to remember.

The new guard immediately earned its keep: it surfaced a latent portability bug. The generator matched license files against a fixed-case candidate list, which resolves a License file on macOS's case-insensitive default filesystem but misses it on Linux's case-sensitive one, so the generated file differed by platform (and the guard failed on Linux). The lookup now scans the directory and compares names case-insensitively, making the output deterministic on every platform. The lockfile change is a small companion fix: one entry still recorded a version range that the package manifest had since pinned to an exact version.

Reviewer Test Plan

How to verify

From a clean checkout: run a clean install, then regenerate the notices. The working tree should remain clean afterward, confirming the generated file matches the dependency tree (generation is idempotent) — which is exactly what the new CI check asserts. The repository's lockfile validation should also still pass.

To see the guard fail as intended, change a dependency without regenerating and run the two new CI steps locally: the "Check VS Code companion notices are up-to-date" step should exit non-zero with instructions.

Expected: no diff after regeneration; lockfile check reports "passed"; the new guard passes on this branch (verified on the Linux CI job).

Evidence (Before & After)

N/A (non-user-visible: a generated license file, a one-line lockfile entry, a generator portability fix, and a CI check).

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux

Environment

Local install + regeneration on macOS; the new guard and the generator fix verified on the Linux CI job.

Risk & Scope

  • Main risk or tradeoff: large diff in a generated file, but it is mechanically produced and idempotent, and the new CI guard keeps it correct going forward.
  • Not validated / out of scope: a few packages still emit "License text not found" because no license file exists at their resolved install path — simple-git and its sub-packages and @xterm/headless ship none, while undici-types and tr46 resolve to nested copies that omit it. This is pre-existing and left untouched; the generator change here only concerns filename matching.
  • Breaking changes / migration notes: none. Contributors who change dependencies must now regenerate the notices; the failing CI step prints the exact command.

Linked Issues

None.

中文说明

本 PR 的内容

重新生成 VS Code 扩展的第三方声明,使其重新列出扩展所附带的每一个依赖;将锁文件中一个依赖条目对齐到 CLI 包中已固定的版本;新增一道 CI 校验,当已提交的声明与依赖树脱节时让构建失败;并修复声明生成器中一个跨平台的潜在 bug,使其在 macOS 与 Linux 上输出一致。同时移除一条指向不存在文件的失效脚本条目。

为什么需要

声明文件是一个生成产物,通过遍历依赖树并收集每个包的许可证生成。它已经与依赖树脱节:去年年底加入到 core 包的一个依赖——连同它自己的传递许可证——一直没有反映到生成结果中。由于生成器会通过 workspace 链接访问 core 包的依赖,因此发布的声明文件缺失了一批第三方许可证文本,从许可证合规的角度看是一个缺口。

更深层的问题是:没有任何机制强制该文件保持同步,因此它在多次依赖变更后悄悄腐烂。除了重新生成,本 PR 还新增了一道 CI 校验——参照 settings schema 已有的做法——它会重新生成声明,并在已提交文件与之不一致时让构建失败,同时打印精确的修复命令。这样漂移在每个 PR 上都会自动暴露并修正,而不再依赖贡献者记得手动重新生成。

这道新校验立刻体现了价值:它暴露了一个潜在的可移植性 bug。生成器用固定大小写的候选列表去匹配许可证文件,这在 macOS 默认大小写不敏感的文件系统上能命中 License 文件,但在 Linux 大小写敏感的文件系统上会漏掉,导致生成的文件因平台而异(守卫在 Linux 上失败)。现在改为扫描目录并按大小写不敏感比较文件名,使输出在每个平台上都确定一致。锁文件改动是一个配套的小修复:某个条目仍记录着一个版本范围,而包清单早已将其固定为精确版本。

审阅测试计划

如何验证

在干净的检出中:执行一次干净安装,然后重新生成声明。此后工作区应保持干净,说明生成文件与依赖树一致(生成是幂等的)——这正是新 CI 校验所断言的。仓库的锁文件校验也应继续通过。

若想验证守卫确实会失败:在不重新生成的情况下改动一个依赖,本地运行新增的两个 CI 步骤,"Check VS Code companion notices are up-to-date" 步骤应以非零退出并给出指引。

预期:重新生成后无 diff;锁文件检查报告 "passed";新守卫在本分支上通过(已在 Linux CI 任务上验证)。

证据(前后对比)

N/A(非用户可见:生成的许可证文件、一行锁文件条目、一处生成器可移植性修复,以及一道 CI 校验)。

测试环境

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux

环境

macOS 本地安装 + 重新生成;新守卫与生成器修复已在 Linux CI 任务上验证。

风险与范围

  • 主要风险/权衡:生成文件的 diff 很大,但它是机械生成的且幂等,新的 CI 守卫会保证其后续始终正确。
  • 未验证/超出范围:少数包仍会输出 "License text not found",因为其解析到的安装路径下没有许可证文件——simple-git 及其子包、@xterm/headless 根本不带许可证文件,undici-types 与 tr46 解析到的嵌套副本则省略了它。这是既有问题、本次未处理;此处的生成器改动只涉及文件名匹配。
  • 破坏性变更/迁移说明:无。改动依赖的贡献者现在必须重新生成声明,失败的 CI 步骤会打印精确命令。

关联 Issue

无。

…lock.json

NOTICES.txt had drifted out of sync with the dependency tree. It was
missing the third-party licenses pulled in transitively through
@qwen-code/core (notably @anthropic-ai/sdk and its dependencies), which
the notices generator reaches by following workspace links. Regenerated
from a clean install so it once again covers every resolved dependency.

Also sync the lockfile's ink spec to 7.0.3 to match
packages/cli/package.json, which pinned it without a caret but was never
reflected in package-lock.json.
@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR! (re-run)

Template looks good ✓

Problem: The NOTICES.txt had genuinely drifted out of sync — @anthropic-ai/sdk@0.36.3 (added to packages/core late last year) and its transitive dependencies were missing from the generated notices. This is a verifiable compliance gap, not a theoretical concern: running the current generator on main produces a diff of +13,218/−1,408 lines against the committed file, confirming the rot.

Direction: Aligned. Keeping generated artifacts in sync via CI guards is an established pattern in this repo (the settings schema guard already works the same way). The ink lockfile pin (^7.0.37.0.3) aligns the lockfile with what packages/cli/package.json already pins — minor correctness fix.

Size: Not applicable — no core modules touched. Changes are scoped to packages/vscode-ide-companion/, package-lock.json, and .github/workflows/ci.yml.

Approach: Scope is tight and well-justified. Each change maps directly to a stated goal: regenerate (fix compliance), add CI guard (prevent future drift), fix case-insensitive lookup (fix cross-platform bug), pin lockfile (correctness). No scope creep.

Moving on to code review. 🔍

中文说明

感谢贡献!(re-run)

模板完整 ✓

问题:NOTICES.txt 确实已经脱节——去年底加入 packages/core@anthropic-ai/sdk@0.36.3 及其传递依赖都没有出现在生成的声明中。这是可验证的合规缺口,不是理论问题:在当前 main 上运行生成器,与已提交文件相比差异达 +13,218/−1,408 行,确认了腐化。

方向:对齐。通过 CI 守卫保持生成产物同步是本仓库已有模式(settings schema 守卫已用同样方式运作)。ink 锁文件固定(^7.0.37.0.3)使锁文件与 packages/cli/package.json 已有的固定版本对齐——小的正确性修复。

规模:不适用——未触及核心模块。变更范围限于 packages/vscode-ide-companion/package-lock.json.github/workflows/ci.yml

方案:范围紧凑且理由充分。每项改动都直接对应一个声明的目标:重新生成(修复合规)、新增 CI 守卫(防止未来漂移)、修复大小写不敏感查找(修复跨平台 bug)、固定锁文件(正确性)。无范围蔓延。

进入代码审查 🔍

Qwen Code · qwen3.7-max

Reviewed at 467846d0fc28f233c8d13400a1f25b579b42f492 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Code Review (re-run)

Independent proposal: To fix the out-of-sync NOTICES.txt, I would (1) run the existing generator to regenerate, (2) add a CI guard following the settings-schema pattern, and (3) fix the case-sensitivity bug by reading the directory and comparing lowercased names. I'd probably skip writing tests for the generator since it's a one-off script — but having tests is better.

Comparison with PR: The PR matches my proposal exactly on all three points, and adds unit tests for findLicenseFile — a sensible addition I would have skipped. The extraction of findLicenseFile as an exported function is clean and testable. The main() guard using process.argv[1] comparison is the standard ESM entry-point detection pattern.

Code quality: Clean. The findLicenseFile function correctly handles the case-insensitive matching edge case — it builds a Map of lowercased directory entries and looks up candidates in priority order. The // Only run when executed directly guard is the right pattern. Test coverage is focused: mixed-case regression, preference ordering, hint honoring, and the empty-directory case.

No correctness issues, security concerns, or regressions found.

Real-Scenario Testing

Generator verification (on main, without PR):

$ node packages/vscode-ide-companion/scripts/generate-notices.js
Warning: Could not find license file for undici-types
Warning: Could not find license file for form-data
Warning: Could not find license file for delayed-stream
Warning: Could not find license file for combined-stream
Warning: Could not find license file for tr46
...
NOTICES.txt generated at .../NOTICES.txt
Total dependencies: 276

$ git diff --stat packages/vscode-ide-companion/NOTICES.txt
 packages/vscode-ide-companion/NOTICES.txt | 14626 +++++++++++++++++++++++++---
 1 file changed, 13218 insertions(+), 1408 deletions(-)

This confirms the compliance gap is real: the committed NOTICES.txt on main is missing 13,218 lines of license text. The "Could not find license file" warnings for form-data, delayed-stream, combined-stream etc. are exactly the case-sensitivity bug the PR fixes — these packages have a License file (mixed case) which the current case-sensitive candidate list misses on Linux.

findLicenseFile logic verification (inline node test):

Test 1 (mixed-case): PASS /tmp/license-test-3K6N3P/License
Test 2 (preference): PASS /tmp/license-test-NJeWZj/LICENSE
Test 3 (none):       PASS undefined
Test 4 (hint):       PASS /tmp/license-test-SF8Rla/COPYING

All four scenarios pass: mixed-case file is resolved (the key regression), LICENSE is preferred over LICENSE.md, undefined is returned for empty directories, and the package.json licenseFile hint is honored.

CI evidence: The ubuntu-latest Test job passed (31m49s) — which includes the new "Check VS Code companion notices are up-to-date" step that runs the PR's fixed generator and asserts no diff. The guard passed on Linux CI, confirming the case-insensitive fix produces correct, deterministic output on case-sensitive filesystems.

中文说明

代码审查(re-run)

独立方案: 修复脱节的 NOTICES.txt,我会 (1) 运行现有生成器重新生成,(2) 参照 settings schema 模式加 CI 守卫,(3) 通过读取目录并比较小写名称来修复大小写敏感 bug。我可能不会给生成器写测试——但有测试更好。

与 PR 对比: PR 在三点上与我的方案完全一致,并为 findLicenseFile 加了单元测试。将 findLicenseFile 抽取为导出函数干净且可测试。main() 守卫使用 process.argv[1] 比较是标准的 ESM 入口检测模式。

代码质量: 干净。findLicenseFile 正确处理了大小写不敏感匹配的边界情况——构建小写目录条目的 Map,按优先级顺序查找候选项。无正确性问题、安全隐患或回归。

真实场景测试

生成器验证(在 main 上,无 PR): 确认了合规缺口真实存在——main 上的 NOTICES.txt 缺少 13,218 行许可证文本。"Could not find license file" 警告正是 PR 修复的大小写敏感 bug——这些包有 License 文件(混合大小写),当前的大小写敏感候选列表在 Linux 上会漏掉。

findLicenseFile 逻辑验证: 4 个场景全部通过:混合大小写文件被解析(关键回归)、LICENSE 优先于 LICENSE.md、空目录返回 undefinedpackage.jsonlicenseFile 提示被遵循。

CI 证据: ubuntu-latest Test 任务通过(31m49s),包含了新的 "Check VS Code companion notices are up-to-date" 步骤,该步骤运行 PR 修复后的生成器并断言无 diff。守卫在 Linux CI 上通过,确认大小写不敏感修复在大小写敏感的文件系统上产生了正确、确定性的输出。

Qwen Code · qwen3.7-max

Reviewed at 467846d0fc28f233c8d13400a1f25b579b42f492 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Confidence: 5/5 — clean, focused PR that fixes a real compliance gap and prevents it from silently recurring.

This is the kind of maintenance PR that doesn't get attention but matters. The NOTICES.txt had rotted by over 13,000 lines — license texts for @anthropic-ai/sdk and its transitive dependencies were simply missing from the shipped extension. The PR doesn't just regenerate the file; it adds the CI guard that should have existed from the start, so drift becomes self-correcting on every future PR instead of relying on contributors remembering.

The case-insensitive findLicenseFile fix is a clean, well-tested solution to the cross-platform bug the new guard surfaced. The PR's scope is tight — every change maps to a stated goal, nothing more.

The generator ran on main and produced a 13,218-line diff against the committed file, confirming the problem is real and not theoretical. The unit tests for findLicenseFile all pass. CI (ubuntu-latest) passed with the new guard included.

Approving.

中文说明

信心度:5/5 — 干净、聚焦的 PR,修复了真实的合规缺口并防止其再次悄悄发生。

这类维护 PR 往往不受关注但很重要。NOTICES.txt 已经腐烂了超过 13,000 行——@anthropic-ai/sdk 及其传递依赖的许可证文本在发布的扩展中完全缺失。PR 不只是重新生成文件;它新增了 CI 守卫,使漂移在每个未来的 PR 上自动修正,而不再依赖贡献者记得手动更新。

大小写不敏感的 findLicenseFile 修复是对新守卫暴露的跨平台 bug 的干净、有测试的解决方案。PR 范围紧凑——每项改动都对应一个声明的目标,没有多余内容。

main 上运行生成器产生了与已提交文件 13,218 行的 diff,确认问题是真实的而非理论性的。findLicenseFile 的单元测试全部通过。CI(ubuntu-latest)在包含新守卫的情况下通过。

批准。

Qwen Code · qwen3.7-max

Reviewed at 467846d0fc28f233c8d13400a1f25b579b42f492 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

Add a CI check that regenerates the companion notices and fails if the
committed file is stale, mirroring the existing settings-schema guard.
This stops the notices from silently rotting whenever a dependency
changes without a regeneration.

Also drop the validate:notices script entry, which pointed at a
non-existent file and was never functional.
@wenshao wenshao changed the title chore(vscode-ide-companion): regenerate third-party notices and sync lockfile chore(vscode-ide-companion): sync third-party notices and guard against future drift Jul 18, 2026

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Not reviewed: chunk 7, chunk 8, chunk 9, chunk 11, chunk 12, chunk 13, chunk 14, chunk 15, chunk 16, chunk 17, chunk 18, chunk 19, chunk 20, chunk 21, chunk 22, chunk 23, chunk 24, chunk 25, chunk 26, chunk 27, chunk 28, chunk 29, chunk 31, chunk 32, chunk 33, chunk 34, chunk 35, chunk 36, chunk 37, chunk 38, chunk 39, chunk 40, chunk 41, chunk 42, chunk 43, chunk 44, chunk 45, chunk 46, chunk 47, chunk 48, chunk 49, chunk 50, chunk 51, chunk 52, chunk 53, chunk 54 — no agent reported covering these; nobody read them. Not reviewed: chunks 2-9, 11-29, 31-54, 56-57 (NOTICES.txt generated license text — 3 sampled chunks confirmed standard boilerplate). Not reviewed: Agent 0: Issue fidelity & root-cause ownership — its prompt was built, but no agent was launched with it. Not reviewed: chunk 2 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 3 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 4 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 5 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 6 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 7 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 8 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 9 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 11 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 12 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 13 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 14 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 15 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 16 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 17 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 18 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 19 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 20 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 21 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 22 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 23 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 24 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 25 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 26 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 27 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 28 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 29 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 31 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 32 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 33 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 34 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 35 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 36 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 37 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 38 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 39 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 40 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 41 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 42 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 43 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 44 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 45 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 46 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 47 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 48 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 49 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 50 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 51 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 52 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 53 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 54 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 56 — its prompt was built, but no agent was launched with it. Not reviewed: chunk 57 — its prompt was built, but no agent was launched with it. Not reviewed: Test coverage matrix (whole-diff) — its prompt was built, but no agent was launched with it. Not reviewed: Agent 1b: Removed-behavior audit — its prompt was built, but no agent was launched with it. Not reviewed: Agent 1c: Cross-file tracer — its prompt was built, but no agent was launched with it. Not reviewed: reverse audit — no auditor ran (Step 5 builds its prompt with agent-prompt --role reverse-audit; none was recorded, so the pass that looks for what Step 3 missed was skipped).

— qwen3.7-max via Qwen Code /review

The license-file lookup matched a fixed-case candidate list via fs.stat,
which resolves a `License` file on macOS's case-insensitive filesystem
but misses it on Linux's case-sensitive one. That made the generated
NOTICES.txt platform-dependent and broke the new CI up-to-date guard.
Scan the directory and compare lowercased names so the output is
identical on every platform.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Suggestions are inline. Not reviewed: chunk 3, chunk 4, chunk 5, chunk 6, chunk 7, chunk 8, chunk 9, chunk 10, chunk 11, chunk 12, chunk 13, chunk 14, chunk 15, chunk 16, chunk 17, chunk 18, chunk 19, chunk 20, chunk 21, chunk 22, chunk 23, chunk 24, chunk 25, chunk 26, chunk 27, chunk 28, chunk 29, chunk 31, chunk 32, chunk 33, chunk 34, chunk 35, chunk 36, chunk 37, chunk 38, chunk 39, chunk 40, chunk 41, chunk 42, chunk 43, chunk 44, chunk 45, chunk 46, chunk 47, chunk 48, chunk 49, chunk 51, chunk 52, chunk 53, chunk 55, chunk 56, chunk 57 — no agent reported covering these; nobody read them. Not reviewed: chunks 3-29, 31-49, 51-57 — generated NOTICES.txt license text (3 sampled chunks confirmed standard boilerplate). Not reviewed: Agent 0: Issue fidelity & root-cause ownership — its prompt was built, but no agent was launched with it. Not reviewed: chunk 3 — no prompt was built for it (agent-prompt --chunk 3 never ran). Not reviewed: chunk 4 — no prompt was built for it (agent-prompt --chunk 4 never ran). Not reviewed: chunk 5 — no prompt was built for it (agent-prompt --chunk 5 never ran). Not reviewed: chunk 6 — no prompt was built for it (agent-prompt --chunk 6 never ran). Not reviewed: chunk 7 — no prompt was built for it (agent-prompt --chunk 7 never ran). Not reviewed: chunk 8 — no prompt was built for it (agent-prompt --chunk 8 never ran). Not reviewed: chunk 9 — no prompt was built for it (agent-prompt --chunk 9 never ran). Not reviewed: chunk 10 — no prompt was built for it (agent-prompt --chunk 10 never ran). Not reviewed: chunk 11 — no prompt was built for it (agent-prompt --chunk 11 never ran). Not reviewed: chunk 12 — no prompt was built for it (agent-prompt --chunk 12 never ran). Not reviewed: chunk 13 — no prompt was built for it (agent-prompt --chunk 13 never ran). Not reviewed: chunk 14 — no prompt was built for it (agent-prompt --chunk 14 never ran). Not reviewed: chunk 15 — no prompt was built for it (agent-prompt --chunk 15 never ran). Not reviewed: chunk 16 — no prompt was built for it (agent-prompt --chunk 16 never ran). Not reviewed: chunk 17 — no prompt was built for it (agent-prompt --chunk 17 never ran). Not reviewed: chunk 18 — no prompt was built for it (agent-prompt --chunk 18 never ran). Not reviewed: chunk 19 — no prompt was built for it (agent-prompt --chunk 19 never ran). Not reviewed: chunk 20 — no prompt was built for it (agent-prompt --chunk 20 never ran). Not reviewed: chunk 21 — no prompt was built for it (agent-prompt --chunk 21 never ran). Not reviewed: chunk 22 — no prompt was built for it (agent-prompt --chunk 22 never ran). Not reviewed: chunk 23 — no prompt was built for it (agent-prompt --chunk 23 never ran). Not reviewed: chunk 24 — no prompt was built for it (agent-prompt --chunk 24 never ran). Not reviewed: chunk 25 — no prompt was built for it (agent-prompt --chunk 25 never ran). Not reviewed: chunk 26 — no prompt was built for it (agent-prompt --chunk 26 never ran). Not reviewed: chunk 27 — no prompt was built for it (agent-prompt --chunk 27 never ran). Not reviewed: chunk 28 — no prompt was built for it (agent-prompt --chunk 28 never ran). Not reviewed: chunk 29 — no prompt was built for it (agent-prompt --chunk 29 never ran). Not reviewed: chunk 31 — no prompt was built for it (agent-prompt --chunk 31 never ran). Not reviewed: chunk 32 — no prompt was built for it (agent-prompt --chunk 32 never ran). Not reviewed: chunk 33 — no prompt was built for it (agent-prompt --chunk 33 never ran). Not reviewed: chunk 34 — no prompt was built for it (agent-prompt --chunk 34 never ran). Not reviewed: chunk 35 — no prompt was built for it (agent-prompt --chunk 35 never ran). Not reviewed: chunk 36 — no prompt was built for it (agent-prompt --chunk 36 never ran). Not reviewed: chunk 37 — no prompt was built for it (agent-prompt --chunk 37 never ran). Not reviewed: chunk 38 — no prompt was built for it (agent-prompt --chunk 38 never ran). Not reviewed: chunk 39 — no prompt was built for it (agent-prompt --chunk 39 never ran). Not reviewed: chunk 40 — no prompt was built for it (agent-prompt --chunk 40 never ran). Not reviewed: chunk 41 — no prompt was built for it (agent-prompt --chunk 41 never ran). Not reviewed: chunk 42 — no prompt was built for it (agent-prompt --chunk 42 never ran). Not reviewed: chunk 43 — no prompt was built for it (agent-prompt --chunk 43 never ran). Not reviewed: chunk 44 — no prompt was built for it (agent-prompt --chunk 44 never ran). Not reviewed: chunk 45 — no prompt was built for it (agent-prompt --chunk 45 never ran). Not reviewed: chunk 46 — no prompt was built for it (agent-prompt --chunk 46 never ran). Not reviewed: chunk 47 — no prompt was built for it (agent-prompt --chunk 47 never ran). Not reviewed: chunk 48 — no prompt was built for it (agent-prompt --chunk 48 never ran). Not reviewed: chunk 49 — no prompt was built for it (agent-prompt --chunk 49 never ran). Not reviewed: chunk 51 — no prompt was built for it (agent-prompt --chunk 51 never ran). Not reviewed: chunk 52 — no prompt was built for it (agent-prompt --chunk 52 never ran). Not reviewed: chunk 53 — no prompt was built for it (agent-prompt --chunk 53 never ran). Not reviewed: chunk 54 — no prompt was built for it (agent-prompt --chunk 54 never ran). Not reviewed: chunk 55 — no prompt was built for it (agent-prompt --chunk 55 never ran). Not reviewed: chunk 56 — no prompt was built for it (agent-prompt --chunk 56 never ran). Not reviewed: chunk 57 — no prompt was built for it (agent-prompt --chunk 57 never ran). Not reviewed: Test coverage matrix (whole-diff) — its prompt was built, but no agent was launched with it. Not reviewed: Agent 1b: Removed-behavior audit — no prompt was built for it (agent-prompt --role 1b never ran). Not reviewed: Agent 1c: Cross-file tracer — its prompt was built, but no agent was launched with it. Not reviewed: reverse audit — its prompt was built, but no agent was launched with it that opened its brief, so the reverse-audit pass did not run. Not reviewed: verification — the review posts findings, but no verifier ran (Step 4 builds its prompt with agent-prompt --role verify; none was recorded, so the findings were not verified).

— qwen3.7-max via Qwen Code /review

Comment on lines 57 to 59
'LICENSE-MIT.txt',
'license.md',
'license',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The licenseFileCandidates list includes LICENSE-MIT.txt but not LICENSE-MIT (without the .txt extension). Packages like ignore@5.3.2 ship their license as LICENSE-MIT, so the generator writes "License text not found." despite the file existing on disk. — Concrete cost: the NOTICES.txt ships an incorrect "License text not found." for a package whose MIT license is present, and the new CI check enforces this incorrect output on every future PR.

Suggested change
'LICENSE-MIT.txt',
'license.md',
'license',
'LICENSE-MIT.txt',
'LICENSE-MIT',
'license.md',
'license',

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — applied in 5fe47f5. Added LICENSE-MIT (resolves ignore) plus the British-spelled LICENCE.md, which was the same class of miss for bignumber.js. The "License text not found" count drops from 8 to 6.

The remaining 6 are packages with no license file at their resolved install path — a separate, pre-existing concern from filename matching:

  • simple-git, @simple-git/args-pathspec, @simple-git/argv-parser, @xterm/headless ship no license file at all;
  • undici-types and tr46 resolve to nested copies (@anthropic-ai/sdk/node_modules/..., node-fetch/node_modules/...) that omit it — the hoisted copies carry the license, but the generator reads the resolved path.

Left out of scope here to keep this change focused.

…tices

Extend the license-file candidate list with `LICENSE-MIT` and the
British-spelled `LICENCE.md` so packages that ship those (e.g. ignore,
bignumber.js) no longer emit a spurious "License text not found." in the
generated notices. Regenerates NOTICES.txt to include their licenses.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Suggestions are inline. Not reviewed: chunk 3, chunk 4, chunk 5, chunk 6, chunk 7, chunk 8, chunk 9, chunk 10, chunk 11, chunk 12, chunk 13, chunk 14, chunk 15, chunk 16, chunk 17, chunk 18, chunk 19, chunk 20, chunk 21, chunk 22, chunk 23, chunk 24, chunk 25, chunk 26, chunk 27, chunk 28, chunk 29, chunk 31, chunk 32, chunk 33, chunk 34, chunk 35, chunk 36, chunk 37, chunk 38, chunk 39, chunk 40, chunk 41, chunk 42, chunk 43, chunk 44, chunk 45, chunk 46, chunk 47, chunk 48, chunk 49, chunk 50, chunk 51, chunk 52, chunk 56 — no agent reported covering these; nobody read them. Not reviewed: Agent 0: Issue fidelity & root-cause ownership — its prompt was built, but no agent was launched with it. Not reviewed: chunk 2 — no prompt was built for it (agent-prompt --chunk 2 never ran). Not reviewed: chunk 3 — no prompt was built for it (agent-prompt --chunk 3 never ran). Not reviewed: chunk 4 — no prompt was built for it (agent-prompt --chunk 4 never ran). Not reviewed: chunk 5 — no prompt was built for it (agent-prompt --chunk 5 never ran). Not reviewed: chunk 6 — no prompt was built for it (agent-prompt --chunk 6 never ran). Not reviewed: chunk 7 — no prompt was built for it (agent-prompt --chunk 7 never ran). Not reviewed: chunk 8 — no prompt was built for it (agent-prompt --chunk 8 never ran). Not reviewed: chunk 9 — no prompt was built for it (agent-prompt --chunk 9 never ran). Not reviewed: chunk 10 — no prompt was built for it (agent-prompt --chunk 10 never ran). Not reviewed: chunk 11 — no prompt was built for it (agent-prompt --chunk 11 never ran). Not reviewed: chunk 12 — no prompt was built for it (agent-prompt --chunk 12 never ran). Not reviewed: chunk 13 — no prompt was built for it (agent-prompt --chunk 13 never ran). Not reviewed: chunk 14 — no prompt was built for it (agent-prompt --chunk 14 never ran). Not reviewed: chunk 15 — no prompt was built for it (agent-prompt --chunk 15 never ran). Not reviewed: chunk 16 — no prompt was built for it (agent-prompt --chunk 16 never ran). Not reviewed: chunk 17 — no prompt was built for it (agent-prompt --chunk 17 never ran). Not reviewed: chunk 18 — no prompt was built for it (agent-prompt --chunk 18 never ran). Not reviewed: chunk 19 — no prompt was built for it (agent-prompt --chunk 19 never ran). Not reviewed: chunk 20 — no prompt was built for it (agent-prompt --chunk 20 never ran). Not reviewed: chunk 21 — no prompt was built for it (agent-prompt --chunk 21 never ran). Not reviewed: chunk 22 — no prompt was built for it (agent-prompt --chunk 22 never ran). Not reviewed: chunk 23 — no prompt was built for it (agent-prompt --chunk 23 never ran). Not reviewed: chunk 24 — no prompt was built for it (agent-prompt --chunk 24 never ran). Not reviewed: chunk 25 — no prompt was built for it (agent-prompt --chunk 25 never ran). Not reviewed: chunk 26 — no prompt was built for it (agent-prompt --chunk 26 never ran). Not reviewed: chunk 27 — no prompt was built for it (agent-prompt --chunk 27 never ran). Not reviewed: chunk 28 — no prompt was built for it (agent-prompt --chunk 28 never ran). Not reviewed: chunk 29 — no prompt was built for it (agent-prompt --chunk 29 never ran). Not reviewed: chunk 31 — no prompt was built for it (agent-prompt --chunk 31 never ran). Not reviewed: chunk 32 — no prompt was built for it (agent-prompt --chunk 32 never ran). Not reviewed: chunk 33 — no prompt was built for it (agent-prompt --chunk 33 never ran). Not reviewed: chunk 34 — no prompt was built for it (agent-prompt --chunk 34 never ran). Not reviewed: chunk 35 — no prompt was built for it (agent-prompt --chunk 35 never ran). Not reviewed: chunk 36 — no prompt was built for it (agent-prompt --chunk 36 never ran). Not reviewed: chunk 37 — no prompt was built for it (agent-prompt --chunk 37 never ran). Not reviewed: chunk 38 — no prompt was built for it (agent-prompt --chunk 38 never ran). Not reviewed: chunk 39 — no prompt was built for it (agent-prompt --chunk 39 never ran). Not reviewed: chunk 40 — no prompt was built for it (agent-prompt --chunk 40 never ran). Not reviewed: chunk 41 — no prompt was built for it (agent-prompt --chunk 41 never ran). Not reviewed: chunk 42 — no prompt was built for it (agent-prompt --chunk 42 never ran). Not reviewed: chunk 43 — no prompt was built for it (agent-prompt --chunk 43 never ran). Not reviewed: chunk 44 — no prompt was built for it (agent-prompt --chunk 44 never ran). Not reviewed: chunk 45 — no prompt was built for it (agent-prompt --chunk 45 never ran). Not reviewed: chunk 46 — no prompt was built for it (agent-prompt --chunk 46 never ran). Not reviewed: chunk 47 — no prompt was built for it (agent-prompt --chunk 47 never ran). Not reviewed: chunk 48 — no prompt was built for it (agent-prompt --chunk 48 never ran). Not reviewed: chunk 49 — no prompt was built for it (agent-prompt --chunk 49 never ran). Not reviewed: chunk 50 — no prompt was built for it (agent-prompt --chunk 50 never ran). Not reviewed: chunk 51 — no prompt was built for it (agent-prompt --chunk 51 never ran). Not reviewed: chunk 52 — no prompt was built for it (agent-prompt --chunk 52 never ran). Not reviewed: chunk 53 — no prompt was built for it (agent-prompt --chunk 53 never ran). Not reviewed: chunk 55 — no prompt was built for it (agent-prompt --chunk 55 never ran). Not reviewed: chunk 56 — no prompt was built for it (agent-prompt --chunk 56 never ran). Not reviewed: chunk 57 — no prompt was built for it (agent-prompt --chunk 57 never ran). Not reviewed: Test coverage matrix (whole-diff) — its prompt was built, but no agent was launched with it. Not reviewed: Agent 1b: Removed-behavior audit — its prompt was built, but no agent was launched with it. Not reviewed: Agent 1c: Cross-file tracer — its prompt was built, but no agent was launched with it. Not reviewed: reverse audit — its prompt was built, but no agent was launched with it that opened its brief, so the reverse-audit pass did not run. Not reviewed: verification — its prompt was built, but no agent was launched with it that opened its brief, so the posted findings were not verified.

— qwen3.7-max via Qwen Code /review

Comment on lines +52 to +64
const licenseFileCandidates = [
depPackageJson.licenseFile,
'LICENSE',
'LICENSE.md',
'LICENSE.txt',
'LICENSE-MIT.txt',
'LICENSE-MIT',
'LICENCE.md',
'license.md',
'license',
].filter(Boolean);
]
.filter(Boolean)
.map((candidate) => candidate.toLowerCase());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The case-insensitive license file matching — the core bug fix of this PR — has no unit test. A future refactor back to fs.stat with fixed-case candidates would silently reintroduce the macOS/Linux divergence: on Linux CI, packages whose license file is named License (not LICENSE) would fall through to "License text not found", and the CI drift guard would not catch it because both the baseline and the check run on the same Linux CI.

Suggested change
const licenseFileCandidates = [
depPackageJson.licenseFile,
'LICENSE',
'LICENSE.md',
'LICENSE.txt',
'LICENSE-MIT.txt',
'LICENSE-MIT',
'LICENCE.md',
'license.md',
'license',
].filter(Boolean);
]
.filter(Boolean)
.map((candidate) => candidate.toLowerCase());
const licenseFileCandidates = [
depPackageJson.licenseFile,
'LICENSE',
'LICENSE.md',
'LICENSE.txt',
'LICENSE-MIT.txt',
'LICENSE-MIT',
'LICENCE.md',
'license.md',
'license',
]
.filter(Boolean)
.map((candidate) => candidate.toLowerCase());

A unit test creating a temp directory with a mixed-case license file (e.g., License) and asserting the lookup resolves it would prevent this regression.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done in 467846d. Extracted the lookup into an exported findLicenseFile(packageDir, licenseFileHint) and guarded main() so the script can be imported in tests without regenerating NOTICES.txt.

Added scripts/generate-notices.test.js (wired into the vitest include) with a regression test that creates a temp dir containing a mixed-case License file and asserts the lookup resolves it — plus variant priority, the licenseFile hint, and the not-found case. Because the test asserts resolution directly rather than via the CI regenerate-and-compare loop, a revert to case-sensitive fs.stat matching would fail it on any OS, closing the gap that the Linux-only drift guard cannot cover.

…okup

The Linux-only CI drift guard regenerates and compares on the same
case-sensitive filesystem, so a future revert to case-sensitive license
matching would produce consistent-but-wrong output and still pass. Extract
the lookup into an exported findLicenseFile() and add a regression test that
resolves a mixed-case `License` file regardless of platform.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Not reviewed: chunk 2, chunk 3, chunk 4, chunk 5, chunk 6, chunk 7, chunk 8, chunk 9, chunk 10, chunk 11, chunk 12, chunk 13, chunk 14, chunk 15, chunk 16, chunk 17, chunk 18, chunk 19, chunk 20, chunk 21, chunk 22, chunk 23, chunk 24, chunk 25, chunk 26, chunk 27, chunk 28, chunk 29, chunk 30, chunk 31, chunk 32, chunk 33, chunk 34, chunk 35, chunk 36, chunk 37, chunk 38, chunk 39, chunk 40, chunk 41, chunk 42, chunk 43, chunk 44, chunk 45, chunk 46, chunk 47, chunk 48, chunk 49, chunk 50, chunk 51, chunk 52, chunk 53, chunk 54, chunk 55, chunk 56, chunk 57 — no agent reported covering these; nobody read them. Not reviewed: chunks 2-57 — generated NOTICES.txt license text (sampled chunks at lines 5000 and 10000 confirmed standard MIT/Apache/BSD/ISC boilerplate). Not reviewed: Agent 0: Issue fidelity & root-cause ownership — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 1 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 2 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 3 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 4 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 5 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 6 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 7 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 8 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 9 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 10 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 11 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 12 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 13 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 14 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 15 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 16 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 17 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 18 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 19 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 20 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 21 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 22 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 23 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 24 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 25 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 26 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 27 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 28 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 29 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 30 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 31 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 32 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 33 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 34 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 35 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 36 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 37 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 38 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 39 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 40 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 41 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 42 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 43 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 44 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 45 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 46 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 47 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 48 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 49 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 50 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 51 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 52 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 53 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 54 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 55 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 56 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 57 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 58 — its prompt was built, but no agent on record was launched with it. Not reviewed: chunk 59 — its prompt was built, but no agent on record was launched with it. Not reviewed: Test coverage matrix (whole-diff) — its prompt was built, but no agent on record was launched with it. Not reviewed: Agent 1b: Removed-behavior audit — its prompt was built, but no agent on record was launched with it. Not reviewed: Agent 1c: Cross-file tracer — its prompt was built, but no agent on record was launched with it. Not reviewed: Agent 7: Build & test verification — its prompt was built, but no agent on record was launched with it. Not reviewed: reverse audit — no auditor was launched with a prompt this skill builds — the pass that hunts what the rest of the review missed ran, if at all, without the method its brief carries.

— qwen3.7-max via Qwen Code /review

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. The case-insensitive license lookup fix is the right call, and the CI drift guard mirrors the existing settings-schema pattern nicely. Generated NOTICES.txt diff is mechanical and idempotent — no concerns there.

@wenshao

wenshao commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants