os.availableParallelism() reports host CPUs rather than a container's
CPU quota (256 against a 64-CPU quota here), so local defaults spawned
255 Vitest forks (55 GiB peak RSS) and oversubscribed every scheduler.
scripts/parallelism.ts keeps the CPU count in CI and caps other runs at
12, or 48 for Vitest forks and the local coverage budget; documentation
and hygiene aggregates run up to 8 gates instead of 4. Explicit
DSH_GATE_CONCURRENCY, DSH_OXLINT_THREADS, DSH_COVERAGE_MAX_WORKERS, and
--maxWorkers still win.
Refs #5852
Type checks only need Host declarations and Typert-generated
declarations, but typecheck, lint, lint:fix, doc-typecheck, and the CI
typert-contracts gate rebuilt the whole Host tsdown pass, Desktop bundle
included, to obtain them. A source-only scripts/generate-typert.ts now
runs the same generator and artifact writer as the tsdown plugin, beside
Host package compilation: Typert reads only sources once the missing
@deepseek-ai/dsh-client-file-upload/types path alias exists, and
rejectBuildOutputs fails the pass when a workspace lib/ declaration
enters its program. typecheck runs as a build.ts scope that checks both
aggregates in build mode after their own face compiles.
Refs #5852
build:artifacts produces every artifact, including the tsdown bundles,
the Desktop bundle, and Web, so naming it after sources undersold it;
build:check checks scripts, benchmarks, and website code besides tests.
The build.ts scopes, aggregate step ids, the Python builder flag
(--build-artifacts), workflows, callers, docs, and the build-split Agent
Note follow the new names. The note also records the naming
alternatives.
Refs #5852
pnpm run build ran every step in one serial chain, including the two
noEmit aggregate programs (tests, scripts, benchmarks, website) that no
artifact step consumes, so the Host aggregate check alone delayed tsdown by
about 66 s.
- build:src (scripts/build.ts --scope src) builds every artifact in the
existing order and writes the client build record; it replaces the
removed --artifacts-only flag and the libraries CLI mode.
- build:test (--scope test) type-checks only the Host and Client aggregate
programs with tsc -p, concurrently, without compiling references or
checking prerequisites.
- build runs both as one dependency graph through run-gates' new
runAggregate: each aggregate check starts once its face's packages are
compiled, and Client compilation stays after the Host bundle that writes
the Typert /remote declarations. DSH_GATE_CONCURRENCY=1 serializes it;
DSH_GATE_FAIL_FAST=1 stops the remaining steps at the first failure.
Passing steps print one PASS line (DSH_GATE_VERBOSE=1 shows their output).
- PR artifact builds (e2e, release, preview, Node 22 compatibility) and the
Python builder (--build-src) use build:src; dev:web and dev:desktop use
build:src. test:*, bench, typecheck and the required consumer/Windows
lanes keep the complete build.
- lint, lint:fix, doc-typecheck and the Typert contracts gate prepare with
host-libraries (Host package compilation + Host bundle) instead of
build:lib:host; their own checks never read the aggregate root or the
Desktop bundle.
Parallel and serial build produce byte-identical lib/, Web dist and client
build record; a test-only type error fails build and build:test while
build:src passes.
Refs #5852
`scripts/gen-cli-help.ts` runs the source `dsh` launcher with temporary
Harness and Agents homes and captures the launcher, `dsh plugin`, and every
shipped profile's text help (profile names come from `PROFILE_TEMPLATES`).
It recursively discovers subcommands from each page's `Commands:` section,
shows identical alias help once with every invocation, and writes
`docs/cli-help.md`, its Chinese page, and their pairing record. The record
resolves links between the two pages as written, so a first run already
matches `verify-translation-pairing`. `verify-cli-help` checks all three
files and runs as the `cli-help` doc-sync leaf, which the static CI lane
also runs on an unbuilt tree.
`dsh plugin` now owns `-h`/`--help` and reads its own options only before
the forwarded pnpm arguments: `dsh plugin --help` prints its help instead of
failing on the missing `--profile`, while everything after the pnpm command
(including `-h`, `--help`, and `--profile`) reaches pnpm verbatim. A
`--profile` placed after the pnpm command is no longer accepted; the
v0.2.1-alpha.1 upgrade guide covers the migration.
Refs #4114
Add appendPluginRecord(), which appends one ignorable event whose type is
`plugin:` followed by lowercase slash-separated segments, and
pluginRecordOf(), which reads such an event back. Records are not
SessionEventMap members, so the persistence catalog is unchanged; a build
that does not recognize one retains and skips it, and records never enter
the model-visible surface. Resume and fork carry them with the log, and a
format migration keeps them on a best-effort basis.
Only production source under packages/experimental/ may call the writer.
The verify-plugin-record-callers hygiene gate checks every reference in
repository sources, and the persistence catalog generator rejects a
SessionEventMap member in the plugin: namespace.
Session.append() and the record writer share one commit path, so a record
follows the same reentrancy, detach, and observer-containment rules. The
ignorable-events decision, the session README, the format-version
cookbook, and the plugin-author practices describe the records.
Delete @deepseek-ai/dsh-invariants, every package's ./invariant companion
and its tests, the invariant gates and Vitest setup, the scoped-event
resolver generator, and the INVARIANT-coded listener rethrow paths.
Update docs, READMEs, Agent Notes, and add an upgrade guide.
Resolves the semantic conflict with #5331: its ci-unit gate spread
coverageTestTimeoutArgs, which this branch retires; the gate is now a
plain vitest run and the darwin lane budget reaches vitest.config.ts
through the inherited DSH_COVERAGE_TEST_TIMEOUT_MS. The ci-unit spec
cases pin that shape under both budget values.
The fixture's poll case counts attempts instead of reading a clock, so a host
stall cannot turn a budget-ended poll into a pass; the control case reads the
poll's reported duration, which the 1000 ms default bounds from below. The
spawning spec acquires its temporary config in beforeAll, bounds the child
with a timeout and asserts no signal ended it, and counts the polled values it
inspects. CoverageTestTimeoutOptions is Vitest's own TestUserConfig members;
the rule's reach has one home in its JSDoc, which the config, gate, spec, and
note comments reference.
DSH_COVERAGE_TEST_TIMEOUT_MS expanded to --testTimeout, --hookTimeout, and
--expect.poll.timeout on the coverage and serial lanes, but Vitest forwards
only a fixed list of CLI overrides into inline projects and neither
hookTimeout nor expect is on it, so under the root config's two projects the
hook and expect.poll budgets stayed at Vitest's defaults on every lane.
vitest.config.ts now reads the variable through coverageTestTimeoutOptions and
spreads the three budgets into both projects; run-gates and the partition
coordinator pass no timeout flags, leaving one owner for the rule. A new spec
spawns vitest on the root config with a 200 ms budget and proves all three
waits end inside both projects, that unset keeps the defaults, and that a
malformed value fails at config load.
Fixes#5335
Related to #5325
run-gates.spec.ts clears DSH_COVERAGE_PARTITIONS and DSH_COVERAGE_MAX_WORKERS
before each case, so the coverage gates it builds take their shape from the
case, not from a partitioned lane that exports the variables to every child.
The ci-unit case derives its two gates from the `test` package script's
segments. The serial-reference note keeps only the lane fact; the mechanism
fact about inline-project inheritance is owned by the fix that changes it.
The unit-darwin job ran the whole unit inventory at Vitest's 5000 ms default
on a shared 3-vCPU macos-latest runner; the first two master runs after #5292
each timed out a different set of cases that pass in under a second on a quiet
host. Every other whole-inventory lane raises the budget through
DSH_COVERAGE_TEST_TIMEOUT_MS=90000.
The job now sets that variable and runs `pnpm run check:ci:unit`, a run-gates
aggregate that is `pnpm run test` (native build, then `vitest run`) through the
path that consumes it. ci-unit receives no CI worker settings, so the inventory
inherits the environment `pnpm run test` finds: on a 3-CPU host the defaults
would inject DSH_COVERAGE_PARTITIONS=2, which run-gates.spec.ts reads as a
partitioned coverage lane.
Fixes#5325