Skip to content

fix(cli): restore shared Skill status and runner CI checks - #11933

Merged
yiliang114 merged 1 commit into
QwenLM:mainfrom
qqqys:fix/shared-skill-ci
Sep 15, 2026
Merged

yiliang114 merged 1 commit into
QwenLM:mainfrom
qqqys:fix/shared-skill-ci

Conversation

@qqqys

@qqqys qqqys commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Restores extension Skill defaults and workspace overrides in the daemon's fallback catalog after Skill names gained extension prefixes. Inactive entries now use the same qualified identity as the live runtime. Also makes the runner-scheduling test fixture explicitly CommonJS so it works when Node defaults to ESM.

Why it's needed

The shared baseline produces 13 workspace Skill test failures, including disabled Skills incorrectly reported as enabled. The runner helper also fails two tests under an ESM default. These failures affect #11922, #11924 and #11927 independently of their Goal changes.

Reviewer Test Plan

How to verify

  • Confirm manifest defaults and workspace overrides remain effective for qualified Skill names, including two extensions with the same authored name and names containing a colon.
  • Confirm only a qualified enable entry grants access, legacy short-name restrictions still block, and inactive extensions remain disabled.
  • Run the runner-scheduling tests with ordinary Node defaults and with NODE_OPTIONS=--experimental-default-type=module; both should pass.

Evidence (Before & After)

Baseline reproduction: 13/39 workspace Skill tests failed; the runner helper failed 2/6 tests under an ESM default. After the fix: 42/42 workspace Skill tests, 8/8 status mapping tests, 30/30 settings tests, and 6/6 runner tests in each module mode pass. Build, bundle, repository typecheck, targeted ESLint and formatting checks pass. No visual UI change.

Tested on

OS Status
macOS ✅ Node.js 22.17.0
Windows ⚠️ Not tested locally
Linux ⚠️ Awaiting hosted CI

Risk & Scope

Linked Issues

Related: #11922, #11924, #11927.

中文说明

改动

修复 Skill 名称增加扩展前缀后,daemon fallback 目录未正确读取扩展默认值和工作区覆盖配置的问题。未激活条目现在使用与运行时一致的完整名称。同时明确 runner 调度测试替身使用 CommonJS,使其在 Node 默认 ESM 模式下也能运行。

原因

公共基线存在 13 个工作区 Skill 测试失败,其中包含禁用 Skill 被错误报告为启用的实际行为偏差。Runner 辅助测试在默认 ESM 模式下另有 2 个失败。这些问题影响 #11922#11924#11927,与它们的 Goal 改动相互独立。

验证方法

  • 检查完整 Skill 名称仍正确遵循 manifest 默认值和工作区覆盖配置,包括不同扩展下同名、原名包含冒号的情况。
  • 检查只有完整名称的启用配置授予访问权限,旧短名限制仍生效,未激活扩展保持禁用。
  • 分别在普通 Node 模式和 NODE_OPTIONS=--experimental-default-type=module 下运行 runner 调度测试,均应通过。

验证证据

修复前:工作区 Skill 测试 13/39 失败,runner 测试在默认 ESM 模式下 2/6 失败。修复后:工作区 Skill 42/42、状态映射 8/8、设置 30/30 通过;runner 测试两种模式均为 6/6。Build、bundle、全仓 typecheck、相关 ESLint 和格式检查通过。没有视觉 UI 改动。

测试环境

  • macOS:已验证,Node.js 22.17.0。
  • Windows:未本地验证。
  • Linux:等待远端 CI。

风险与范围

关联:#11922#11924#11927

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed, not theoretical — and I confirmed it by reading the code rather than taking the description's word for it. Core's SkillManager now builds extension rows with name: qualifySkillName(extension.name, skill.name) and carries authoredName alongside (packages/core/src/skills/skill-manager.ts). The daemon fallback still keys its extensionSkillStates map from the manifest's authored spelling but then looks it up with skill.name, which is now the qualified one. Every lookup misses, enabled comes back undefined, and enabled !== false is true — so manifest defaults and workspace overrides are silently dropped and a disabled Skill reports as enabled. That's a fail-open in a permission-adjacent path, not a cosmetic drift. The base test asserting name: 'active-skill' while core emits active:active-skill also means main is currently red here, which is the strongest possible reproduction.

Direction: aligned. This restores parity between two producers that mapSkillConfigToStatus exists to keep in shape — the daemon fallback and the ACP child had drifted apart, and the child was the correct one. CHANGELOG has a direct reference: the reference product moved the same way, namespacing synced skills as anthropic-skills:<name> and separately fixing /reload-skills reporting a count that "disagreed with the slash menu". Qualifying by owner and keeping listing surfaces consistent is the established direction.

Size: not applicable — no core paths. packages/cli/src/serve/** isn't in the protected set and the change stays inside one package. Breakdown: 6 production lines (workspace-skills-status.ts), 120 test lines, 0 generated/schema.

Approach: the scope feels right, and it's the fix I'd have written. It reuses authoredSkillName / qualifySkillName from core instead of hand-rolling string surgery, the inactive-row construction is a verbatim structural match of what acpAgent.ts already does, and it correctly leaves enabledNames.has(skill.name...) alone — grants match the registry identity only, so a legacy bare entry opens nothing. The new it.each pins exactly the rule that's easy to get wrong: an authored-spelling enabled entry does not grant, a qualified one grants only for the owning extension, and a restriction under the authored spelling still blocks. That's the colon edge (audit:detail inside firstfirst:audit:detail) covered too.

Two things worth a look before this goes further, neither a blocker:

  • The .github/scripts/review-runner-schedule.test.mjs change is a separate concern from the Skill fix — the title bundles them, but pinning a CJS fixture has nothing to do with Skill naming. Would you consider splitting it out? Also, its justification rests on NODE_OPTIONS=--experimental-default-type=module, a flag nothing in this repo sets and one I'd expect engines.node >= 22 to reject outright. I couldn't check — node execution is blocked in this triage environment — so I'm flagging it as a question rather than a claim. The change itself is inert and harmless either way.
  • Once extension rows are qualified, collectCachedSkills dedupes on skill.name, so a project Skill and an extension Skill sharing an authored name no longer collide and both now surface. The renamed test documents that honestly, but it is a real shift in cross-level precedence for same-name skills. It comes from core's naming change, not from this PR — just making sure that consequence is deliberate and not something the fallback should still be shadowing.

Risk: no elevated risk signals — none of the changed paths match the revert-correlated set.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题: 是已观测到的 bug,不是理论性加固——而且我是通过读代码确认的,没有只采信 PR 描述。Core 的 SkillManager 现在用 name: qualifySkillName(extension.name, skill.name) 构造扩展条目,并额外携带 authoredNamepackages/core/src/skills/skill-manager.ts)。而 daemon fallback 仍然用 manifest 的原始名(authored spelling)作为 extensionSkillStates 的 key,却用已经变成完整名的 skill.name 去查。每次查询都落空,enabled 返回 undefined,而 enabled !== falsetrue——于是 manifest 默认值和工作区覆盖被静默丢弃,被禁用的 Skill 反而报告为启用。这是权限相关路径上的 fail-open,不是单纯的外观漂移。base 测试断言 name: 'active-skill' 而 core 实际产出 active:active-skill,也意味着 main 上这个用例当前就是红的——这是最有力的复现证据。

方向: 对齐。这个改动恢复的是两个 producer 之间的一致性,而 mapSkillConfigToStatus 存在的意义正是保证两者形状不漂移——daemon fallback 和 ACP child 已经分叉,而 child 是正确的那一方。CHANGELOG 有直接参照:参考产品走的是同一条路,把同步来的 skill 命名为 anthropic-skills:<name>,另外也修过 /reload-skills 报告的数量"与 slash 菜单不一致"的问题。按 owner 加前缀、并让各个列举入口保持一致,是既定方向。

规模: 不适用——未触及核心路径。packages/cli/src/serve/** 不在受保护集合内,改动也只涉及单个 package。明细:生产代码 6 行workspace-skills-status.ts),测试 120 行,生成/schema 0 行。

方案: 范围合理,也正是我会写的修法。它复用了 core 的 authoredSkillName / qualifySkillName,而不是自己手搓字符串处理;未激活条目的构造与 acpAgent.ts 现有写法在结构上完全一致;并且正确地没有改动 enabledNames.has(skill.name...)——授权(grant)只匹配完整注册名,因此旧的裸名条目不会放开任何权限。新增的 it.each 恰好钉住了最容易写错的规则:authored 拼写的 enabled 条目不授权,完整名条目只对所属扩展授权,而 authored 拼写下的限制依然生效。冒号边界(first 下的 audit:detailfirst:audit:detail)也覆盖到了。

有两点在继续深入前值得看一下,都不是阻塞项:

  • .github/scripts/review-runner-schedule.test.mjs 的改动与 Skill 修复是两件独立的事——标题把它们并在了一起,但把一个 CJS 测试替身固定为 CJS 和 Skill 命名毫无关系。是否考虑拆分出去?另外,它的依据是 NODE_OPTIONS=--experimental-default-type=module,而本仓库没有任何地方设置这个 flag,且按 engines.node >= 22 我预计该 flag 根本不被接受。我无法验证——本次 triage 环境禁止执行 node——所以这是作为疑问提出,不是断言。无论如何这个改动本身是惰性的、无害的。
  • 扩展条目加前缀之后,collectCachedSkillsskill.name 去重,因此同名的 project Skill 与 extension Skill 不再冲突,两者都会出现。重命名后的测试如实记录了这一点,但这确实是同名 skill 跨层级优先级的实质变化。它来自 core 的命名改动,而非本 PR——只是希望确认这个后果是有意为之,而不是 fallback 本应继续做遮蔽。

风险: 无升级风险信号——改动路径均未命中与 revert 相关的集合。

进入代码审查 🔍

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 6fffac454541ea4649b54d7c205ba9e2322fe166 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

Built the PR base vs this PR head 6fffac4, drove a fixed endpoint set against each, and diffed the JSON responses. Only fields that changed are shown.

No response changes against the PR base across 12 scenario(s).

Qwen Code · serve A/B

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

Before reading the diff I wrote down what I'd do from the title and the "Why it's needed" section alone: the fallback's extensionSkillStates is built from manifest authored names but queried with the now-qualified skill.name, so the fix is to query it with authoredSkillName(skill); and the inactive-extension rows are built from manifest skills without qualifying, so they need name: qualifySkillName(...) plus authoredName to match what the child producer emits. That is what this PR does, essentially line for line, so I have no simpler alternative to propose.

No critical blockers and no AGENTS.md violations. Specifics I checked rather than assumed:

  • The reuse ladder is satisfied. It imports authoredSkillName / qualifySkillName from core instead of adding parallel string handling. Both already exist in packages/core/src/skills/types.ts and are the same helpers Config.isSkillEnabled uses, so the fallback now resolves ownership exactly the way the authoritative runtime path does.
  • The barrel import is fine. This file is already listed in eslint.legacy-core-barrel-imports.mjs, so extending its existing root import adds no new allowlist entry — which is what the convention actually forbids.
  • Not changing enabledNames.has(skill.name...) is correct, not an oversight. Core documents grants as registry-identity-only while restrictions match both spellings. Leaving that line alone is what makes a legacy bare enabled entry open nothing, and the new it.each case 1 pins exactly that.
  • authoredName: skill.name on the inactive rows is load-bearing, not decoration. lookupSkillDisablement walks skillRestrictionNames, which returns both spellings only when authoredName is present and differs. Without it, a pre-existing disabled: ['pdf'] would stop biting once the row is qualified — a rename silently delivering a capability grant. Case 3 of the new test covers it.
  • The types line up. SkillConfig.authoredName is an existing optional field, so the property added to that object literal type-checks without a cast.
  • Nothing else in the tree still asserts the old identity. I checked the neighbouring suites rather than trusting the PR's file list: workspace-skills-mapping.test.ts already asserts qualified names (acme:pdf), and routes/workspace-skills.test.ts seeds a stubbed { name: 'runtime', level: 'extension' } that never passes through listSkills(). So the fallback provider really was the last producer lagging behind core's naming change, and the diff is complete.

Two non-blocking observations, named rather than gated on:

The inactive-row construction is now duplicated verbatim between acpAgent.ts and this file — same name / authoredName / level / extensionName / extensionDisplayName shape. mapSkillConfigToStatus was extracted precisely so the two listings "can never drift in shape", but the row identity construction stayed duplicated, and that duplication is what allowed this drift to happen. A small shared builder for the inactive-extension row would turn the next naming change into a one-line edit instead of a two-site hunt. Worth a follow-up, not worth blocking this fix on.

Separately, and echoing Stage 1: the renamed test now expects three rows where it expected two, because collectCachedSkills dedupes on skill.name and a qualified extension name no longer collides with a bare project name. The PR records that honestly in the test name, but the behaviour — a project Skill and an extension Skill sharing an authored name both surfacing, where the old test asserted project precedence — originates in core's naming change, not here. Flagging so it's a decision rather than an accident.

CI test evidence

This is the PR's own CI read through the API. I built and executed nothing from this branch — the review is static, so every number below is GitHub's, not mine.

Thirty-five check runs on the reviewed commit, zero failures and zero cancellations. Everything that has settled is green: Integration Tests (no-AK, No Sandbox), Desktop Shell on ubuntu and windows, Real daemon E2E, TUI parity snapshots, OpenTUI no-flicker gate, and the full Java matrix. Test (macos-latest), Test (windows-latest), Integration Tests (CLI, No Sandbox), tmux-testing and verify are skipped by classification, not failing.

The honest gap: the three checks that actually gate this change are still runningTest (ubuntu-latest, Node 22.x) (the suite that would prove the 13 baseline failures are fixed), Lint & Static (ubuntu-latest, Node 22.x), and Serve A/B (ubuntu-latest, Node 22.x). I'm not polling or guessing their outcome, and there is no failing log to quote because nothing has failed.

So the PR's "42/42 workspace Skill tests, 8/8 status mapping, 30/30 settings, 6/6 runner tests in each module mode" figures are the author's claim, not independently re-run, and its own "Tested on" table lists Linux as awaiting hosted CI. What I can attest to from reading the code is the mechanism: the lookup miss is real, enabled !== false does fail open on it, and the fix addresses it at the right layer using core's own helpers. What a static pass cannot attest to is that the updated tests genuinely fail without the two-line production change.

Check Conclusion
Test (ubuntu-latest, Node 22.x) in_progress
Lint & Static (ubuntu-latest, Node 22.x) in_progress
Serve A/B (ubuntu-latest, Node 22.x) in_progress
Integration Tests (no-AK, No Sandbox) completed / success
Desktop Shell (ubuntu-22.04) completed / success
Desktop Shell (windows-2022) completed / success
Real daemon E2E / Java 11 completed / success
TUI parity snapshots (ink vs opentui) completed / success
OpenTUI no-flicker gate completed / success
ubuntu-latest / Java 11, 17, 21 completed / success
macos-latest / Java 21 completed / success
windows-latest / Java 21 completed / success
Test (macos-latest, Node 22.x) completed / skipped
Test (windows-latest, Node 22.x) completed / skipped
Integration Tests (CLI, No Sandbox) completed / skipped
tmux-testing completed / skipped
verify completed / skipped

Sandboxed verification would settle the part static review cannot: @qwen-code /verify — that the three new grant-scoping cases and the qualified-name assertions actually fail with authoredSkillName / qualifySkillName reverted, which is not observable from the diff, since a suite that passes identically without the production change is green and proves nothing. @qwen-code /tmux would separately cover the user-visible half: that toggling Enable on an inactive extension's Skill in the Web Shell now persists the qualified name — the toggle writes skill.name verbatim through SkillsManagerPage.tsx, so this is the row whose identity the fix changes — and that the entry can therefore still grant once the extension activates. The author has write access, so both lanes are directly available and neither needs a sponsored run.

中文说明

代码审查

在读 diff 之前,我先只根据标题和"为什么需要"写下了自己的修法:fallback 的 extensionSkillStates 是用 manifest 的原始名(authored name)建的表,却用已经加过前缀的 skill.name 去查,所以应该改用 authoredSkillName(skill) 查询;未激活扩展的条目是从 manifest 的 skill 直接构造的、没有加前缀,因此需要补上 name: qualifySkillName(...)authoredName,才能与 child producer 的产出一致。本 PR 基本就是逐行这么做的,所以我没有更简单的替代方案可提。

没有阻塞性问题,也没有违反 AGENTS.md。以下是我实际核对过、而非想当然的点:

  • 复用优先做到了。 它从 core 引入 authoredSkillName / qualifySkillName,而不是另写一套字符串处理。这两个 helper 本来就存在于 packages/core/src/skills/types.ts,也正是 Config.isSkillEnabled 使用的同一套,因此 fallback 现在的归属解析方式与权威运行时路径完全一致。
  • barrel import 没问题。 该文件已经在 eslint.legacy-core-barrel-imports.mjs 白名单里,所以扩展它现有的根导入并没有新增白名单条目——而约定真正禁止的正是新增条目。
  • 不改动 enabledNames.has(skill.name...) 是正确的,不是遗漏。 Core 明确规定:授权(grant)只匹配完整注册名,而限制(restriction)两种拼写都匹配。保留这一行,才使得旧的裸名 enabled 条目不会放开任何权限,新增 it.each 的第 1 个用例正是钉住这一点。
  • 未激活条目上的 authoredName: skill.name 是承重的,不是装饰。 lookupSkillDisablement 会遍历 skillRestrictionNames,而后者只有在 authoredName 存在且与完整名不同时才返回两种拼写。少了它,一旦条目被加上前缀,原有的 disabled: ['pdf'] 就不再生效——等于一次重命名静默地送出了权限授予。新增测试的第 3 个用例覆盖了这种情况。
  • 类型是对得上的。 SkillConfig.authoredName 是既有的可选字段,因此对象字面量里新增该属性无需类型断言即可通过检查。
  • 代码树里没有其他地方还在断言旧的身份标识。 我核对了相邻套件,而不是直接采信 PR 的文件清单:workspace-skills-mapping.test.ts 已经在断言完整名(acme:pdf),而 routes/workspace-skills.test.ts 播种的是一个桩数据 { name: 'runtime', level: 'extension' },根本不经过 listSkills()。所以 fallback provider 确实是 core 命名改动之后最后一个没跟上的 producer,这份 diff 是完整的。

两点非阻塞的观察,只点名、不作为门槛:

未激活条目的构造现在在 acpAgent.ts 和本文件之间逐字重复——name / authoredName / level / extensionName / extensionDisplayName 的形状完全一样。当初抽出 mapSkillConfigToStatus 正是为了让两份列表"形状上永不漂移",但条目身份的构造仍然是重复的,而这种重复恰恰是本次漂移得以发生的原因。抽出一个共享的"未激活扩展条目"构造器,能让下一次命名变更从"两处排查"变成"一行修改"。值得作为后续跟进,但不值得因此阻塞本次修复。

另外,与 Stage 1 呼应:重命名后的测试现在期望三行而非两行,因为 collectCachedSkillsskill.name 去重,而加了前缀的扩展名不再与裸的 project 名冲突。PR 在测试名里如实记录了这一点,但这个行为——同名的 project Skill 与 extension Skill 同时出现,而旧测试断言的是 project 优先——源自 core 的命名改动,不在本 PR。提出来是希望它成为一个明确决定,而不是意外结果。

CI 测试证据

以上是通过 API 读取的本 PR 自身 CI。我没有构建或执行该分支的任何代码——本次审查是静态的,因此下面每个数字都来自 GitHub,不是我自己跑出来的。

被审 commit 上共 35 个 check run,零失败、零取消。已经出结果的全部为绿:Integration Tests (no-AK, No Sandbox)、ubuntu 与 windows 上的 Desktop ShellReal daemon E2ETUI parity snapshotsOpenTUI no-flicker gate,以及完整的 Java 矩阵。Test (macos-latest)Test (windows-latest)Integration Tests (CLI, No Sandbox)tmux-testingverify 是被分类跳过,不是失败。

需要如实说明的缺口:真正能判定本次改动的三个 check 仍在运行——Test (ubuntu-latest, Node 22.x)(也就是能证明 13 个基线失败已被修复的那个套件)、Lint & Static (ubuntu-latest, Node 22.x)Serve A/B (ubuntu-latest, Node 22.x)。我没有轮询,也没有猜测它们的结果;没有失败日志可引用,因为目前没有任何失败。

因此 PR 中"工作区 Skill 42/42、状态映射 8/8、设置 30/30、runner 两种模式各 6/6"这些数字属于作者自述,未经我独立复跑,其"测试环境"表格本身也把 Linux 标为等待远端 CI。我能通过读代码确认的是机制:查询落空确实存在,enabled !== false 在此确实会 fail-open,修复也落在正确的层次并复用了 core 自己的 helper。静态审查无法确认的是:更新后的测试在缺少那两行生产代码改动时是否真的会失败。

沙箱验证可以补上静态审查覆盖不到的部分:@qwen-code /verify —— 用以确认三个新增的授权范围用例以及完整名断言,在把 authoredSkillName / qualifySkillName 回退后确实会失败;这一点从 diff 看不出来,因为如果去掉生产代码改动后套件依然全绿,那它什么也没证明。@qwen-code /tmux 则可以单独覆盖用户可见的那一半:在 Web Shell 中对未激活扩展的 Skill 打开 Enable 开关后,现在持久化的是完整名——该开关通过 SkillsManagerPage.tsx 原样写入 skill.name,所以这正是本次修复改变了身份标识的那一行——因而扩展激活后这条配置仍然能够授权。作者具有 write 权限,两条通道都可直接触发,无需 sponsored run。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 6fffac454541ea4649b54d7c205ba9e2322fe166 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — the production fix is six lines that I could verify against two independent authorities in the tree, and it closes a fail-open on a permission-adjacent path; the reservations are all non-blocking and mostly about what's bundled alongside it.

Stepping back: this is a real regression fix, not a hypothesis. Core's SkillManager started qualifying extension Skill names, and the daemon's pre-child fallback was the one producer that never caught up. Because the stale lookup returns undefined and the code reads it as enabled !== false, the failure direction is fail-open — a Skill that a manifest default or a workspace override had turned off was reported as enabled. That's the direction where a silent bug costs the most, and it's why I'm comfortable calling this a fix rather than code hygiene.

What gives me the 4 is that I didn't have to take the PR's word for the mechanism. The fix mirrors Config.isSkillEnabled for the ownership lookup and acpAgent.ts's producer for the inactive rows — two authoritative references already in the tree that independently say what correct looks like. It reuses core's helpers instead of re-deriving the spellings. And the new tests pin the two rules that are genuinely easy to get backwards: an authored-spelling grant opens nothing, while an authored-spelling restriction still blocks. That asymmetry is deliberate in core and this PR reproduces it faithfully.

I also checked the diff is complete rather than trusting the file list — the neighbouring suites already assert qualified names or stub around listSkills(), so nothing else was left asserting the old identity.

Where I land on the three reservations, none of which I'd gate on:

The bundled runner-fixture change is scope creep and I'd still prefer it split out. It's also the one claim in this PR I could not check at all: its justification is NODE_OPTIONS=--experimental-default-type=module, a flag nothing in this repo sets, on a repo pinned to engines.node >= 22. node execution is blocked in this environment, so I'm flagging it as an open question rather than a defect. The change itself is inert — it writes a package.json into an mkdtemp dir that nothing else reads — so the cost of being wrong is near zero either way. Per the gate's own rule, a scope-hygiene concern isn't grounds to defer, so I'm noting it and moving on.

The duplicated inactive-row construction between the two producers is the thing I'd actually want addressed, because it's the root cause of this class of drift, not a symptom. But extracting it is a separate change with its own blast radius, and doing it inside a six-line regression fix would make this harder to review and revert. Follow-up issue territory.

And the cross-level precedence shift — a project Skill and an extension Skill sharing an authored name now both surface — is a genuine behaviour change that this PR documents but does not cause. It arrives with core's naming change. If that was unintended, it needs a decision in core, not a patch here.

On the pattern question, since this author has ten PRs open and six landed today: I evaluated this one on its own merits and it holds up — small, focused, verified against the code, and it unblocks CI for three of the author's other PRs rather than advancing a feature. The volume is worth a maintainer's awareness in the aggregate, but it isn't a reason to discount a PR that fixes a red baseline.

Approval is deferred until CI lands green on 6fffac454541ea4649b54d7c205ba9e2322fe166. The three checks that actually gate this change — Test, Lint & Static, and Serve A/B on ubuntu/Node 22 — were still running when I wrote this, and approving now would attest to a result that doesn't exist yet. The finalize workflow posts the commit-pinned approval once every check on that SHA completes green, and withholds it if anything lands red or the head moves.

中文说明

Confidence: 4/5 —— 生产代码修复只有六行,而且我能拿代码树里两处相互独立的权威实现来验证它;它堵住的是一个权限相关路径上的 fail-open。保留意见全部是非阻塞的,且大多与"顺带打包进来的东西"有关。

退一步看整体:这是一次真实的回归修复,不是假设。Core 的 SkillManager 开始给扩展 Skill 名加前缀,而 daemon 在 child 尚未就绪时的 fallback 是唯一没跟上的 producer。由于过期的查询返回 undefined,而代码把它读作 enabled !== false,失败方向是 fail-open——被 manifest 默认值或工作区覆盖关掉的 Skill,反而被报告为启用。这正是静默 bug 代价最大的方向,也是我愿意把它称为"修复"而非"代码整洁性"的原因。

给我 4 分的依据是:机制部分我不必采信 PR 的说法。这个修复在归属查询上对齐了 Config.isSkillEnabled,在未激活条目上对齐了 acpAgent.ts 的 producer——代码树里已有两处权威参照,各自独立地说明了"正确"长什么样。它复用了 core 的 helper,而不是自己重新推导拼写。新增测试也钉住了两条真正容易写反的规则:authored 拼写的授权不放开任何权限,而 authored 拼写的限制依然生效。这种不对称在 core 中是刻意为之,本 PR 忠实地复现了它。

我还核对了 diff 的完整性,而不是直接采信文件清单——相邻套件要么已经在断言完整名,要么用桩数据绕过了 listSkills(),所以没有别处还在断言旧的身份标识。

关于三条保留意见,我的结论是:都不足以作为门槛。

打包进来的 runner 测试替身改动属于范围蔓延,我仍然更希望它被拆出去。它也是本 PR 中我唯一完全无法核实的说法:其依据是 NODE_OPTIONS=--experimental-default-type=module,而本仓库没有任何地方设置这个 flag,仓库又锁定在 engines.node >= 22。本环境禁止执行 node,所以我是把它作为待解疑问提出,而不是判定为缺陷。这个改动本身是惰性的——它只往一个 mkdtemp 目录里写了一个没有别处会读的 package.json——因此无论判断对错,代价都接近于零。按门禁自身的规则,范围整洁性问题不构成推迟的理由,所以我在此记录后继续推进。

两个 producer 之间重复的未激活条目构造,才是我真正希望被处理的一点,因为它是这类漂移的根因,而不是症状。但把它抽出来是一次独立的改动,有自己的影响面;塞进一个六行的回归修复里,只会让本次改动更难审查、更难回滚。这属于后续 issue 的范畴。

至于跨层级优先级的变化——同名的 project Skill 与 extension Skill 现在都会出现——是一个真实的行为变化,本 PR 记录了它但并未造成它。它随 core 的命名改动一起到来。如果这不是有意为之,需要在 core 里做决定,而不是在这里打补丁。

关于"是否属于某种模式"这个问题,鉴于该作者有 10 个 PR 处于开启状态、今天就有 6 个:我是就这一个 PR 本身的价值来评估的,而它站得住——小、聚焦、经代码核实,并且它是为作者另外三个 PR 打通 CI,而不是推进某个功能。整体上的数量值得维护者留意,但这不足以让我折价看待一个修复红色基线的 PR。

批准将推迟至 CI 在 6fffac454541ea4649b54d7c205ba9e2322fe166 上全绿之后。 真正能判定本次改动的三个 check——ubuntu/Node 22 上的 TestLint & StaticServe A/B——在我撰写时仍在运行,现在批准等于为一个尚不存在的结果背书。待该 SHA 上所有 check 全绿后,finalize 流程会发布与 commit 绑定的批准;若有任何一项变红或 head 发生移动,则不会批准。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 6fffac454541ea4649b54d7c205ba9e2322fe166 · re-run with @qwen-code /triage

@yiliang114
yiliang114 added this pull request to the merge queue Sep 15, 2026
Merged via the queue into QwenLM:main with commit faded7f Sep 15, 2026
73 of 74 checks passed
yiliang114 added a commit that referenced this pull request Sep 15, 2026
main's #11933 (faded7f) landed the identical workspace skill identity
fix: packages/cli/src/serve/workspace-skills-status.ts merges to the same
blob on both sides, and main's workspace-skills-status.test.ts is a superset
of this branch's version (this branch deleted main's `scopes grants to the
owning extension` cases). Both files are therefore taken from origin/main,
which leaves the two corrected .size-baseline integers as the whole diff.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
yiliang114 added a commit that referenced this pull request Sep 15, 2026
Picks up main's review-runner-schedule helper-test fix (#11933), which writes a commonjs package.json into the fake-gh temp dir so the Lint lane's helper tests stop failing with 'require is not defined in ES module scope' when TMPDIR resolves inside the repo.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Patrol-Run: qwen-pr-conflict/jmu2luc9n6d
yiliang114 added a commit that referenced this pull request Sep 15, 2026
Picks up #11933, which aligned packages/cli/src/serve/workspace-skills-status.test.ts with the qualified Skill identities the provider already emits. This branch never touched that file; its PR base predates the fix, so the Test lane ran the stale expectations and reported 13 failures.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Patrol-Run: qwen-pr-conflict/jmu2luc9n6d
yiliang114 added a commit that referenced this pull request Sep 15, 2026
Picks up #11933, which aligned packages/cli/src/serve/workspace-skills-status.test.ts with the qualified Skill identities the provider emits. This branch never touched that file, so the 13 Test-lane failures were the stale expectations from its older base, not a regression here.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

Patrol-Run: qwen-pr-conflict/jmu2luc9n6d
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.

4 participants