7 Commits
Author SHA1 Message Date
易良 351b5fc51a ci(lint): stop new cli imports from the core package root (#11798)
Importing a value from the @qwen-code/qwen-code-core root evaluates all
of core in every test whose import graph reaches the importing file,
which is where most cli test time goes (#10908). The existing rule stops
at packages/core/src, so cli kept adding root imports: 446 production
files at #10957's merge, 465 eight days later.

Enable @typescript-eslint/no-restricted-imports for cli production files
with type-only imports allowed, reusing the barrel specifier set from
no-core-root-barrel-import. The 372 files that value-import the root
today are listed in eslint.legacy-core-barrel-imports.mjs; the list only
shrinks. The new file is registered wherever eslint.legacy-filenames.mjs
is: the lint-gate freshness list, the autofix sensitive-file classifier,
and the hygiene fix agent's deny list.

ESLint and Prettier (--experimental-cli) were run on the changed files,
and the new block was exercised through ESLint's API against the real
eslint.config.js. The scripts/tests vitest suites were not run locally.

Refs #10908
2026-09-14 04:49:03 +00:00
703678136a perf(cli): import core modules directly instead of the package root (#10957)
* perf(cli): let tests resolve core modules individually, and stop two files importing the whole package

Importing from the core package root pulls in its entire export graph — a bit
over six hundred modules — however little of it a file actually uses. In a
release run the cli workspace spent 2223s collecting modules against 1372s
running tests, and a file that imports the package root costs about 11.5s
before its first assertion where one importing a single module costs about 2s.

cli's tsconfig already maps a wildcard subpath onto core's sources, so esbuild
resolves per-module imports when it bundles. Vitest does not read tsconfig
paths, and the alias list that stands in for them named only four subpaths, so
those imports did not resolve under test at all. This adds the wildcard there.

Expressing the alias list as an ordered array is what allows a pattern entry.
The package root has to become an exact match in the process: as a string it
would also match everything beneath it and rewrite each subpath into a path
under index.ts.

Two files move to per-module imports as a first check that the mapping holds
end to end. Both were picked because nothing that depends on them replaces the
core package with a mock factory — where a test does that, the mock stops
intercepting once the code under test imports the module directly, so those
call sites need their mocks moved in the same change and are left alone here.

* fix(cli): restore the named core subpaths the previous commit dropped

The previous commit was assembled from a working copy that predated main by
several weeks, so it silently reverted this file to that older state. Four
named core subpaths added since — envVarResolver, noFollowOpen,
subSessionConstants and toolWriteOrigin — disappeared with it, and the new
wildcard then claimed those specifiers and pointed them at files that do not
exist. 257 test files failed to load as a result.

All eight named subpaths are restored and kept ahead of the wildcard, with a
comment saying why that order matters and what a contributor adding a ninth
has to do. None of the eight can be derived from its specifier, so none of
them can be folded into the pattern.

The two migrated source files are rebuilt on their current contents for the
same reason; one of them had also been reverted by a line.

* perf(cli): import core modules directly where no test mocks the package

Importing from the core package root evaluates its whole export graph — a bit
over six hundred modules — however little of it a file uses. On the release
lane the cli workspace spends more time collecting modules than running tests,
and on the main lane it now takes 84 minutes on its own, most of it collection.

These 130 files ask for named modules instead. They were chosen by checking,
for every test whose module graph reaches them, whether that test replaces the
core package with a mock: a test that swaps the package wholesale stops
intercepting once the code under test imports a module directly, and a test
that spreads the real package and overrides a few names only matters if one of
those names is what the file imports. Files with either kind of coupling are
left for a later change that moves the mocks at the same time.

Only import statements move; every other line is byte-identical.

* fix(cli): point ProviderModelConfig at the module that declares it, and wrap long imports

Two problems with the previous commit, both found by CI.

The symbol map resolved a re-exported name to the module that re-exports it
rather than the one that declares it, so `ProviderModelConfig` was asked of
`models/types` when it is declared in `providers/types`, and the build failed
to typecheck. A checker now confirms, for every generated specifier, that the
named module really does export that symbol — following its own re-exports —
and it reports one bad pair out of 492.

The formatting pass that was supposed to run over these files had silently
done nothing: invoked from the repository root against paths outside it,
Prettier skips the files and still reports success, so long import statements
went out unwrapped. Rerunning it properly reflows 47 files.

Two files are left with an over-long line Prettier would wrap, because that
line is over-long on the base commit too and the lint gate does not flag it;
reformatting it here would be unrelated noise. Every other line outside an
import statement stays byte-identical.

* perf(cli): import core modules directly in another 114 files

The same mechanical change as the previous commit, over the files a corrected
reading of the test suite showed were always safe to move.

The earlier pass classified a test as replacing the core package if the text
of such a call appeared anywhere in it, including inside a comment. One file
only mentions the pattern in a doc comment explaining why it deliberately
avoids it, and being counted as a blocker there ruled out 217 modules that
nothing actually blocks. Ignoring comments when detecting the call raises the
number of files movable without touching a single test from 141 to 260.

Every generated specifier is checked against the exports its named module
really has, following that module's own re-exports — 865 pairs here, none
wrong. Outside import statements every line is byte-identical.

* test(cli): move three barrel mocks onto the modules they actually stub

Where a test replaces the whole core package with a factory, the code under
test cannot move to per-module imports on its own: the mock would stop
intercepting and the real implementation would load instead, quietly changing
what the test exercises while leaving it green. The mock has to move in the
same commit.

These three are the cases where that is unambiguous — every name the factory
stubs is declared in one module, and the code under test imports exactly those
names. Each pair moves together onto that module.

The pattern generalises: about sixty tests each hold back one or two modules
this way, and roughly ninety more modules are held by several tests at once
and need them changed together. Establishing the shape on the clean cases
first keeps the ambiguous ones honest.

* Revert the migrated modules that any failing suite depends on

Retargeting this PR at main got the unit suite to run for the first time, and
it failed: 16 files, 127 tests, none of them failing on the jsdom PR that
shares the same base. So they are this stack's doing.

Every one is the same shape — a stub the suite installs no longer intercepts
once the code under test names a module instead of the package root. The
static analysis that picked these files models three ways of installing such a
stub and misses at least two more: a spy planted on a namespace import of the
package, and whatever six of the sixteen suites do, which it cannot parse at
all. Sharpening the heuristic further is not the answer; it was already wrong
in a way no amount of local reading would have caught.

So this restores every migrated module that any failing suite reaches, 146 of
them, and keeps the 110 that nothing failing depends on. That is blunt — some
of the 146 are certainly fine — but it is the version that can be shown to
pass, and picking the survivors apart is work for a run that is green to begin
with.

* fix(core): let consumers outside the cli package resolve a core module by path

The integration gate failed to compile against the migrated files:

  error TS2307: Cannot find module '@qwen-code/qwen-code-core/utils/debugLogger.js'
  error TS2307: Cannot find module '@qwen-code/qwen-code-core/utils/editor.js'

Only packages/cli maps these specifiers, through a wildcard in its own
tsconfig. The integration suite lists the eight named subpaths and no
wildcard, and the package's exports map has entries for those same eight plus
the dist and src trees — so anything resolving the normal way, this suite and
any consumer of the published package alike, cannot name a core module.

Both gaps close here: the wildcard is added to the integration suite's path
mappings, and a catch-all maps a bare module path onto the build output. The
catch-all exposes nothing new; `./dist/*` already reaches the same files.

This is the part of the change with consequences beyond the test run. The
shipped CLI is a single bundle and never resolves these specifiers at runtime,
but the package is published, and until now a migrated import was only
resolvable from inside the one workspace that happens to map it.

* fix(cli): repair the mock pairs the revert split, and keep dev on source

Three findings from review, all of them consequences of earlier steps here.

Two mock pairs were left half-migrated. In one, the mock and the code under
test both moved to the module, but the suite's own import of the same two
functions still read the package root — so the suite held the real functions
while the code held the stubs. In the other, the revert restored the code to
the package root and left the mock pointing at a module nothing imports any
more, which stubs nothing at all. The first is completed, the second put back.

The exports catch-all also changed what `npm run dev` runs. Its loader
intercepts the exact package root and nothing else, so subpath imports fall
through — and where they used to fail to resolve, they now quietly reach
compiled output from whenever the tree was last built. The loader now redirects
them to the source tree when the source file is there, leaving the named
subpath exports, whose file names do not mirror their specifiers, to resolve
as before.

* Cover the exports catch-all, and stop it hiding from the architecture rule

Three review suggestions, all about the `./*` entry this branch added to core's
exports and the things that quietly depend on it.

Nothing exercised that entry. In the repo, cli's subpath imports resolve
through tsconfig paths or vitest aliases; in the published package neither
exists and Node resolves them against `exports`. So removing the entry, or
renaming the dist root, would leave every suite green and break `qwen` on its
first core subpath import. A new check runs Node's own resolver against the
built package — deleting the entry turns it red, which is the property that
makes it worth having.

The entry also widened what the utils-layer rule has to police. It resolved
self-references by exact key only, so a deep specifier like
`.../config/storage.js` matched nothing, returned early, and reported nothing —
while resolving perfectly at runtime. It now follows Node's own order: exact
keys first, then the longest literal prefix among patterns. Fixtures cover the
wildcard idiom, exact-key precedence, and a sibling utils import that must
still be allowed.

Finally the integration suite's path mappings. The guidance comment above them
said a bare wildcard falls through to dist, which stopped being true when the
wildcard was added — nodenext substitutes `.js` for `.ts`, and the program
resolves core subpaths through it to source today. Left alone, a maintainer
following that comment would delete the wildcard as a violation and silently
re-route live imports to dist declarations. The comment now describes what the
block does, and says why the six alias-style entries cannot be folded into the
pattern. Two entries that had drifted out of sync with core's exports map,
toolWriteOrigin and envVarResolver, are restored.

* fix(cli): point the session-picker branch mock at the module it imports

Review finding R1-4b. StandaloneSessionPicker now takes getGitBranch from
@qwen-code/qwen-code-core/utils/gitUtils.js, but the colocated suite kept
mocking the package root, so the override no longer intercepted anything the
picker resolves and the factory's importActual still pulled core's whole
index into the suite's module graph. The mock now names the same specifier
the component imports, and a small pin test goes red if the pair splits
again.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtme4yqxhl

* fix(dev): preserve source-backed core entry paths

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts): close core subpath verification gaps

Co-authored-by: Qwen-Coder <[email protected]>

* fix(scripts): gate dep-only core subpath exports in the check

The exports check collected specifiers only from packages/cli/src, but
acp-bridge and sdk-typescript are runtime dependencies of the cli whose
compiled dist keeps core subpath specifiers verbatim. Entries named only
by those packages (./goalWire, ./transcriptRecords, ./subSessionConstants)
were therefore ungated: deleting one kept every gate green while
`node packages/cli` died with ERR_MODULE_NOT_FOUND on the serve/replay
path. Extend the source scan to packages/acp-bridge/src and
packages/sdk-typescript/src so all 95 collected specifiers resolve
through the exports map. Verified: removing ./goalWire or
./transcriptRecords from packages/core/package.json now makes the check
exit 1 (entries restored after the mutation probe).

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtmxfessij

* docs(harness): note terminal-capture loader is manually exercised

No CI test executes the terminal-capture harness loader's subpath
redirect; it runs manually only via `npm run test:terminal-bench`.
Record that at the redirect site and point at
scripts/check-core-subpath-exports.mjs as the CI gate for the real
resolution path, so the `named` map stays consciously in sync with the
named entries in packages/core/package.json.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtmxfessij

* fix(scripts): reject core subpath targets outside the published dist

The exports check validated resolved targets against the repo tree, but
core's exports map also carries "./src/*": "./src/*" while package.json
publishes only dist, vendor and scripts/postinstall.js. A specifier
routed through that entry resolved to a real in-repo packages/core/src
file, passed the bare existsSync and exited 0 even though the published
artifact ships nothing — the installed CLI would die at startup with
ERR_MODULE_NOT_FOUND while the gate stayed green. Require the resolved
target to lie inside packages/core/dist/, where every legitimate
runtime specifier lands.

Add scripts/tests/check-core-subpath-exports.test.js: a fixture-tree
suite (per the scripts/tests convention) that runs a copy of the real
script in a temp workspace. It pins the dep-only scan from a18ffebb8
(goalWire named by no cli source resolves; removing its exports entry
exits 1 — the mutation witness requested in review, made hermetic) and
pins this guard (a ./src/*-routed specifier is reported as not
published; removing the guard turns that test red).

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtn1pqbuir

* fix(scripts): allow published core package.json in subpath gate, cover probe stages

The round-4 dist-containment guard rejected
@qwen-code/qwen-code-core/package.json, which core's exports map
deliberately publishes ("./package.json": "./package.json") and npm ships
regardless of "files". Allow that one target; ./src/* targets stay
rejected (existing test remains red without the guard).

Also extend the fixture suite to the two failure stages it did not
witness: the named-export probe stage (target exists but lacks the probed
export) and the resolve-failure branch (exports map without the ./*
catch-all). Mutant-verified: removing the probe import/export-name check
or the resolve-catch failed++ turns the matching new test red and nothing
else.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtngpuscjl

* fix(terminal-capture): add conversationsRuntimeMarker to harness loader map

The loader's named map listed 8 of the 9 named entries in core's exports
map: conversationsRuntimeMarker (source at utils/conversations-runtime-marker.ts,
a path the specifier does not mirror) was missing. The specifier is
reachable in the harness graph today via shared-env-keys.ts, so resolution
fell through to the exports map — ERR_MODULE_NOT_FOUND mid-capture on a
fresh clone, or a stale dist silently mixed into a source-backed capture
otherwise. Add the entry, matching packages/core/package.json and the cli
vitest alias.

Also add a sync guard (scripts/tests) asserting every named key of core's
exports map has a loader entry pointing at an existing core source file,
so the next omitted entry goes red in CI.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtngpuscjl

* fix(integration-tests): map conversationsRuntimeMarker to core source in tsconfig paths

The ninth named exports entry was missing from the paths block, so
`@qwen-code/qwen-code-core/conversationsRuntimeMarker` (imported by
packages/cli/src/config/shared-env-keys.ts) fell through the wildcard to
a nonexistent substitution and resolved against packages/core/dist — or
failed with TS2307 on an unbuilt tree — the exact stale-dist mode this
block's comment warns about. Add a sync gate asserting every named
exports key whose target stem differs from the key has a paths entry
pointing at the matching source file.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtntktgsk8

* test(scripts): assert loader map values and reverse key-set against core exports

The harness loader sync gate only checked key coverage (exports key ->
loader entry) and file existence, so two drifts passed it green:
a retargeted exports entry left the loader serving the old module (the
named map short-circuits ahead of the stem probe), and a stale
loader-only entry short-circuits captures on a module graph the shipped
package can no longer resolve. Assert each loader entry equals the
exports import target mapped into source space, and every loader entry
has a matching exports key. Mutating either direction now fails the
suite.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtntktgsk8

* test(scripts): make the exact-key-precedence fixture verdict-sensitive

The rule verdicts by directory layer and never checks file existence, so
the goalWire fixture reported one violation whether the exact exports
key or the ./* wildcard resolved it — removing the exact-key branch of
resolveExportTarget left the test green. Switch the fixture to
transcriptRecords, whose exact resolution lands inside utils/ (allowed)
while the wildcard resolution lands outside (violation), so dropping the
exact-key branch now flips the assertion from 0 to 1.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmtntktgsk8

* docs(cli): correct the subpath-alias and mock-wiring comments

packages/cli/vitest.config.ts named the cli tsconfig `paths` block as the
trigger for adding a named core subpath alias, but only three of the nine
entries (noFollowOpen, subSessionConstants, transcriptRecords) appear there;
the source of truth is the named keys of the `exports` map in
packages/core/package.json. The comment now says so, and records that this
map — unlike the skill-review-harness loader's, which
scripts/tests/text-capture-core-loader-sync.test.js checks against core's
exports — has no gate.

StandaloneSessionPicker.test.tsx's wiring test claimed to pin the wrapper's
import, but StandaloneSessionPicker.tsx is not in that suite's module graph:
the tests render SessionPicker, which takes `currentBranch` as a prop, so the
stub's only consumer is the test file's own import. Renamed the test and
corrected both comments to state what it pins and what it does not cover.

Comment and test-name only; no behavior change.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-closeout/jmto4amd1ku

* ci: pin the new core subpath exports step in the lint lane payload

`Check core subpath exports resolve` was added to the lint_and_static job
without being added to the pin that asserts that job's full-gated payload by
name and order, so the guard failed with 19 received against 18 expected.

Co-authored-by: Qwen-Coder <[email protected]>
Patrol-Run: qwen-pr-conflict/jmtosl3ualt

---------

Co-authored-by: Qwen-Coder <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
2026-09-06 11:21:50 +00:00
易良andShaojin Wen 6652fdc9f6 refactor(core): centralize worktree config derivation (#9915)
* refactor(core): centralize worktree config derivation

* refactor(core): centralize agent config derivation (#9918)

* refactor(core): centralize agent config derivation

* refactor(core): centralize scoped config profiles (#9920)

* fix(core): keep approval-mode transitions working on derived teammate and dispatch configs

Give every per-agent config child-local approval state, not just the ones
spawned with an explicit mode: tools bind to the config and teammate mode
switches (Shift+Tab) plus 'Proceed always' confirmations call setApprovalMode
on it, which the derived-Config guard rejects without an approval profile.
Snapshot the base mode when none is requested so no AUTO strip is acquired
and cleanup stays a no-op.

Layer an approval profile over the workflow-orchestrator's derived dispatch
contexts (worktree isolation / workingDir) as agent.ts does, with cleanup in
the outer finally.

* fix(core): anchor plans-directory assertions at the plans-owning base Config

Derived agent profiles rebind targetDir to their own workspace, but the plan
file stays in the owning base's configured plans directory. The containment
assertions in savePlan/loadPlan compared against the derived workspace and
threw FatalConfigError for teammates whose cwd differs from the parent project
root, which savePlanBestEffort swallowed into a debug warning — silently
dropping the plan. Resolve the anchor to the Config that owns the
plans-directory state instead.

* test(core): cover deriveAgentConfig workspace rebinds

Mirror the deriveWorktreeConfig pairing test: assert the public getter
overrides (getCwd/getProjectRoot/getWorkingDir/getTargetDir, plan-file path)
and the paired private targetDir/cwd field writes plus own
workspaceContext/fileDiscoveryService, so a refactor that drops any rebind
fails loudly instead of leaking parent-tree reads into spawned agents.

* test(scripts): add flat-config integration test for no-config-object-create

Mirror no-core-root-barrel-config.test.js: load the real eslint.config.js via
new ESLint({overrideConfigFile}) and lintText virtual packages/core/src/**
paths, so a future edit to the flat-config wiring (plugin key, rule name,
ignores) that silently stops the gate applying to packages/core/src/** turns
this test red instead of degrading the invariant with no signal.

---------

Co-authored-by: Shaojin Wen <[email protected]>
2026-08-27 12:28:08 +00:00
易良 a60cbbc54a refactor(core): make utils/ a leaf layer (#9778)
* refactor(core): make utils/ a leaf layer

Eliminate every runtime (value) upward import from
packages/core/src/utils production modules so utils/ can become a leaf
layer with no runtime dependency on the rest of core.

Two mechanisms, no behavior change:

- Relocate domain-coupled modules out of utils/ into their owning
  module (agents, config, core, memory, services, tools), and move
  generic constants/types that live elsewhere into utils/. All
  `git mv` moves keep history; every import that pointed at a moved
  file is rewritten.

- Extract the remaining value imports as small leaf modules inside
  utils/ (AuthType, isTool, ToolErrorType, DEFAULT_QWEN_MODEL) and
  re-export them from their original owners so cross-package consumers
  are unaffected. doesToolInvocationMatch moves into shell-utils, its
  only production consumer.

Only type-only imports now cross the utils/ boundary. The two deferred
inversions in debugLogger (Storage, getTraceContext) are stateful and
left for a follow-up.

* chore(core): enforce utils/ leaf layer with lint rule

Add architecture/no-core-utils-upward-import, which flags runtime
(value) imports that leave packages/core/src/utils. Type-only imports,
sibling utils imports, and external package specifiers stay allowed;
the two deferred debugLogger inversions (config/storage,
telemetry/trace-context) are carried on an explicit allowlist.

Enable the rule as an error on core sources and cover it with
Linter-based tests.

* fix(core): restore iconv-lite tree-shaking for sync-file-encoding

The utils leaf-layer refactor moved sync-file-encoding from utils/ to services/, but the esbuild tree-shake plugin still matched the old ./utils/ specifier, so its sideEffects:false marker no longer applied and the ACP startup closure regained a static iconv-lite import. Point the onResolve filter at the new ./services/ path.

* fix(ci): catch stale integration imports earlier

* fix(core): close utils boundary review gaps

* fix(core): close self-reference boundary gaps

* ci: re-trigger after self-hosted runner checkout EACCES
2026-08-24 07:43:01 +00:00
易良andQwen-Coder 56db17bd4c refactor(cli): enforce utils leaf-layer dependency direction (#9146) (#9737)
* refactor(cli): enforce utils leaf-layer dependency direction (#9146)

Move domain-coupled modules out of packages/cli/src/utils into the
directories that own them: config/ (dialogScopeUtils, settingsUtils),
i18n/ (languageUtils), ui/ (handleAutoUpdate, standalone-update,
systemInfo, systemInfoFields, update-relaunch, commands, doctorChecks),
nonInteractive/ (nonInteractiveHelpers, chat-recording-failure,
tool-result-boundary-diagnostics, permission-suggestions), serve/
(sandbox), services/housekeeping/ (scheduler, non-interactive-scheduler),
and commands/review/ (findings).

Extract the generic normalizePartList helper into
utils/normalize-part-list.ts so utils consumers keep importing downward,
and move the MergeStrategy enum into utils/deepMerge.ts (its owner).

Add an eslint architecture rule (no-utils-upward-import) that forbids
value imports from utils/ back up into a domain directory. Type-only
imports stay exempt: they are erased at compile time and cannot create a
runtime cycle (Settings in modelConfigUtils, CommandContext in
sessionPaths).

No behavior change: typecheck, build, and the affected unit tests pass.

* fix: use Qwen Team 2026 license header on new files (#9146)

* chore: refresh stale utils/ path references after leaf-layer move (#9146)

* docs: reconcile no-utils-upward-import header with the allowed type-only set (#9146)

* fix(cli): allowlist sandbox process.env accesses after leaf-layer move (#9146)

* chore(ci): re-record qwen-autofix.yml size baseline after #9677 (#9146)

#9677 recorded qwen-autofix.yml at 392111 bytes while the file it
committed was already 397656, so every PR that merged main after it
tripped the growth ratchet. Re-record the actual size; the file itself
is unchanged by this PR.

* fix(review): drop the stale utils/findings.ts digest root after the leaf-layer move (#9146)

The #9146 move returned findings.ts to commands/review/, but the digest
root lists merged from main still pinned it under utils/, where the file
no longer exists — the absent root darkened every review's staleness
check and failed review-source-digest.test.ts. Drop the stale file-shaped
root from both digest copies and their pins; the commands/review/
directory root covers the validator at its new home, and the two utils
helpers keep their file-shaped roots.

* fix(review): colocate seatbelt profiles with the sandbox module (#9146)

* fix(review): exempt inline type-only specifiers from the utils upward-import rule (#9146)

* fix(review): report upward inline type-specifier imports under verbatimModuleSyntax (#9146)

Co-authored-by: Qwen-Coder <[email protected]>

* test(review): pin mixed-specifier and zero-specifier upward imports in the utils rule (#9146)

Co-authored-by: Qwen-Coder <[email protected]>

* test(review): anchor the nested-checkout utils rule fixture on the last marker (#9146)

Co-authored-by: Qwen-Coder <[email protected]>

* test(review): pin that the utils/findings.ts digest root stays removed (#9146)

Co-authored-by: Qwen-Coder <[email protected]>

* fix(review): reword stale-bundle SCOPE header to the post-move helper shape (#9146)

* test(review): drop the pre-move utils/findings.ts from the skill-parity fixture (#9146)

* test(serve): derive the seatbelt colocation tripwire from BUILTIN_SEATBELT_PROFILES (#9146)

* fix(architecture): fail closed on computed dynamic imports in the utils leaf rule (#9146)

* fix(cli): point settings.test.ts at the post-move settingsUtils path (#9146)

main updated settings.test.ts after this branch moved settingsUtils.ts
from utils/ into config/, and the merge kept main's old import
specifier, which vite fails to resolve. Repoint it at ./settingsUtils.js;
every other consumer already uses the new path.

* fix(cli): close utils boundary review gaps

* test(cli): cover utils boundary allow paths

---------

Co-authored-by: Qwen-Coder <[email protected]>
2026-08-23 14:41:49 +00:00
易良 10c647f320 refactor(core): remove root barrel self-imports and enforce the boundary (#9635)
* refactor(core): remove root barrel self-imports

* fix(core): activate root barrel boundary rule

* test(core): cover root barrel export visitors

* fix(core): remove unused boundary constant

* fix(core): correct misplaced imports flagged by root barrel rule

coreToolScheduler: ToolExecutionStatus is exported from core/turn.ts, not
tools/tools.ts; was also duplicated in the tools import block.
nonInteractiveToolExecutor: RuntimeContentGeneratorView is exported from
agents/runtime/agent-context.ts, not core/contentGenerator.ts.

Both were surfaced by the new no-core-root-barrel-import rule in #9152.

* fix(core): complete telemetry mock and harden barrel rule edge cases

extensionManager.test: the ../telemetry/loggers.js mock was missing
logExtensionInstallEvent, logExtensionUninstall, and logExtensionDisable,
which the source now imports from that path after the barrel cleanup.
This caused 53 test failures.

no-core-root-barrel-import.js: added package-specifier detection
(@qwen-code/qwen-code-core), template-literal dynamic import support,
depth-0 ./index.js handling, and __tests__ exemption parity with the
test-file check. Added trailing newline.

Tests: added coverage for all new edge cases in both rule test files.

Refs #9152

* fix(core): close package-level barrel gap and coverage holes

The barrel rule blocked only specifiers resolving to the src-level
index.{js,ts}; the package-level barrel (packages/core/index.ts, which
re-exports the src barrel) remained reachable via one extra ../ hop.
Match ../index.{js,ts} so core production files cannot re-import the
root barrel through that path.

Also: hoist the duplicated src-root marker to a single constant, reword
the rule message so it no longer hardcodes ../index.js, pin the .ts
spelling branch, fix the vacuous fixtures-exemption case, and add a
cross-directory flat-config fixture so a narrowed files glob fails the
integration test.

Refs #9152

* fix(core): flag barrel subpaths and inline type imports in the root barrel rule

* fix(core): harden no-core-root-barrel-import rule

Anchor the source-root marker on its last occurrence so nested checkouts resolve correctly, reject relative imports of the compiled dist barrel, and treat .spec files as tests. Adds regression coverage for each.
2026-08-22 13:42:10 +00:00
koalazf.99 a9d6965bef pre-release commit 2025-07-22 23:26:01 +08:00