Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input

# Conflicts:
#	docs/module-graph.md
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/src/client/chat/AssistantMarkdown.tsx
#	packages/client/ui-conversation/src/client/chat/ChatView.tsx
#	packages/client/ui-conversation/src/client/chat/MessageItem.tsx
#	packages/client/ui-conversation/src/client/skeleton/ConversationSession.tsx
#	packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
#	packages/client/ui-conversation/tests/input-bar.spec.tsx
#	packages/client/ui-trajectory/tests/client-bundle.spec.ts
This commit is contained in:
creatixchu
2026-07-31 03:12:38 +08:00
172 changed files with 2834 additions and 1160 deletions

View File

@@ -0,0 +1,55 @@
/**
* Mechanical guard for the coverage-exempt roster: each entry's positional
* filter and exclude glob must select the same non-empty file set out of the
* repository's spec inventory, so a renamed suite cannot silently fall out of
* the uninstrumented gate while its exclude goes stale.
*/
import { globSync } from 'node:fs'
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { coverageExemptHeavySuites } from './coverage-exempt.ts'
const root = resolve(import.meta.dirname, '..')
/** The spec inventory mirrored from vitest.config.ts testIncludes. */
const allSpecs = new Set([
...globSync('packages/*/*/tests/**/*.spec.ts', { cwd: root }),
...globSync('packages/*/*/tests/**/*.spec.tsx', { cwd: root }),
...globSync('apps/*/tests/**/*.spec.ts', { cwd: root }),
...globSync('examples/*/tests/**/*.spec.ts', { cwd: root }),
...globSync('scripts/**/*.spec.ts', { cwd: root }),
].map(path => path.replaceAll('\\', '/')))
function excludeMatches(exclude: string): string[] {
return globSync(exclude, { cwd: root })
.map(path => path.replaceAll('\\', '/'))
.filter(path => allSpecs.has(path))
.sort()
}
function filterMatches(filter: string): string[] {
return [...allSpecs].filter(spec => spec.startsWith(filter)).sort()
}
describe('coverage-exempt roster', () => {
it.each(coverageExemptHeavySuites.map(suite => [suite.filter, suite] as const))(
'filter and exclude select the same non-empty spec set for %s',
(_filter, suite) => {
const fromExclude = excludeMatches(suite.exclude)
const fromFilter = filterMatches(suite.filter)
expect(fromExclude.length).toBeGreaterThan(0)
expect(fromFilter).toEqual(fromExclude)
},
)
it('entries never overlap, so no suite is double-run or double-excluded', () => {
const seen = new Map<string, string>()
for (const suite of coverageExemptHeavySuites) {
for (const spec of excludeMatches(suite.exclude)) {
expect(seen.get(spec), `${spec} matched by ${seen.get(spec) ?? ''} and ${suite.exclude}`).toBeUndefined()
seen.set(spec, suite.exclude)
}
}
})
})

View File

@@ -0,0 +1,41 @@
/**
* Heavy suites the coverage aggregate runs uninstrumented in a parallel gate.
* Membership contract: a suite qualifies only when every coverage-measured
* file it executes in-process (`coverage.include` spans package src trees;
* typert generator src is threshold-excluded in vitest.config.ts) is already
* fully covered by other suites, so removing it from the instrumented run
* changes no threshold outcome. The aggregate still runs every listed suite
* plain beside the instrumented gate, so correctness signal is unchanged —
* only the v8 instrumentation tax on compiler- and subprocess-heavy fixtures
* is dropped.
*/
/** One coverage-exempt suite: a Vitest CLI filter and its exclude glob. */
export interface CoverageExemptSuite {
/** Positional file filter selecting the suite in the uninstrumented gate. */
readonly filter: string
/** Exclude glob removing the suite from the instrumented gate. */
readonly exclude: string
}
/**
* Set to `1` by the instrumented coverage gate; vitest.config.ts then drops
* the exempt suites from every project. CLI `--exclude` cannot express this:
* it does not reach per-project include resolution.
*/
export const COVERAGE_EXEMPT_ENV = 'DSH_COVERAGE_EXEMPT_HEAVY'
/** Coverage-exempt heavy suites; keep filter and exclude selecting the same files. */
export const coverageExemptHeavySuites: readonly CoverageExemptSuite[] = [
// Whole-workspace compiler analysis per case — the lane's longest tail.
// Generator src is threshold-excluded; tools-catalog's registry and
// tool-cordis imports are fully covered by those packages' own tests.
{
filter: 'packages/typert/generator/tests/',
exclude: 'packages/typert/generator/tests/**',
},
// Real child-process fixtures over scripts/ sources, which coverage never measures.
{ filter: 'scripts/install-lefthook.spec.ts', exclude: 'scripts/install-lefthook.spec.ts' },
{ filter: 'scripts/oxlint-contract.spec.ts', exclude: 'scripts/oxlint-contract.spec.ts' },
{ filter: 'scripts/change-scope.spec.ts', exclude: 'scripts/change-scope.spec.ts' },
]

View File

@@ -9,6 +9,7 @@ import { spawn } from 'node:child_process'
import { availableParallelism } from 'node:os'
import { resolve } from 'node:path'
import { performance } from 'node:perf_hooks'
import { COVERAGE_EXEMPT_ENV, coverageExemptHeavySuites } from './coverage-exempt.ts'
/** A named aggregate exposed by the gate runner. */
export type Mode =
@@ -202,7 +203,7 @@ export function gatesForMode(selected: Mode): Gate[] {
pnpmScript('duplication', 'duplication'),
]
case 'ci-coverage':
return [coverageGate()]
return coverageGates()
case 'ci-snapshot':
return [pnpmScript('build', 'build'), snapshotGate()]
case 'ci-artifacts':
@@ -248,7 +249,7 @@ function ciPrimaryGates(): Gate[] {
pnpmScript('typecheck', 'typecheck'),
lintGate(),
pnpmScript('duplication', 'duplication'),
coverageGate(),
...coverageGates(),
...nodeCompatSmokeGates(),
snapshotGate(),
...docSyncLeafGates(),
@@ -407,15 +408,51 @@ function lintGate(): Gate {
: { displayCommand: `DSH_OXLINT_THREADS=${raw} pnpm run lint` })
}
function coverageGate(): Gate {
return pnpmExec('coverage', [
'vitest',
'run',
'--coverage',
...positiveIntArg('DSH_COVERAGE_MAX_WORKERS', '--maxWorkers'),
], {
label: 'test:coverage',
})
// The heavy suites run uninstrumented beside the thresholded gate: their
// compiler- and subprocess-bound fixtures pay a multiple of their runtime
// under v8 instrumentation while contributing nothing the thresholds need
// (membership contract in scripts/coverage-exempt.ts).
//
// DSH_COVERAGE_MAX_WORKERS is the lane's worker budget, so the two parallel
// gates split it instead of each claiming it whole (the failover pool's
// 8 x 6-instance bound assumes one lane never exceeds its value). The exempt
// gate's wall clock is dominated by its longest single file, so it takes the
// small share. A budget of 1 gives each gate 1 worker; lanes that need a
// strict total of one (the serial reference jobs) also set
// DSH_GATE_CONCURRENCY=1, which keeps the gates from overlapping at all.
function coverageWorkerArgs(): { instrumented: string[]; exempt: string[] } {
const [flag] = positiveIntArg('DSH_COVERAGE_MAX_WORKERS', '--maxWorkers')
if (flag === undefined) return { instrumented: [], exempt: [] }
const total = Number.parseInt(flag.split('=')[1] ?? '', 10)
const exempt = Math.max(1, Math.floor(total / 3))
const instrumented = Math.max(1, total - exempt)
return {
instrumented: [`--maxWorkers=${String(instrumented)}`],
exempt: [`--maxWorkers=${String(exempt)}`],
}
}
function coverageGates(): Gate[] {
const workers = coverageWorkerArgs()
return [
pnpmExec('coverage', [
'vitest',
'run',
'--coverage',
...workers.instrumented,
], {
label: 'test:coverage',
env: { [COVERAGE_EXEMPT_ENV]: '1' },
}),
pnpmExec('coverage-exempt-heavy', [
'vitest',
'run',
...coverageExemptHeavySuites.map(suite => suite.filter),
...workers.exempt,
], {
label: 'test:coverage-exempt-heavy',
}),
]
}
// Example and package snapshots boot their bins in `lib` mode (built artifacts under plain Node,