From f6b0c5af59a493474043ff5e88563acb80512fcb Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Sun, 11 Oct 2026 22:16:47 +0800 Subject: [PATCH] build: rename build:src and build:test to build:artifacts and build:check 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 --- ...026-10-05-pr-artifact-typechecks.i18n.yaml | 4 +- .../2026-10-05-pr-artifact-typechecks.md | 6 +- .../2026-10-05-pr-artifact-typechecks.zh.md | 6 +- ...0-11-build-artifacts-check-split.i18n.yaml | 40 +++++++++ ...2026-10-11-build-artifacts-check-split.md} | 90 ++++++++++--------- ...6-10-11-build-artifacts-check-split.zh.md} | 90 ++++++++++--------- .../2026-10-11-build-src-test-split.i18n.yaml | 40 --------- ...26-10-11-lossless-build-speedups.i18n.yaml | 8 +- .../2026-10-11-lossless-build-speedups.md | 4 +- .../2026-10-11-lossless-build-speedups.zh.md | 4 +- .agents/skills/dsh-pre-push-checks/SKILL.md | 2 +- .../workflows/build-exe-for-python-sdk.yml | 2 +- .../workflows/build-preview-cloudflare.yml | 2 +- .github/workflows/e2e.yml | 2 +- .github/workflows/release.yml | 2 +- AGENTS.md | 10 +-- apps/desktop/scripts/dev.ts | 2 +- docs/development.i18n.yaml | 8 +- docs/development.md | 16 ++-- docs/development.zh.md | 16 ++-- package.json | 4 +- scripts/build-exe-for-python-sdk.spec.ts | 8 +- scripts/build-exe-for-python-sdk.ts | 16 ++-- scripts/build.spec.ts | 22 ++--- scripts/build.ts | 24 ++--- scripts/ci-workflow.spec.ts | 12 +-- scripts/dev-web.ts | 12 +-- scripts/preview-workflow.spec.ts | 4 +- scripts/run-gates.spec.ts | 4 +- scripts/run-gates.ts | 2 +- scripts/tests/ci-release-selfhosted.spec.ts | 2 +- 31 files changed, 234 insertions(+), 230 deletions(-) create mode 100644 .agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.i18n.yaml rename .agents/notes/implemented/process/{2026-10-11-build-src-test-split.md => 2026-10-11-build-artifacts-check-split.md} (54%) rename .agents/notes/implemented/process/{2026-10-11-build-src-test-split.zh.md => 2026-10-11-build-artifacts-check-split.zh.md} (54%) delete mode 100644 .agents/notes/implemented/process/2026-10-11-build-src-test-split.i18n.yaml diff --git a/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.i18n.yaml b/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.i18n.yaml index e0c2177559..192e40c2d2 100644 --- a/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.i18n.yaml +++ b/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.i18n.yaml @@ -9,8 +9,8 @@ en: e5c984fa32cc5e69 zh: 25d5742b2106d830 /agent-note-pr-artifact-builds-and-aggregate-typescript-checks/decision: - en: 34bb902d1d242729 - zh: 391b71f0fdb38d50 + en: af93d8849bb8831a + zh: 53e15e801ab844da /agent-note-pr-artifact-builds-and-aggregate-typescript-checks/alternatives-considered: en: f61da066f9ddcd62 zh: 816fd6e0063017cd diff --git a/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.md b/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.md index 27842735a3..6c422a612d 100644 --- a/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.md +++ b/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.md @@ -12,13 +12,13 @@ PR jobs need the same compiled packages for benchmarks, compatibility, packaging Required Linux consumer and native Windows build jobs retain normal builds, including both complete aggregate programs. Public `build` and `typecheck` keep the same checks. [Node version ownership](2026-07-06-node-engine-floor.md) and [native Windows ownership](2026-08-08-native-windows-pull-request-ci.md) remain in force. -Checked PR artifact builds compile every existing project reference from each compiler face with one serial `tsc -b` process, then run the ordinary Typert, bundling, Desktop, and Web stages. The helper reads the existing Host and Client aggregates rather than maintaining another package list. It omits only each root's additional test and script program; package diagnostics, emitted outputs, and Host-before-Client ordering remain required. Non-PR release and real-API workflow builds retain their normal checks. That artifact build is `pnpm run build:src`; [the build split](2026-10-11-build-src-test-split.md) owns its steps and scheduling. +Checked PR artifact builds compile every existing project reference from each compiler face with one serial `tsc -b` process, then run the ordinary Typert, bundling, Desktop, and Web stages. The helper reads the existing Host and Client aggregates rather than maintaining another package list. It omits only each root's additional test and script program; package diagnostics, emitted outputs, and Host-before-Client ordering remain required. Non-PR release and real-API workflow builds retain their normal checks. That artifact build is `pnpm run build:artifacts`; [the build split](2026-10-11-build-artifacts-check-split.md) owns its steps and scheduling. Only the CI benchmark helper selects `tsc -b --noCheck` for package emission. Benchmarks consume JavaScript and Typert runtime metadata; their declaration and build-information files are disposable and never published. Typert analyzes source types independently, while required complete builds own semantic diagnostics. Syntax and configuration failures still reject benchmark preparation. TypeScript records pending checks so a later ordinary incremental build diagnoses the same source without deleting caches or forcing a rebuild. Public benchmark scripts and every release, Python, preview, compatibility, and normal artifact builder keep their checked defaults. -The Python executable builder selects `build:src` artifact compilation with `--build-src` after verifying its runtime closure. Invalid closure inputs fail before compilation, deployment, or packing; the default complete build and prebuilt `--skip-build` mode keep their existing stages. +The Python executable builder selects `build:artifacts` artifact compilation with `--build-artifacts` after verifying its runtime closure. Invalid closure inputs fail before compilation, deployment, or packing; the default complete build and prebuilt `--skip-build` mode keep their existing stages. -PR artifact builds in CI invoke the root manifest's `build:src`; a PR that edits these root manifest scripts activates the separate native-addon platform matrix, a trade-off [the build split](2026-10-11-build-src-test-split.md) records. The vendor rehearsal and benchmark lanes invoke the internal build helpers directly. +PR artifact builds in CI invoke the root manifest's `build:artifacts`; a PR that edits these root manifest scripts activates the separate native-addon platform matrix, a trade-off [the build split](2026-10-11-build-artifacts-check-split.md) records. The vendor rehearsal and benchmark lanes invoke the internal build helpers directly. Job inventory, runner selectors, and benchmark sample counts stay fixed; gate and worker ceilings do not increase. The three compatibility cases run one job at a time; the two-target Python runtime matrix starts after the keyless Python SDK job succeeds. Four Linux lanes, the Python SDK, three Windows lanes, and one compatibility job permit at most nine worker jobs initially. Once the SDK job finishes, the remaining seven independent lanes, one compatibility job, and two runtime targets permit at most ten. The runtime plan and wheel stages precede those targets and each use one job. This permits runtime validation to overlap the Node 22 build within the original ten-job ceiling. Coverage partitions reuse compact file-cost metadata within their platform and runner pool. Parallel release packing uses pnpm's existing bounded scheduler; serial publication keeps family order. diff --git a/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.zh.md b/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.zh.md index 95b5baee6a..9f25366401 100644 --- a/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.zh.md +++ b/.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.zh.md @@ -12,13 +12,13 @@ PR job 需要相同的编译包来运行 benchmark、兼容性、打包和运行 必需的 Linux consumer 与原生 Windows build job 保留普通构建,包括两个完整 aggregate program。公开的 `build` 和 `typecheck` 保留相同检查。[Node 版本职责](2026-07-06-node-engine-floor.zh.md)与[原生 Windows 职责](2026-08-08-native-windows-pull-request-ci.zh.md)仍然有效。 -带检查的 PR 产物构建通过一个串行 `tsc -b` 进程编译各 compiler face 的全部现有 project reference,然后运行普通 Typert、bundling、Desktop 和 Web 阶段。helper 读取现有 Host 与 Client aggregate,而不维护另一份包清单。它只省略各 root 附加的测试和脚本 program;包诊断、emit 输出及 Host 先于 Client 的顺序仍然必需。非 PR 的 release 和真实 API 工作流构建保留普通检查。该产物构建即 `pnpm run build:src`,其步骤与调度见[构建拆分](2026-10-11-build-src-test-split.zh.md)。 +带检查的 PR 产物构建通过一个串行 `tsc -b` 进程编译各 compiler face 的全部现有 project reference,然后运行普通 Typert、bundling、Desktop 和 Web 阶段。helper 读取现有 Host 与 Client aggregate,而不维护另一份包清单。它只省略各 root 附加的测试和脚本 program;包诊断、emit 输出及 Host 先于 Client 的顺序仍然必需。非 PR 的 release 和真实 API 工作流构建保留普通检查。该产物构建即 `pnpm run build:artifacts`,其步骤与调度见[构建拆分](2026-10-11-build-artifacts-check-split.zh.md)。 只有 CI benchmark helper 选择 `tsc -b --noCheck` 来 emit 包。benchmark 消费 JavaScript 和 Typert 运行时元数据;其声明与 build-information 文件是临时产物,不会发布。Typert 独立分析源码类型,语义诊断由必需的完整构建负责。语法与配置错误仍会拒绝 benchmark 准备。TypeScript 记录待检查状态,使后续普通增量构建无需删除缓存或强制重建,就能诊断相同源码。公开 benchmark script,以及所有 release、Python、preview、compatibility 和普通产物 builder,均保留带检查的默认行为。 -Python executable builder 在验证 runtime closure 后,通过 `--build-src` 选择 `build:src` 产物编译。无效 closure 在编译、部署或打包前失败;默认完整构建和复用预构建产物的 `--skip-build` 模式保留原有阶段。 +Python executable builder 在验证 runtime closure 后,通过 `--build-artifacts` 选择 `build:artifacts` 产物编译。无效 closure 在编译、部署或打包前失败;默认完整构建和复用预构建产物的 `--skip-build` 模式保留原有阶段。 -CI 的 PR 产物构建调用 root manifest 中的 `build:src`;修改 root manifest 中这些 script 的 PR 会激活独立的 native-addon 平台矩阵,[构建拆分](2026-10-11-build-src-test-split.zh.md)记录这一取舍。vendor 演练与 benchmark lane 直接调用内部 build helper。 +CI 的 PR 产物构建调用 root manifest 中的 `build:artifacts`;修改 root manifest 中这些 script 的 PR 会激活独立的 native-addon 平台矩阵,[构建拆分](2026-10-11-build-artifacts-check-split.zh.md)记录这一取舍。vendor 演练与 benchmark lane 直接调用内部 build helper。 job 清单、runner 选择和 benchmark sample 数量保持固定;gate 与 worker 上限不提高。三个 compatibility case 每次运行一个 job;双目标 Python runtime 矩阵在 keyless Python SDK job 成功后启动。四个 Linux lane、Python SDK、三个 Windows lane 和一个 compatibility job,初始最多占用九个 worker job。SDK job 完成后,剩余七个独立 lane、一个 compatibility job 和两个 runtime target,最多占用十个 job。runtime plan 与 wheel 阶段先于这些 target,各占一个 job。这允许 runtime 验证与 Node 22 构建重叠,同时保持原有十个 job 的上限。覆盖率分区在各自的平台和 runner pool 内复用精简的文件成本元数据。并行 release 打包使用 pnpm 现有的有界调度器;串行发布保持 family 顺序。 diff --git a/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.i18n.yaml b/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.i18n.yaml new file mode 100644 index 0000000000..25a485df64 --- /dev/null +++ b/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.i18n.yaml @@ -0,0 +1,40 @@ +# Bilingual-pair consistency record for 2026-10-11-build-artifacts-check-split.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.md +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently: + en: b1a98fdd8008dce1 + zh: 15bdc2f2c2584226 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/problem: + en: bc382ad1f1e71352 + zh: 44189bf0f69a0bf7 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision: + en: a15c7c13f167e5ac + zh: 63bcc2ef60c2f344 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/commands: + en: 174241330463841d + zh: 7fe361c07e634053 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/steps-and-dependencies: + en: 40f21a0941b9a23a + zh: c9f1d702b804ecf2 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/scheduling-failure-and-output: + en: 4b324f92307d3691 + zh: e0cf4411a747f7e5 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/callers: + en: af86d6dbe8d3b17c + zh: 8e5fa596d6709b35 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/measurements: + en: a5d74335200d1476 + zh: e817deb711f98504 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/completeness-and-lossless-evidence: + en: 7308ab88d2b0926f + zh: 773ce413da90bcbc +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/decision/intentional-trade-offs-not-lossless: + en: f3a5621dbfcd602d + zh: f5beabfa81272ffb +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/alternatives-considered: + en: 54905b912d17c74e + zh: 86a298004c8024e3 +/agent-note-split-build-artifacts-and-build-check-with-build-running-both-concurrently/consequences: + en: ce9135e424811303 + zh: 1737f084156211c3 diff --git a/.agents/notes/implemented/process/2026-10-11-build-src-test-split.md b/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.md similarity index 54% rename from .agents/notes/implemented/process/2026-10-11-build-src-test-split.md rename to .agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.md index c4aede7339..4778f49621 100644 --- a/.agents/notes/implemented/process/2026-10-11-build-src-test-split.md +++ b/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.md @@ -1,8 +1,8 @@ -# Agent Note: Split build:src and build:test, with build running both concurrently +# Agent Note: Split build:artifacts and build:check, with build running both concurrently Status: implemented -English | [中文](2026-10-11-build-src-test-split.zh.md) +English | [中文](2026-10-11-build-artifacts-check-split.zh.md) ## Problem @@ -18,11 +18,11 @@ Each command does only its own job. | Command | Responsibility | Steps | Scheduling | Client build record | |---|---|---|---|---| -| `pnpm run build:src` | Produces every artifact of `build`; package projects are fully type-checked as usual, the two aggregate root programs are not checked | native → host-src → host-bundle → desktop-bundle → client-src → client-bundle → web | One linear chain | Deleted at start, written after every step passes | -| `pnpm run build:test` | Type-checks only the two aggregate root programs | host-test, client-test | Both steps concurrent, no prerequisite steps | Neither read nor written | -| `pnpm run build` | Every step of `build:src` and `build:test` | The dependency graph below | Concurrent through the run-gates scheduler | Deleted at start, written after every step passes | +| `pnpm run build:artifacts` | Produces every artifact of `build`; package projects are fully type-checked as usual, the two aggregate root programs are not checked | native → host-src → host-bundle → desktop-bundle → client-src → client-bundle → web | One linear chain | Deleted at start, written after every step passes | +| `pnpm run build:check` | Type-checks only the two aggregate root programs | host-check, client-check | Both steps concurrent, no prerequisite steps | Neither read nor written | +| `pnpm run build` | Every step of `build:artifacts` and `build:check` | The dependency graph below | Concurrent through the run-gates scheduler | Deleted at start, written after every step passes | -`build:test` neither detects, rebuilds, nor validates `build:src` outputs; when prerequisite outputs are missing or stale, the compiler's own diagnostics (for example TS6305 and TS2307) report it, and no command adds prerequisite checks, guards, or fallbacks. `build:src` produces the same artifacts and record as `build` and replaces `--artifacts-only`; that flag and the `libraries` mode of `compile-referenced-projects.ts` are removed, and the Python executable builder uses `--build-src`. `build:official` remains `tsx scripts/build.ts --profile official` and runs the complete `build`. +`build:check` neither detects, rebuilds, nor validates `build:artifacts` outputs; when prerequisite outputs are missing or stale, the compiler's own diagnostics (for example TS6305 and TS2307) report it, and no command adds prerequisite checks, guards, or fallbacks. `build:artifacts` produces the same artifacts and record as `build` and replaces `--artifacts-only`; that flag and the `libraries` mode of `compile-referenced-projects.ts` are removed, and the Python executable builder uses `--build-artifacts`. `build:official` remains `tsx scripts/build.ts --profile official` and runs the complete `build`. ### Steps and dependencies @@ -35,24 +35,24 @@ Each command does only its own job. | client-src | ` -b ` | desktop-bundle | | client-bundle | `tsdown --config-loader native --env.DSH_BUILD_FACE client` | client-src | | web | `pnpm run build:web` | client-bundle | -| host-test | `node --max-old-space-size=4096 -p tsconfig.host.json` | host-src | -| client-test | ` -p tsconfig.client.json` | client-src | +| host-check | `node --max-old-space-size=4096 -p tsconfig.host.json` | host-src | +| client-check | ` -p tsconfig.client.json` | client-src | ```text -native → host-src ─┬─ host-test ─────────────────────────────────────────────┐ - └─ host-bundle → desktop-bundle → client-src ─┬─ client-test ┤ → record +native → host-src ─┬─ host-check ───────────────────────────────────────────────────────┐ + └─ host-bundle → desktop-bundle → client-src ─┬─ client-check ───────┤ → record └─ client-bundle → web ┘ ``` -- client-src must follow host-bundle: Client packages import `@deepseek-ai/dsh-*/remote`, whose types `lib/typert.remote-client.d.ts` only Typert inside Host tsdown writes. host-test reads no Typert output; clean serial builds have always finished the Host aggregate check before Host tsdown. -- The src chain keeps the two non-data edges native → host-src and desktop-bundle → client-src; only the two test steps attach to the chain in parallel. +- client-src must follow host-bundle: Client packages import `@deepseek-ai/dsh-*/remote`, whose types `lib/typert.remote-client.d.ts` only Typert inside Host tsdown writes. host-check reads no Typert output; clean serial builds have always finished the Host aggregate check before Host tsdown. +- The artifact chain keeps the two non-data edges native → host-src and desktop-bundle → client-src; only the two check steps attach to the chain in parallel. - [`compile-referenced-projects.ts`](../../../../scripts/compile-referenced-projects.ts) owns the package-library step list `libraryBuildSteps`, the references argument construction `referencedProjectsCompilerArgs` (relative paths keep the command below Windows' command-line limit), and the aggregate-check arguments `aggregateCheckArgs`. The vendor release rehearsal (`host-libraries`) and CI benchmarks (`--noCheck`) run the same step list serially. -- The test steps use project mode (`tsc -p`): it reads the declarations that references already emitted and compiles no reference, writing incremental state to the aggregates' existing `tsconfig.{host,client}.tsbuildinfo`. `build` and `build:test` run the same command, so their diagnostics are identical. +- The check steps use project mode (`tsc -p`): it reads the declarations that references already emitted and compiles no reference, writing incremental state to the aggregates' existing `tsconfig.{host,client}.tsbuildinfo`. `build` and `build:check` run the same command, so their diagnostics are identical. ### Scheduling, failure, and output -- [`scripts/build.ts`](../../../../scripts/build.ts) owns the step table and the three scopes and executes them through `runAggregate` from [`scripts/run-gates.ts`](../../../../scripts/run-gates.ts): each step is a gate with `needs`, and shell-free spawning, per-step buffered output, fail-fast process-tree termination, Windows `taskkill`, and host signal forwarding come from the run-gates implementation. `build:src` is `tsx scripts/build.ts --scope src`, `build:test` is `--scope test`, and `build` omits `--scope` and runs both as one graph; `--profile` applies only to artifact scopes, and combining it with `--scope test` fails argument parsing. -- The concurrency limit is `DSH_GATE_CONCURRENCY`, defaulting to the available CPU count; the dependency graph is at most 3 wide (client-test and client-bundle can start while host-test is still running). Master serial reference lanes set `DSH_GATE_CONCURRENCY=1`, so `build` runs serially in topological order there, consistent with the [serial reference decision](2026-07-21-serial-cross-platform-ci-reference.md) and without workflow edits. +- [`scripts/build.ts`](../../../../scripts/build.ts) owns the step table and the three scopes and executes them through `runAggregate` from [`scripts/run-gates.ts`](../../../../scripts/run-gates.ts): each step is a gate with `needs`, and shell-free spawning, per-step buffered output, fail-fast process-tree termination, Windows `taskkill`, and host signal forwarding come from the run-gates implementation. `build:artifacts` is `tsx scripts/build.ts --scope artifacts`, `build:check` is `--scope check`, and `build` omits `--scope` and runs both as one graph; `--profile` applies only to artifact scopes, and combining it with `--scope check` fails argument parsing. +- The concurrency limit is `DSH_GATE_CONCURRENCY`, defaulting to the available CPU count; the dependency graph is at most 3 wide (client-check and client-bundle can start while host-check is still running). Master serial reference lanes set `DSH_GATE_CONCURRENCY=1`, so `build` runs serially in topological order there, consistent with the [serial reference decision](2026-07-21-serial-cross-platform-ci-reference.md) and without workflow edits. - By default (locally), every dependent of a failed step is recorded as skipped, while running steps and steps unrelated to the failure run to completion; the executed set depends only on step outcomes, so one run reports every independent type error. With `DSH_GATE_FAIL_FAST=1` (set in every building PR lane), the first failure terminates the process trees of the other running steps and records every unfinished step as skipped. - A failed step prints `== FAILED ==`, its command, the exit reason, and the step's complete buffered output, and the final summary lists failed and skipped steps; a passing step prints only its PASS line, and `DSH_GATE_VERBOSE=1` prints all output. The exit code is 1 when any step failed or was skipped, and the client build record is not written. Every step inherits the public Client environment selected by `clientBuildProcessEnvironment`. - In CI, `build` itself is one gate of an outer run-gates aggregate; outer fail-fast termination propagates to the inner scheduler. `check:node-compat` and `check:ci:lint:contracts-ready` already nest the same way. @@ -64,20 +64,20 @@ native → host-src ─┬─ host-test ───────────── | `build`, `build:official`, `Makefile` `build`, `scripts/publish-npm-baseline.ts`, `apps/desktop/scripts/package-target.ts`, `scripts/dev-bundle-registry.ts`, `release-publish.yml`, `sandbox.yml`, and the non-PR branches of `e2e.yml` and `release.yml` | `build` / `build:official` | Yes | | `test:web`, `test:web:refresh`, `test:web:perf`, `test:web:stress`, `apps/desktop` `test:updates:local`, `test:bench` / `build:bench` | `build`; `build:bench` runs `build:native-system`, `build:lib`, and the benchmark tsdown | Yes | | `typecheck` (run by the pre-push hook) | `build:lib:host` (Host `tsc -b tsconfig.host.json`, Host bundle, Desktop bundle) and Client `tsc -b tsconfig.client.json` | Yes | -| `dev:web` ([`scripts/dev-web.ts`](../../../../scripts/dev-web.ts)), `dev:desktop` (`apps/desktop/scripts/dev.ts`) | `build:src` | None before launch; after `dev:web` launches, its `tsc -b tsconfig.client.json --watch` keeps checking the Client aggregate root program | +| `dev:web` ([`scripts/dev-web.ts`](../../../../scripts/dev-web.ts)), `dev:desktop` (`apps/desktop/scripts/dev.ts`) | `build:artifacts` | None before launch; after `dev:web` launches, its `tsc -b tsconfig.client.json --watch` keeps checking the Client aggregate root program | | Prerequisite step of `lint`, `lint:fix`, `doc-typecheck`, and the CI Typert contracts gate | Host package compilation and the Host bundle (`compile-referenced-projects.ts host-libraries`), without the Desktop bundle | None | | CI node 24 / snapshots and artifacts (`check:ci:consumers`), windows node 24 / build & observational (`check:ci:windows-blocking`) | `build` | Yes, the two required lanes | -| CI node-compat 22.19 | `build:src` (through run-gates) | None | -| CI python runtime | `build-exe-for-python-sdk.ts --build-src` | None | -| PR rehearsals in `e2e.yml` and `release.yml` | `pnpm run build:src --profile official` | None | -| `build-preview-cloudflare.yml` | `pnpm run build:src` | None | +| CI node-compat 22.19 | `build:artifacts` (through run-gates) | None | +| CI python runtime | `build-exe-for-python-sdk.ts --build-artifacts` | None | +| PR rehearsals in `e2e.yml` and `release.yml` | `pnpm run build:artifacts --profile official` | None | +| `build-preview-cloudflare.yml` | `pnpm run build:artifacts` | None | | CI node 24 / benchmarks | `run-ci-bench.ts` (`tsc -b --noCheck`) | None | | PR rehearsal in `release-vendor.yml` | `compile-referenced-projects.ts host-libraries` | None | | Non-PR branch of `release-vendor.yml`, `release-vendor-publish.yml` | `build:lib:host` | Host aggregate root program only | `lint`, `lint:fix`, `doc-typecheck`, and the Typert contracts gate read only Typert outputs and package declarations: both aggregates are `noEmit`, oxlint builds its own programs, `doc-typecheck` references only package projects, and no consumer of `host-libraries` reads `apps/desktop/lib`. Their prerequisite step therefore skips the Host aggregate check and the Desktop bundle and falls from 159.8 s to 90.9 s (see the measurements below). -The consumers lane's HMR Web test starts `dev:web` and therefore uses `build:src` inside that lane; the same lane's `build` gate still runs both aggregate checks. CI does not call `build:test` separately: the two required lanes already get the same checks through `build`. The [PR artifact build decision](2026-10-05-pr-artifact-typechecks.md) keeps its required complete builds, benchmark `--noCheck`, job and worker ceilings, serial node-compat Vitest, coverage weights, and pnpm packing checks; its PR artifact build is `build:src`. +The consumers lane's HMR Web test starts `dev:web` and therefore uses `build:artifacts` inside that lane; the same lane's `build` gate still runs both aggregate checks. CI does not call `build:check` separately: the two required lanes already get the same checks through `build`. The [PR artifact build decision](2026-10-05-pr-artifact-typechecks.md) keeps its required complete builds, benchmark `--noCheck`, job and worker ceilings, serial node-compat Vitest, coverage weights, and pnpm packing checks; its PR artifact build is `build:artifacts`. ### Measurements @@ -89,7 +89,7 @@ Prototype conditions: a shared 256 vCPU Linux host (load average 23–70), Node | Each step run separately in sequence | 255.1 | 373.8 | 5.82 | | Concurrent by dependency graph | 172.1 | 392.1 | 8.00 | -Median wall falls from 249.4 s to 172.1 s (−77.3 s, −31%). The two runs of each step separately differ by 28 s, so the overhead of running the steps as separate processes is not resolvable. CPU grows by about 14 s (+3.7%), mostly Host aggregate check user time under contention; the RSS peak grows by about 2.1 GiB where the Host aggregate check (about 4 GiB) overlaps Host tsdown (about 5.7 GiB). The concurrent critical path is host-src → host-bundle → desktop-bundle → client-src → client-test; host-test finishes about 33 s before the end and client-bundle about 21 s before. +Median wall falls from 249.4 s to 172.1 s (−77.3 s, −31%). The two runs of each step separately differ by 28 s, so the overhead of running the steps as separate processes is not resolvable. CPU grows by about 14 s (+3.7%), mostly Host aggregate check user time under contention; the RSS peak grows by about 2.1 GiB where the Host aggregate check (about 4 GiB) overlaps Host tsdown (about 5.7 GiB). The concurrent critical path is host-src → host-bundle → desktop-bundle → client-src → client-check; host-check finishes about 33 s before the end and client-bundle about 21 s before. Final measurements: master (the merge base) and this branch run interleaved, each from clean, in a 64-core cgroup; values are medians of 2 samples unless marked n=1. @@ -97,59 +97,61 @@ Final measurements: master (the merge base) and this branch run interleaved, eac |---|---|---|---|---| | Complete build `pnpm run build` | 317.1 s | 169.1 s (−46.7%) | 423.1 → 374.4 s (−11.5%) | 6.11 → 7.53 GiB (+1.42) | | Serial branch build `DSH_GATE_CONCURRENCY=1 pnpm run build` (n=1) | — | 266.7 s | 389.5 s | 6.27 GiB | -| Artifact build: master `build --artifacts-only`, branch `build:src` | 185.8 s | 153.6 s (−17.3%) | 256.2 → 231.2 s | 6.58 → 6.18 GiB | -| Development iteration: master `build`, branch `build:src` | 317.1 s | 153.6 s (−51.6%) | 423.1 → 231.2 s (−45.4%) | 6.11 → 6.18 GiB | -| `pnpm run build:test` after `build:src` | — | 67.7 s | 144.6 s | 5.33 GiB | -| `pnpm run build:test` no-op rerun | — | 19.4 s | 33.1 s | Not measured | +| Artifact build: master `build --artifacts-only`, branch `build:artifacts` | 185.8 s | 153.6 s (−17.3%) | 256.2 → 231.2 s | 6.58 → 6.18 GiB | +| Development iteration: master `build`, branch `build:artifacts` | 317.1 s | 153.6 s (−51.6%) | 423.1 → 231.2 s (−45.4%) | 6.11 → 6.18 GiB | +| `pnpm run build:check` after `build:artifacts` | — | 67.7 s | 144.6 s | 5.33 GiB | +| `pnpm run build:check` no-op rerun | — | 19.4 s | 33.1 s | Not measured | | Prerequisite of `lint` and `doc-typecheck`: master `build:lib:host`, branch `host-libraries` | 159.8 s | 90.9 s (−43.2%) | 234.7 → 136.0 s | Not measured | -The serial branch sample overlapped other work on the host (742 s of foreign CPU), so its wall likely overstates the serial cost. In the serial build segments (master → branch, one sample each), Host tsdown falls from 22.5 to 11.7 s, Client package compilation from 55.6 to 48.7 s, the Client aggregate check from 68.2 to 35.9 s, and Client tsdown from 13.9 to 10.5 s; the segments sum to 308.5 s and 254.9 s. Those reductions come from [the lossless speed-ups note](2026-10-11-lossless-build-speedups.md); the further reduction from 254.9 s of serial segments to the 169.1 s concurrent build comes from this note's scheduling, which takes the Host aggregate check (about 66 s) off the critical path. In the concurrent build, the critical path is native → host-src → host-bundle → desktop-bundle → client-src → client-test, and host-test ends about 30 s before the last step (one earlier sample). `build:test` runs the Host and Client aggregate checks concurrently, so its wall is the Host check's (about 65 s). +The serial branch sample overlapped other work on the host (742 s of foreign CPU), so its wall likely overstates the serial cost. In the serial build segments (master → branch, one sample each), Host tsdown falls from 22.5 to 11.7 s, Client package compilation from 55.6 to 48.7 s, the Client aggregate check from 68.2 to 35.9 s, and Client tsdown from 13.9 to 10.5 s; the segments sum to 308.5 s and 254.9 s. Those reductions come from [the lossless speed-ups note](2026-10-11-lossless-build-speedups.md); the further reduction from 254.9 s of serial segments to the 169.1 s concurrent build comes from this note's scheduling, which takes the Host aggregate check (about 66 s) off the critical path. In the concurrent build, the critical path is native → host-src → host-bundle → desktop-bundle → client-src → client-check, and host-check ends about 30 s before the last step (one earlier sample). `build:check` runs the Host and Client aggregate checks concurrently, so its wall is the Host check's (about 65 s). ### Completeness and lossless evidence -`build` runs the same set of compiler programs as the serial build: host-src and client-src compile every package project of both faces, and host-test and client-test check both aggregate root programs with the same commands as `build:test`. Across the prototype's six rounds, the 11,580 files of `{packages,apps,vendor,native}/**/lib/**` (excluding `*.tsbuildinfo`) are byte-identical between serial and concurrent runs, and after every round `tsc -b --dry tsconfig.host.json tsconfig.client.json` reports no out-of-date project. tsbuildinfo writers are host-src (292), client-src (76, exactly the client-only projects), and one per aggregate check; client-src rewrites no tsbuildinfo or declaration that host-test reads, so the graph has no write race. The fixture tests `checks only the aggregate program in project mode` and `reports missing package declarations without compiling references` show that `tsc -b --dry` treats the tsbuildinfo written by `tsc -p` as up to date and that project mode compiles no reference. In this implementation, the concurrent `build` and `DSH_GATE_CONCURRENCY=1 pnpm run build` produce byte-identical `lib/` (11,584 files), `apps/web/dist` (156 files), and client build record; afterwards `tsc -b --dry` reports all 372 projects of both aggregates up to date, with none to build. +`build` runs the same set of compiler programs as the serial build: host-src and client-src compile every package project of both faces, and host-check and client-check check both aggregate root programs with the same commands as `build:check`. Across the prototype's six rounds, the 11,580 files of `{packages,apps,vendor,native}/**/lib/**` (excluding `*.tsbuildinfo`) are byte-identical between serial and concurrent runs, and after every round `tsc -b --dry tsconfig.host.json tsconfig.client.json` reports no out-of-date project. tsbuildinfo writers are host-src (292), client-src (76, exactly the client-only projects), and one per aggregate check; client-src rewrites no tsbuildinfo or declaration that host-check reads, so the graph has no write race. The fixture tests `checks only the aggregate program in project mode` and `reports missing package declarations without compiling references` show that `tsc -b --dry` treats the tsbuildinfo written by `tsc -p` as up to date and that project mode compiles no reference. In this implementation, the concurrent `build` and `DSH_GATE_CONCURRENCY=1 pnpm run build` produce byte-identical `lib/` (11,584 files), `apps/web/dist` (156 files), and client build record; afterwards `tsc -b --dry` reports all 372 projects of both aggregates up to date, with none to build. -| Error | `build:src` | `build:test` | `build` | `typecheck` | Required CI lanes | +| Error | `build:artifacts` | `build:check` | `build` | `typecheck` | Required CI lanes | |---|---|---|---|---|---| | Package source type error | host-src / client-src | Not reported (reads existing declarations) | host-src / client-src | Reported | Reported | -| Type error only in an aggregate root program's tests, scripts, benchmarks, or website | Not reported | host-test / client-test | host-test / client-test | Reported | consumers, windows-build | +| Type error only in an aggregate root program's tests, scripts, benchmarks, or website | Not reported | host-check / client-check | host-check / client-check | Reported | consumers, windows-build | | Package tsconfig configuration error | host-src / client-src | Not reported (`tsc -p` does not validate referenced projects' tsconfig options or syntax) | host-src / client-src | Reported | Reported | | Typert analysis error | host-bundle | Not reported | host-bundle | Reported (through the Host bundle) | Reported | | Missing prerequisite output | — | Compiler diagnostic (TS6305, TS2307) | — | — | — | -Negative controls: after a type error (TS2322) is added to `scripts/pnpm-invocation.spec.ts`, which only the Host aggregate root program contains, `build:src` exits 0; `build:test` exits 1 with the Host aggregate check failing and the Client aggregate check passing; `build` exits 1 with 8 of 9 steps passing and 1 failing, and writes no client build record. In a scratch fixture whose referenced project's tsconfig has an invalid `target`, a JSON syntax error, or an unknown compiler option, `tsc -p` exits 0, while `tsc -b` reports TS6046, TS5092 with TS1012, and TS5023 respectively and exits 1; when the referenced project lacks `composite`, both report TS6306. +Negative controls: after a type error (TS2322) is added to `scripts/pnpm-invocation.spec.ts`, which only the Host aggregate root program contains, `build:artifacts` exits 0; `build:check` exits 1 with the Host aggregate check failing and the Client aggregate check passing; `build` exits 1 with 8 of 9 steps passing and 1 failing, and writes no client build record. In a scratch fixture whose referenced project's tsconfig has an invalid `target`, a JSON syntax error, or an unknown compiler option, `tsc -p` exits 0, while `tsc -b` reports TS6046, TS5092 with TS1012, and TS5023 respectively and exits 1; when the referenced project lacks `composite`, both report TS6306. CI evidence: on PR #6005 both required lanes that run the concurrent `pnpm run build` passed, `node 24 / snapshots and artifacts` (`check:ci:consumers` on `blacksmith-16vcpu-ubuntu-2404`) and `windows node 24 / build & observational` (`check:ci:windows-blocking` on `blacksmith-16vcpu-windows-2025`); the GitHub-hosted 4 vCPU benchmarks lane does not run `build`. The inner concurrency follows `DSH_GATE_CONCURRENCY`: master serial lanes set 1, the consumers lane sets 10 under self-hosted failover, and an unset value selects the available CPU count, capped at the 9 steps. ### Intentional trade-offs (not lossless) -- **Some commands skip the aggregate checks:** `build:src`, the launch builds of `dev:web` and `dev:desktop`, and the prerequisite steps of `lint`, `lint:fix`, `doc-typecheck`, and the Typert contracts gate do not type-check the two aggregate root programs, and these prerequisite steps do not produce `apps/desktop/lib`. `build`, `build:test`, `typecheck` (pre-push hook), and the two required CI lanes own completeness; `dev:web` and `dev:desktop` do not refuse to launch because of test type errors. -- **`build:test` does not check freshness:** When sources change after `build:src`, `build:test` checks against stale declarations, including Typert Remote declarations; rerun `build:src` first. The complete `build` is unaffected. -- **Concurrent `build` raises the memory peak:** This branch's complete `build` peaks at 7.53 GiB of process-tree RSS against 6.11 GiB for the master serial build (+1.42 GiB, medians of 2) and 6.27 GiB for the branch's serial build (n=1); CPU meanwhile falls from 423.1 s to 374.4 s because [the lossless speed-ups note](2026-10-11-lossless-build-speedups.md) removes more work than concurrent contention adds. In the prototype (without the Typert changes), concurrent scheduling alone raises the RSS peak by about 2.1 GiB (5.9 → 8.0 GiB) and CPU by about 3.7%. `DSH_GATE_CONCURRENCY` bounds it; under memory pressure use `DSH_GATE_CONCURRENCY=1 pnpm run build`, or run `build:src` and `build:test` separately. In the windows-build lane, `build` runs concurrently with `docs:build`. +- **Some commands skip the aggregate checks:** `build:artifacts`, the launch builds of `dev:web` and `dev:desktop`, and the prerequisite steps of `lint`, `lint:fix`, `doc-typecheck`, and the Typert contracts gate do not type-check the two aggregate root programs, and these prerequisite steps do not produce `apps/desktop/lib`. `build`, `build:check`, `typecheck` (pre-push hook), and the two required CI lanes own completeness; `dev:web` and `dev:desktop` do not refuse to launch because of test type errors. +- **`build:check` does not check freshness:** When sources change after `build:artifacts`, `build:check` checks against stale declarations, including Typert Remote declarations; rerun `build:artifacts` first. The complete `build` is unaffected. +- **Concurrent `build` raises the memory peak:** This branch's complete `build` peaks at 7.53 GiB of process-tree RSS against 6.11 GiB for the master serial build (+1.42 GiB, medians of 2) and 6.27 GiB for the branch's serial build (n=1); CPU meanwhile falls from 423.1 s to 374.4 s because [the lossless speed-ups note](2026-10-11-lossless-build-speedups.md) removes more work than concurrent contention adds. In the prototype (without the Typert changes), concurrent scheduling alone raises the RSS peak by about 2.1 GiB (5.9 → 8.0 GiB) and CPU by about 3.7%. `DSH_GATE_CONCURRENCY` bounds it; under memory pressure use `DSH_GATE_CONCURRENCY=1 pnpm run build`, or run `build:artifacts` and `build:check` separately. In the windows-build lane, `build` runs concurrently with `docs:build`. - **Passing steps' output is hidden by default:** Only failed steps print their complete output. -- **`tsc -p` does not skip an up-to-date root program:** An immediate rerun of `build:test` without changes still takes 19.4 s and 33.1 s of CPU because both programs are parsed again; `tsc -b` skips an up-to-date root. -- **On many-core cold builds `build:src` is only 15.5 s faster than `build`:** This branch's cold `build` takes 169.1 s and `build:src` 153.6 s (medians of 2), because host-test is off the critical path and client-test is only about 16 s longer than client-bundle plus web. The rest of `build:src`'s saving is CPU and memory: 374.4 → 231.2 s of CPU and 7.53 → 6.18 GiB of peak RSS; on few-core machines and in incremental rebuilds this also turns into wall time. -- **CI invokes public commands in the root manifest:** PR artifact builds invoke `build:src` rather than an internal helper flag; a PR that edits these root manifest scripts activates the separate Node Addon System platform matrix. +- **`tsc -p` does not skip an up-to-date root program:** An immediate rerun of `build:check` without changes still takes 19.4 s and 33.1 s of CPU because both programs are parsed again; `tsc -b` skips an up-to-date root. +- **On many-core cold builds `build:artifacts` is only 15.5 s faster than `build`:** This branch's cold `build` takes 169.1 s and `build:artifacts` 153.6 s (medians of 2), because host-check is off the critical path and client-check is only about 16 s longer than client-bundle plus web. The rest of `build:artifacts`'s saving is CPU and memory: 374.4 → 231.2 s of CPU and 7.53 → 6.18 GiB of peak RSS; on few-core machines and in incremental rebuilds this also turns into wall time. +- **CI invokes public commands in the root manifest:** PR artifact builds invoke `build:artifacts` rather than an internal helper flag; a PR that edits these root manifest scripts activates the separate Node Addon System platform matrix. ## Alternatives considered - **Keep the aggregate checks only in `typecheck`:** `pnpm run build` must stay complete and detect every error. -- **Keep `--artifacts-only` and add a `build:src` alias:** One operation would have two names. -- **`build:test` runs `tsc -b `:** Build mode compiles out-of-date references, doing `build:src`'s work. -- **`build:test` first completes or validates prerequisite outputs:** It violates each command doing only its own job and adds prerequisite logic; compiler diagnostics already name missing outputs. +- **Keep `--artifacts-only` and add a `build:artifacts` alias:** One operation would have two names. +- **Name the commands `build:src` and `build:test`:** `build:artifacts` produces every artifact, including the tsdown bundles, the Desktop bundle, and Web, not only compiled sources; `build:check` checks scripts, benchmarks, and website code as well as tests. +- **Name the commands after compiler terms (`build:b`/`build:p`, `build:project`, `build:emit`) or `build:dist`:** tsc runs in only two artifact steps, and `-p` is an implementation choice for the check steps; TypeScript calls the `tsc -p` side the project; `emit` reads as the emit-only `--noCheck` mode although package projects stay fully checked; package outputs live under `lib/`, and `dist` suggests release packaging. +- **`build:check` runs `tsc -b `:** Build mode compiles out-of-date references, doing `build:artifacts`'s work. +- **`build:check` first completes or validates prerequisite outputs:** It violates each command doing only its own job and adds prerequisite logic; compiler diagnostics already name missing outputs. - **A new scheduler (shell `&`/`wait`, `concurrently`, turbo/nx):** It duplicates run-gates' failure summary, process-tree termination, and Windows handling. - **Expand `build` into several gates in CI's run-gates:** `pnpm run build` must be a complete command usable directly locally and by release workflows, and it writes the record. -- **Relax the non-data edges in the src chain (native ∥ host-src, desktop-bundle ∥ client-src):** Not measured; the Desktop bundle itself takes only 4.0–4.5 s. +- **Relax the non-data edges in the artifact chain (native ∥ host-src, desktop-bundle ∥ client-src):** Not measured; the Desktop bundle itself takes only 4.0–4.5 s. - **Move tests into a new tsconfig:** The existing aggregates already separate package references from the root program. - **Split the aggregate root programs into sub-programs:** Each sub-program must inject the 242 files carrying module augmentations, CPU grows by 9–57%, and only wall falls (see [the lossless speed-ups note](2026-10-11-lossless-build-speedups.md)). -- **Run `build:test` separately in CI:** The required lanes already get the same checks through `build`; a separate run repeats the work. +- **Run `build:check` separately in CI:** The required lanes already get the same checks through `build`; a separate run repeats the work. ## Consequences -Development iterations can skip the two aggregate root programs with `build:src` (51.6% faster cold than master's `build`) and complete the checks with `build:test` or `build` before declaring work complete. `build` stays complete: the Host aggregate check is off the critical path, the Client aggregate check runs alongside the Client bundle and web, and a cold build is 46.7% faster than master (including the lossless speed-ups note's changes; medians of 2). The costs are a higher memory peak, a little extra CPU from concurrent contention, less passing output by default, and several development and prerequisite commands that do not check test types (see the trade-offs above). +Development iterations can skip the two aggregate root programs with `build:artifacts` (51.6% faster cold than master's `build`) and complete the checks with `build:check` or `build` before declaring work complete. `build` stays complete: the Host aggregate check is off the critical path, the Client aggregate check runs alongside the Client bundle and web, and a cold build is 46.7% faster than master (including the lossless speed-ups note's changes; medians of 2). The costs are a higher memory peak, a little extra CPU from concurrent contention, less passing output by default, and several development and prerequisite commands that do not check test types (see the trade-offs above). Removing or changing `build` in the two required lanes, consumers and windows-build, requires first transferring both aggregate checks to another blocking owner. If Typert later gains its own generation step, the step table adds it and client-src depends on it; command semantics and scheduling stay the same. client-src's dependency on host-bundle guarantees the "Host and Remote artifacts complete before Client compilation" ordering that [Typert Remote method calls](../architecture/2026-08-02-typert-remote-method-calls.md) describes. -Verification: `scripts/build.spec.ts` covers the type-stripping check, rejection of the removed flag and invalid scopes, the conflict between `--profile` and `--scope test`, scope selection, step dependencies, and the environment; `compile-referenced-projects.spec.ts` covers project mode checking only the aggregate program, the diagnostic for missing declarations, and `tsc -b --dry` accepting the tsbuildinfo that `tsc -p` writes; failure propagation, output buffering, and process-tree termination come from the run-gates scheduler and its tests; `run-gates.spec.ts`, `ci-workflow.spec.ts`, `preview-workflow.spec.ts`, `build-exe-for-python-sdk.spec.ts`, `dev-web.spec.ts`, and `tests/ci-release-selfhosted.spec.ts` pin each caller's command. +Verification: `scripts/build.spec.ts` covers the type-stripping check, rejection of the removed flag and invalid scopes, the conflict between `--profile` and `--scope check`, scope selection, step dependencies, and the environment; `compile-referenced-projects.spec.ts` covers project mode checking only the aggregate program, the diagnostic for missing declarations, and `tsc -b --dry` accepting the tsbuildinfo that `tsc -p` writes; failure propagation, output buffering, and process-tree termination come from the run-gates scheduler and its tests; `run-gates.spec.ts`, `ci-workflow.spec.ts`, `preview-workflow.spec.ts`, `build-exe-for-python-sdk.spec.ts`, `dev-web.spec.ts`, and `tests/ci-release-selfhosted.spec.ts` pin each caller's command. diff --git a/.agents/notes/implemented/process/2026-10-11-build-src-test-split.zh.md b/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.zh.md similarity index 54% rename from .agents/notes/implemented/process/2026-10-11-build-src-test-split.zh.md rename to .agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.zh.md index ed7e1f6344..caf81647b4 100644 --- a/.agents/notes/implemented/process/2026-10-11-build-src-test-split.zh.md +++ b/.agents/notes/implemented/process/2026-10-11-build-artifacts-check-split.zh.md @@ -1,8 +1,8 @@ -# Agent Note: 拆分 build:src 与 build:test,build 并发执行两者 +# Agent Note: 拆分 build:artifacts 与 build:check,build 并发执行两者 Status: implemented -[English](2026-10-11-build-src-test-split.md) | 中文 +[English](2026-10-11-build-artifacts-check-split.md) | 中文 ## 问题 @@ -18,11 +18,11 @@ Status: implemented | 命令 | 职责 | 步骤 | 调度 | client build record | |---|---|---|---|---| -| `pnpm run build:src` | 产出 `build` 的全部产物;包 project 照常完整类型检查,不检查两个聚合根程序 | native → host-src → host-bundle → desktop-bundle → client-src → client-bundle → web | 一条线性链 | 开始时删除,全部通过后写入 | -| `pnpm run build:test` | 只类型检查两个聚合根程序 | host-test、client-test | 两步并发,无前置步骤 | 不读写 | -| `pnpm run build` | `build:src` 与 `build:test` 的全部步骤 | 下文依赖图 | run-gates 调度器并发执行 | 开始时删除,全部通过后写入 | +| `pnpm run build:artifacts` | 产出 `build` 的全部产物;包 project 照常完整类型检查,不检查两个聚合根程序 | native → host-src → host-bundle → desktop-bundle → client-src → client-bundle → web | 一条线性链 | 开始时删除,全部通过后写入 | +| `pnpm run build:check` | 只类型检查两个聚合根程序 | host-check、client-check | 两步并发,无前置步骤 | 不读写 | +| `pnpm run build` | `build:artifacts` 与 `build:check` 的全部步骤 | 下文依赖图 | run-gates 调度器并发执行 | 开始时删除,全部通过后写入 | -`build:test` 不检测、不重建、也不校验 `build:src` 的产物;前置产物缺失或过期时,由编译器自身的诊断(例如 TS6305、TS2307)报告,任何命令都不增加前置检查、守卫或回退逻辑。`build:src` 产出的产物与 record 和 `build` 相同,并取代 `--artifacts-only`;该参数与 `compile-referenced-projects.ts` 的 `libraries` 模式已删除,Python executable builder 使用 `--build-src`。`build:official` 仍是 `tsx scripts/build.ts --profile official`,执行完整的 `build`。 +`build:check` 不检测、不重建、也不校验 `build:artifacts` 的产物;前置产物缺失或过期时,由编译器自身的诊断(例如 TS6305、TS2307)报告,任何命令都不增加前置检查、守卫或回退逻辑。`build:artifacts` 产出的产物与 record 和 `build` 相同,并取代 `--artifacts-only`;该参数与 `compile-referenced-projects.ts` 的 `libraries` 模式已删除,Python executable builder 使用 `--build-artifacts`。`build:official` 仍是 `tsx scripts/build.ts --profile official`,执行完整的 `build`。 ### 步骤与依赖 @@ -35,24 +35,24 @@ Status: implemented | client-src | ` -b ` | desktop-bundle | | client-bundle | `tsdown --config-loader native --env.DSH_BUILD_FACE client` | client-src | | web | `pnpm run build:web` | client-bundle | -| host-test | `node --max-old-space-size=4096 -p tsconfig.host.json` | host-src | -| client-test | ` -p tsconfig.client.json` | client-src | +| host-check | `node --max-old-space-size=4096 -p tsconfig.host.json` | host-src | +| client-check | ` -p tsconfig.client.json` | client-src | ```text -native → host-src ─┬─ host-test ─────────────────────────────────────────────┐ - └─ host-bundle → desktop-bundle → client-src ─┬─ client-test ┤ → record +native → host-src ─┬─ host-check ───────────────────────────────────────────────────────┐ + └─ host-bundle → desktop-bundle → client-src ─┬─ client-check ───────┤ → record └─ client-bundle → web ┘ ``` -- client-src 必须在 host-bundle 之后:Client 包导入 `@deepseek-ai/dsh-*/remote`,其类型 `lib/typert.remote-client.d.ts` 只由 Host tsdown 中的 Typert 写出。host-test 不读取 Typert 输出,干净的串行构建一直在 Host tsdown 之前完成 Host 聚合检查。 -- src 链保留 native → host-src 与 desktop-bundle → client-src 这两条非数据依赖;只有两个 test 步骤并行挂接到链上。 +- client-src 必须在 host-bundle 之后:Client 包导入 `@deepseek-ai/dsh-*/remote`,其类型 `lib/typert.remote-client.d.ts` 只由 Host tsdown 中的 Typert 写出。host-check 不读取 Typert 输出,干净的串行构建一直在 Host tsdown 之前完成 Host 聚合检查。 +- 产物链保留 native → host-src 与 desktop-bundle → client-src 这两条非数据依赖;只有两个 check 步骤并行挂接到链上。 - [`compile-referenced-projects.ts`](../../../../scripts/compile-referenced-projects.ts) 拥有包库步骤列表 `libraryBuildSteps`、references 参数构造 `referencedProjectsCompilerArgs`(相对路径传参,避开 Windows 命令行长度上限)与聚合检查参数 `aggregateCheckArgs`。vendor 发布演练(`host-libraries`)与 CI benchmark(`--noCheck`)串行执行同一步骤列表。 -- test 步骤使用 project mode(`tsc -p`):它读取 references 已有的声明而不编译任何 reference,incremental 状态写入聚合配置已有的 `tsconfig.{host,client}.tsbuildinfo`。`build` 与 `build:test` 使用同一条命令,因此诊断相同。 +- check 步骤使用 project mode(`tsc -p`):它读取 references 已有的声明而不编译任何 reference,incremental 状态写入聚合配置已有的 `tsconfig.{host,client}.tsbuildinfo`。`build` 与 `build:check` 使用同一条命令,因此诊断相同。 ### 调度、失败与输出 -- [`scripts/build.ts`](../../../../scripts/build.ts) 拥有步骤表与三个范围,执行复用 [`scripts/run-gates.ts`](../../../../scripts/run-gates.ts) 的 `runAggregate`:每个步骤是一个带 `needs` 的 gate,shell-free 启动、按步骤缓冲输出、fail-fast 进程树终止、Windows `taskkill` 与宿主信号转发都沿用 run-gates 的实现。`build:src` 为 `tsx scripts/build.ts --scope src`,`build:test` 为 `--scope test`,`build` 不带 `--scope`,把两者作为一张图执行;`--profile` 只适用于产生产物的范围,与 `--scope test` 同用时参数解析失败。 -- 并发上限取 `DSH_GATE_CONCURRENCY`,未设置时为可用 CPU 数;依赖图宽度最多为 3(host-test 尚未结束时,client-test 与 client-bundle 可能已开始)。master 串行参考 lane 设置 `DSH_GATE_CONCURRENCY=1`,`build` 在那里按拓扑顺序串行执行,符合[串行参考决策](2026-07-21-serial-cross-platform-ci-reference.zh.md),无需修改 workflow。 +- [`scripts/build.ts`](../../../../scripts/build.ts) 拥有步骤表与三个范围,执行复用 [`scripts/run-gates.ts`](../../../../scripts/run-gates.ts) 的 `runAggregate`:每个步骤是一个带 `needs` 的 gate,shell-free 启动、按步骤缓冲输出、fail-fast 进程树终止、Windows `taskkill` 与宿主信号转发都沿用 run-gates 的实现。`build:artifacts` 为 `tsx scripts/build.ts --scope artifacts`,`build:check` 为 `--scope check`,`build` 不带 `--scope`,把两者作为一张图执行;`--profile` 只适用于产生产物的范围,与 `--scope check` 同用时参数解析失败。 +- 并发上限取 `DSH_GATE_CONCURRENCY`,未设置时为可用 CPU 数;依赖图宽度最多为 3(host-check 尚未结束时,client-check 与 client-bundle 可能已开始)。master 串行参考 lane 设置 `DSH_GATE_CONCURRENCY=1`,`build` 在那里按拓扑顺序串行执行,符合[串行参考决策](2026-07-21-serial-cross-platform-ci-reference.zh.md),无需修改 workflow。 - 缺省(本地)时,失败步骤的所有依赖方记为 skipped,正在运行的步骤和与失败无关的步骤继续运行到结束;执行集合只取决于各步骤的结果,因此一次运行报告所有相互独立的类型错误。`DSH_GATE_FAIL_FAST=1`(所有构建型 PR lane 已设置)时,第一个失败终止其余正在运行步骤的进程树,所有未完成步骤记为 skipped。 - 失败步骤打印 `== FAILED <步骤> ==`、命令、退出原因与该步骤完整的缓冲输出,结尾汇总列出 failed 与 skipped 步骤;成功步骤只打印 PASS 行,`DSH_GATE_VERBOSE=1` 打印全部输出。任一步骤 failed 或 skipped 时退出码为 1,且不写 client build record。所有步骤继承 `clientBuildProcessEnvironment` 选定的公开 Client 环境。 - CI 中 `build` 本身是外层 run-gates 的一个 gate;外层 fail-fast 终止会转发到内层调度。`check:node-compat` 与 `check:ci:lint:contracts-ready` 已使用同样的嵌套。 @@ -64,20 +64,20 @@ native → host-src ─┬─ host-test ───────────── | `build`、`build:official`、`Makefile` `build`、`scripts/publish-npm-baseline.ts`、`apps/desktop/scripts/package-target.ts`、`scripts/dev-bundle-registry.ts`、`release-publish.yml`、`sandbox.yml`、`e2e.yml` 与 `release.yml` 的非 PR 分支 | `build` / `build:official` | 有 | | `test:web`、`test:web:refresh`、`test:web:perf`、`test:web:stress`、`apps/desktop` `test:updates:local`、`test:bench` / `build:bench` | `build`;`build:bench` 运行 `build:native-system`、`build:lib` 与 benchmark tsdown | 有 | | `typecheck`(pre-push hook 运行它) | `build:lib:host`(Host `tsc -b tsconfig.host.json`、Host bundle、Desktop bundle)与 Client `tsc -b tsconfig.client.json` | 有 | -| `dev:web`([`scripts/dev-web.ts`](../../../../scripts/dev-web.ts))、`dev:desktop`(`apps/desktop/scripts/dev.ts`) | `build:src` | 启动前无;`dev:web` 启动后的 `tsc -b tsconfig.client.json --watch` 持续检查 Client 聚合根程序 | +| `dev:web`([`scripts/dev-web.ts`](../../../../scripts/dev-web.ts))、`dev:desktop`(`apps/desktop/scripts/dev.ts`) | `build:artifacts` | 启动前无;`dev:web` 启动后的 `tsc -b tsconfig.client.json --watch` 持续检查 Client 聚合根程序 | | `lint`、`lint:fix`、`doc-typecheck` 与 CI 的 Typert contracts gate 的前置步骤 | Host 包编译与 Host bundle(`compile-referenced-projects.ts host-libraries`),不含 Desktop bundle | 无 | | CI node 24 / snapshots and artifacts(`check:ci:consumers`)、windows node 24 / build & observational(`check:ci:windows-blocking`) | `build` | 有,两个必需 lane | -| CI node-compat 22.19 | `build:src`(经 run-gates) | 无 | -| CI python runtime | `build-exe-for-python-sdk.ts --build-src` | 无 | -| `e2e.yml`、`release.yml` 的 PR 演练 | `pnpm run build:src --profile official` | 无 | -| `build-preview-cloudflare.yml` | `pnpm run build:src` | 无 | +| CI node-compat 22.19 | `build:artifacts`(经 run-gates) | 无 | +| CI python runtime | `build-exe-for-python-sdk.ts --build-artifacts` | 无 | +| `e2e.yml`、`release.yml` 的 PR 演练 | `pnpm run build:artifacts --profile official` | 无 | +| `build-preview-cloudflare.yml` | `pnpm run build:artifacts` | 无 | | CI node 24 / benchmarks | `run-ci-bench.ts`(`tsc -b --noCheck`) | 无 | | `release-vendor.yml` 的 PR 演练 | `compile-referenced-projects.ts host-libraries` | 无 | | `release-vendor.yml` 的非 PR 分支、`release-vendor-publish.yml` | `build:lib:host` | 仅 Host 聚合根程序 | `lint`、`lint:fix`、`doc-typecheck` 与 Typert contracts gate 只读取 Typert 输出与包声明:两个聚合配置都是 `noEmit`,oxlint 自行构建程序,`doc-typecheck` 只引用包 project,`host-libraries` 的消费方都不读取 `apps/desktop/lib`。它们的前置步骤因此跳过 Host 聚合检查与 Desktop bundle,耗时从 159.8 s 降到 90.9 s(见下文测量)。 -consumers lane 的 HMR Web 测试启动 `dev:web`,因此在该 lane 内使用 `build:src`;同一 lane 的 `build` gate 仍运行两个聚合检查。CI 不单独调用 `build:test`:两个必需 lane 已通过 `build` 获得同一组检查。[PR 产物构建决策](2026-10-05-pr-artifact-typechecks.zh.md)的必需完整构建、benchmark `--noCheck`、job 与 worker 上限、node-compat 串行 Vitest、覆盖率权重与 pnpm 打包检查仍然有效;其中的 PR 产物构建由 `build:src` 执行。 +consumers lane 的 HMR Web 测试启动 `dev:web`,因此在该 lane 内使用 `build:artifacts`;同一 lane 的 `build` gate 仍运行两个聚合检查。CI 不单独调用 `build:check`:两个必需 lane 已通过 `build` 获得同一组检查。[PR 产物构建决策](2026-10-05-pr-artifact-typechecks.zh.md)的必需完整构建、benchmark `--noCheck`、job 与 worker 上限、node-compat 串行 Vitest、覆盖率权重与 pnpm 打包检查仍然有效;其中的 PR 产物构建由 `build:artifacts` 执行。 ### 测量 @@ -89,7 +89,7 @@ consumers lane 的 HMR Web 测试启动 `dev:web`,因此在该 lane 内使用 | 各步骤依次单独运行 | 255.1 | 373.8 | 5.82 | | 按依赖图并发 | 172.1 | 392.1 | 8.00 | -wall 中位数从 249.4 s 降到 172.1 s(−77.3 s,−31%)。各步骤单独运行的两次样本相差 28 s,因此把步骤拆成独立进程的开销无法分辨。CPU 增加约 14 s(+3.7%),主要是争用下 Host 聚合检查增加的 user 时间;RSS 峰值增加约 2.1 GiB,出现在 Host 聚合检查(约 4 GiB)与 Host tsdown(约 5.7 GiB)重叠时。并发时的关键路径是 host-src → host-bundle → desktop-bundle → client-src → client-test;host-test 在结束前约 33 s 完成,client-bundle 在结束前约 21 s 完成。 +wall 中位数从 249.4 s 降到 172.1 s(−77.3 s,−31%)。各步骤单独运行的两次样本相差 28 s,因此把步骤拆成独立进程的开销无法分辨。CPU 增加约 14 s(+3.7%),主要是争用下 Host 聚合检查增加的 user 时间;RSS 峰值增加约 2.1 GiB,出现在 Host 聚合检查(约 4 GiB)与 Host tsdown(约 5.7 GiB)重叠时。并发时的关键路径是 host-src → host-bundle → desktop-bundle → client-src → client-check;host-check 在结束前约 33 s 完成,client-bundle 在结束前约 21 s 完成。 最终测量:master(合并基点)与本分支交替运行,每次均从 clean 开始,在 64 核 cgroup 中运行;除标注 n=1 者外,数值均为 2 个样本的中位数。 @@ -97,59 +97,61 @@ wall 中位数从 249.4 s 降到 172.1 s(−77.3 s,−31%)。各步骤单 |---|---|---|---|---| | 完整构建 `pnpm run build` | 317.1 s | 169.1 s(−46.7%) | 423.1 → 374.4 s(−11.5%) | 6.11 → 7.53 GiB(+1.42) | | 本分支串行构建 `DSH_GATE_CONCURRENCY=1 pnpm run build`(n=1) | — | 266.7 s | 389.5 s | 6.27 GiB | -| 产物构建:master `build --artifacts-only`,本分支 `build:src` | 185.8 s | 153.6 s(−17.3%) | 256.2 → 231.2 s | 6.58 → 6.18 GiB | -| 开发迭代:master `build`,本分支 `build:src` | 317.1 s | 153.6 s(−51.6%) | 423.1 → 231.2 s(−45.4%) | 6.11 → 6.18 GiB | -| `pnpm run build:test`(`build:src` 之后) | — | 67.7 s | 144.6 s | 5.33 GiB | -| `pnpm run build:test` 无改动重跑 | — | 19.4 s | 33.1 s | 未测量 | +| 产物构建:master `build --artifacts-only`,本分支 `build:artifacts` | 185.8 s | 153.6 s(−17.3%) | 256.2 → 231.2 s | 6.58 → 6.18 GiB | +| 开发迭代:master `build`,本分支 `build:artifacts` | 317.1 s | 153.6 s(−51.6%) | 423.1 → 231.2 s(−45.4%) | 6.11 → 6.18 GiB | +| `pnpm run build:check`(`build:artifacts` 之后) | — | 67.7 s | 144.6 s | 5.33 GiB | +| `pnpm run build:check` 无改动重跑 | — | 19.4 s | 33.1 s | 未测量 | | `lint` 与 `doc-typecheck` 的前置步骤:master `build:lib:host`,本分支 `host-libraries` | 159.8 s | 90.9 s(−43.2%) | 234.7 → 136.0 s | 未测量 | -本分支串行样本运行时主机上有其他工作(外来 CPU 742 s),因此它的 wall 可能高估串行成本。串行构建的各段中(master → 本分支,各一个样本),Host tsdown 从 22.5 s 降到 11.7 s,Client 包编译从 55.6 s 降到 48.7 s,Client 聚合检查从 68.2 s 降到 35.9 s,Client tsdown 从 13.9 s 降到 10.5 s;各段合计分别为 308.5 s 与 254.9 s。这些缩短来自[无损提速 Note](2026-10-11-lossless-build-speedups.zh.md);从 254.9 s 的串行各段合计到 169.1 s 的并发构建,其余缩短来自本 Note 的调度,它把 Host 聚合检查(约 66 s)移出关键路径。并发构建的关键路径是 native → host-src → host-bundle → desktop-bundle → client-src → client-test,host-test 在最后一步结束前约 30 s 完成(取自更早的一个样本)。`build:test` 并发运行 Host 与 Client 聚合检查,因此其 wall 等于 Host 聚合检查的 wall(约 65 s)。 +本分支串行样本运行时主机上有其他工作(外来 CPU 742 s),因此它的 wall 可能高估串行成本。串行构建的各段中(master → 本分支,各一个样本),Host tsdown 从 22.5 s 降到 11.7 s,Client 包编译从 55.6 s 降到 48.7 s,Client 聚合检查从 68.2 s 降到 35.9 s,Client tsdown 从 13.9 s 降到 10.5 s;各段合计分别为 308.5 s 与 254.9 s。这些缩短来自[无损提速 Note](2026-10-11-lossless-build-speedups.zh.md);从 254.9 s 的串行各段合计到 169.1 s 的并发构建,其余缩短来自本 Note 的调度,它把 Host 聚合检查(约 66 s)移出关键路径。并发构建的关键路径是 native → host-src → host-bundle → desktop-bundle → client-src → client-check,host-check 在最后一步结束前约 30 s 完成(取自更早的一个样本)。`build:check` 并发运行 Host 与 Client 聚合检查,因此其 wall 等于 Host 聚合检查的 wall(约 65 s)。 ### 完整性与无损依据 -`build` 运行的编译器程序集合与串行构建相同:host-src 与 client-src 编译两个 face 的全部包 project,host-test 与 client-test 检查两个聚合根程序,且使用与 `build:test` 相同的命令。原型的 6 轮运行中,`{packages,apps,vendor,native}/**/lib/**`(排除 `*.tsbuildinfo`)的 11,580 个文件在串行与并发之间逐字节一致,每轮之后 `tsc -b --dry tsconfig.host.json tsconfig.client.json` 报告没有过期 project。tsbuildinfo 写入方归属为:host-src 292 个,client-src 76 个(恰好是 client-only project),两个聚合检查各 1 个;client-src 不改写 host-test 读取的任何 tsbuildinfo 或声明,因此该依赖图中不存在写入竞争。fixture 测试 `checks only the aggregate program in project mode` 表明 `tsc -p` 写出的 tsbuildinfo 被 `tsc -b --dry` 视为最新,`reports missing package declarations without compiling references` 表明 project mode 不编译 references。本实现中,并发 `build` 与 `DSH_GATE_CONCURRENCY=1 pnpm run build` 产出逐字节一致的 `lib/`(11,584 个文件)、`apps/web/dist`(156 个文件)与 client build record;之后 `tsc -b --dry` 对两个聚合配置报告 372 个 project 全部为最新,没有需要构建的 project。 +`build` 运行的编译器程序集合与串行构建相同:host-src 与 client-src 编译两个 face 的全部包 project,host-check 与 client-check 检查两个聚合根程序,且使用与 `build:check` 相同的命令。原型的 6 轮运行中,`{packages,apps,vendor,native}/**/lib/**`(排除 `*.tsbuildinfo`)的 11,580 个文件在串行与并发之间逐字节一致,每轮之后 `tsc -b --dry tsconfig.host.json tsconfig.client.json` 报告没有过期 project。tsbuildinfo 写入方归属为:host-src 292 个,client-src 76 个(恰好是 client-only project),两个聚合检查各 1 个;client-src 不改写 host-check 读取的任何 tsbuildinfo 或声明,因此该依赖图中不存在写入竞争。fixture 测试 `checks only the aggregate program in project mode` 表明 `tsc -p` 写出的 tsbuildinfo 被 `tsc -b --dry` 视为最新,`reports missing package declarations without compiling references` 表明 project mode 不编译 references。本实现中,并发 `build` 与 `DSH_GATE_CONCURRENCY=1 pnpm run build` 产出逐字节一致的 `lib/`(11,584 个文件)、`apps/web/dist`(156 个文件)与 client build record;之后 `tsc -b --dry` 对两个聚合配置报告 372 个 project 全部为最新,没有需要构建的 project。 -| 错误 | `build:src` | `build:test` | `build` | `typecheck` | 必需 CI lane | +| 错误 | `build:artifacts` | `build:check` | `build` | `typecheck` | 必需 CI lane | |---|---|---|---|---|---| | 包源码类型错误 | host-src / client-src | 不报告(读取已有声明) | host-src / client-src | 报告 | 报告 | -| 只属于聚合根程序的测试、脚本、benchmark、website 类型错误 | 不报告 | host-test / client-test | host-test / client-test | 报告 | consumers、windows-build | +| 只属于聚合根程序的测试、脚本、benchmark、website 类型错误 | 不报告 | host-check / client-check | host-check / client-check | 报告 | consumers、windows-build | | 包 tsconfig 配置错误 | host-src / client-src | 不报告(`tsc -p` 不校验被引用 project 的 tsconfig 选项与语法) | host-src / client-src | 报告 | 报告 | | Typert 分析错误 | host-bundle | 不报告 | host-bundle | 报告(经 Host bundle) | 报告 | | 前置产物缺失 | — | 编译器诊断(TS6305、TS2307) | — | — | — | -负对照:在只属于 Host 聚合根程序的 `scripts/pnpm-invocation.spec.ts` 中加入类型错误(TS2322)后,`build:src` 以 0 退出;`build:test` 以 1 退出,其中 Host 聚合检查失败、Client 聚合检查通过;`build` 以 1 退出,9 个步骤中 8 个通过、1 个失败,且不写 client build record。在临时 fixture 中,被引用 project 的 tsconfig 含无效 `target`、JSON 语法错误或未知编译选项时,`tsc -p` 以 0 退出,`tsc -b` 分别报告 TS6046、TS5092 与 TS1012、TS5023 并以 1 退出;被引用 project 缺少 `composite` 时,两者都报告 TS6306。 +负对照:在只属于 Host 聚合根程序的 `scripts/pnpm-invocation.spec.ts` 中加入类型错误(TS2322)后,`build:artifacts` 以 0 退出;`build:check` 以 1 退出,其中 Host 聚合检查失败、Client 聚合检查通过;`build` 以 1 退出,9 个步骤中 8 个通过、1 个失败,且不写 client build record。在临时 fixture 中,被引用 project 的 tsconfig 含无效 `target`、JSON 语法错误或未知编译选项时,`tsc -p` 以 0 退出,`tsc -b` 分别报告 TS6046、TS5092 与 TS1012、TS5023 并以 1 退出;被引用 project 缺少 `composite` 时,两者都报告 TS6306。 CI 证据:PR #6005 上,两个运行并发 `pnpm run build` 的必需 lane 均通过:`node 24 / snapshots and artifacts`(`check:ci:consumers`,运行于 `blacksmith-16vcpu-ubuntu-2404`)与 `windows node 24 / build & observational`(`check:ci:windows-blocking`,运行于 `blacksmith-16vcpu-windows-2025`);GitHub 托管的 4 vCPU benchmarks lane 不运行 `build`。内层并发取 `DSH_GATE_CONCURRENCY`:master 串行 lane 设为 1,consumers lane 在 self-hosted failover 时设为 10,未设置时取可用 CPU 数,且不超过 9 个步骤。 ### 有意的取舍(非无损) -- **部分命令跳过聚合检查:** `build:src`、`dev:web` 与 `dev:desktop` 的启动构建,以及 `lint`、`lint:fix`、`doc-typecheck` 与 Typert contracts gate 的前置步骤,都不类型检查两个聚合根程序,这些前置步骤也不产出 `apps/desktop/lib`。完整性由 `build`、`build:test`、`typecheck`(pre-push hook)与两个必需 CI lane 负责;`dev:web` 与 `dev:desktop` 不会因测试类型错误而拒绝启动。 -- **`build:test` 不检查新鲜度:** 在 `build:src` 之后修改源码时,`build:test` 基于旧声明(包括 Typert Remote 声明)检查;需要先重跑 `build:src`。完整 `build` 不受影响。 -- **并发 `build` 提高内存峰值:** 本分支完整 `build` 的进程树 RSS 峰值为 7.53 GiB,master 串行构建为 6.11 GiB(+1.42 GiB,2 个样本的中位数),本分支串行构建为 6.27 GiB(n=1);同时 CPU 从 423.1 s 降到 374.4 s,因为[无损提速 Note](2026-10-11-lossless-build-speedups.zh.md) 省去的工作多于并发争用增加的工作。原型(不含 Typert 改动)中,单独的并发调度使 RSS 峰值增加约 2.1 GiB(5.9 → 8.0 GiB)、CPU 增加约 3.7%。上限由 `DSH_GATE_CONCURRENCY` 控制;内存紧张时使用 `DSH_GATE_CONCURRENCY=1 pnpm run build`,或分别运行 `build:src` 与 `build:test`。windows-build lane 中 `build` 与 `docs:build` 同时运行。 +- **部分命令跳过聚合检查:** `build:artifacts`、`dev:web` 与 `dev:desktop` 的启动构建,以及 `lint`、`lint:fix`、`doc-typecheck` 与 Typert contracts gate 的前置步骤,都不类型检查两个聚合根程序,这些前置步骤也不产出 `apps/desktop/lib`。完整性由 `build`、`build:check`、`typecheck`(pre-push hook)与两个必需 CI lane 负责;`dev:web` 与 `dev:desktop` 不会因测试类型错误而拒绝启动。 +- **`build:check` 不检查新鲜度:** 在 `build:artifacts` 之后修改源码时,`build:check` 基于旧声明(包括 Typert Remote 声明)检查;需要先重跑 `build:artifacts`。完整 `build` 不受影响。 +- **并发 `build` 提高内存峰值:** 本分支完整 `build` 的进程树 RSS 峰值为 7.53 GiB,master 串行构建为 6.11 GiB(+1.42 GiB,2 个样本的中位数),本分支串行构建为 6.27 GiB(n=1);同时 CPU 从 423.1 s 降到 374.4 s,因为[无损提速 Note](2026-10-11-lossless-build-speedups.zh.md) 省去的工作多于并发争用增加的工作。原型(不含 Typert 改动)中,单独的并发调度使 RSS 峰值增加约 2.1 GiB(5.9 → 8.0 GiB)、CPU 增加约 3.7%。上限由 `DSH_GATE_CONCURRENCY` 控制;内存紧张时使用 `DSH_GATE_CONCURRENCY=1 pnpm run build`,或分别运行 `build:artifacts` 与 `build:check`。windows-build lane 中 `build` 与 `docs:build` 同时运行。 - **成功步骤的输出默认不显示:** 只有失败步骤打印完整输出。 -- **`tsc -p` 不跳过已是最新的根程序:** 无改动时立即重跑 `build:test` 仍需 19.4 s 与 33.1 s CPU,因为两个程序都要重新解析;`tsc -b` 会跳过已是最新的根。 -- **多核冷启动时 `build:src` 只比 `build` 快 15.5 s:** 本分支冷启动 `build` 为 169.1 s,`build:src` 为 153.6 s(2 个样本的中位数),因为 host-test 不在关键路径上,client-test 只比 client-bundle 与 web 之和长约 16 s。`build:src` 的其余收益是 CPU 与内存:CPU 从 374.4 s 降到 231.2 s,RSS 峰值从 7.53 GiB 降到 6.18 GiB;在少核机器与增量重建中,这部分也转化为 wall。 -- **CI 调用根 manifest 中的公开命令:** PR 产物构建调用 `build:src`,而不是内部 helper 参数;修改根 manifest 中这些 script 的 PR 会激活独立的 Node Addon System 平台矩阵。 +- **`tsc -p` 不跳过已是最新的根程序:** 无改动时立即重跑 `build:check` 仍需 19.4 s 与 33.1 s CPU,因为两个程序都要重新解析;`tsc -b` 会跳过已是最新的根。 +- **多核冷启动时 `build:artifacts` 只比 `build` 快 15.5 s:** 本分支冷启动 `build` 为 169.1 s,`build:artifacts` 为 153.6 s(2 个样本的中位数),因为 host-check 不在关键路径上,client-check 只比 client-bundle 与 web 之和长约 16 s。`build:artifacts` 的其余收益是 CPU 与内存:CPU 从 374.4 s 降到 231.2 s,RSS 峰值从 7.53 GiB 降到 6.18 GiB;在少核机器与增量重建中,这部分也转化为 wall。 +- **CI 调用根 manifest 中的公开命令:** PR 产物构建调用 `build:artifacts`,而不是内部 helper 参数;修改根 manifest 中这些 script 的 PR 会激活独立的 Node Addon System 平台矩阵。 ## 考虑过的替代方案 - **只在 `typecheck` 中保留聚合检查:** `pnpm run build` 必须完整,能检测出所有错误。 -- **保留 `--artifacts-only` 并新增 `build:src` 别名:** 同一件事有两个名字。 -- **`build:test` 使用 `tsc -b `:** build mode 会编译过期的 references,即执行 `build:src` 的工作。 -- **`build:test` 先补齐或校验前置产物:** 违背每个命令只做自己的工作,并增加前置逻辑;编译器诊断已经指明缺失的输出。 +- **保留 `--artifacts-only` 并新增 `build:artifacts` 别名:** 同一件事有两个名字。 +- **命名为 `build:src` 与 `build:test`:** `build:artifacts` 产出全部产物,包括 tsdown bundle、Desktop bundle 与 Web,而不只是编译后的源码;`build:check` 除测试外还检查脚本、benchmarks 与 website 代码。 +- **按编译器术语命名(`build:b`/`build:p`、`build:project`、`build:emit`)或命名为 `build:dist`:** tsc 只出现在两个产物步骤中,`-p` 只是 check 步骤的实现选择;TypeScript 把 `tsc -p` 一侧称为 project;`emit` 容易被理解为只 emit 的 `--noCheck` 模式,而包 project 仍完整检查;包产物位于 `lib/`,`dist` 则让人联想到发布打包。 +- **`build:check` 使用 `tsc -b `:** build mode 会编译过期的 references,即执行 `build:artifacts` 的工作。 +- **`build:check` 先补齐或校验前置产物:** 违背每个命令只做自己的工作,并增加前置逻辑;编译器诊断已经指明缺失的输出。 - **新的调度实现(shell `&`/`wait`、`concurrently`、turbo/nx):** 重复 run-gates 已有的失败汇总、进程树终止与 Windows 处理。 - **在 CI 的 run-gates 中把 `build` 展开为多个 gate:** `pnpm run build` 必须是本地与 release workflow 可直接使用的完整命令,record 也由它写入。 -- **放宽 src 链内的非数据依赖(native ∥ host-src、desktop-bundle ∥ client-src):** 未测量;Desktop bundle 本身只占 4.0–4.5 s。 +- **放宽产物链内的非数据依赖(native ∥ host-src、desktop-bundle ∥ client-src):** 未测量;Desktop bundle 本身只占 4.0–4.5 s。 - **把测试移入新的 tsconfig:** 现有聚合配置已经把包 references 与根程序分开。 - **把聚合根程序拆成多个子程序:** 每个子程序都要注入 242 个含模块增强的文件,CPU 增加 9–57%,只降低 wall(见[无损提速 Note](2026-10-11-lossless-build-speedups.zh.md))。 -- **CI 单独运行 `build:test`:** 必需 lane 已通过 `build` 获得同一组检查,再单独运行是重复工作。 +- **CI 单独运行 `build:check`:** 必需 lane 已通过 `build` 获得同一组检查,再单独运行是重复工作。 ## 后果 -开发迭代可以用 `build:src` 跳过两个聚合根程序(冷启动比 master 的 `build` 快 51.6%),在声明完成前用 `build:test` 或 `build` 补齐检查。`build` 保持完整:Host 聚合检查不在关键路径上,Client 聚合检查与 Client bundle、web 并行,冷启动比 master 快 46.7%(含无损提速 Note 的改动,2 个样本的中位数)。代价是更高的内存峰值、并发争用带来的少量额外 CPU、默认更少的成功输出,以及多个开发与前置命令不检查测试类型(见上文取舍)。 +开发迭代可以用 `build:artifacts` 跳过两个聚合根程序(冷启动比 master 的 `build` 快 51.6%),在声明完成前用 `build:check` 或 `build` 补齐检查。`build` 保持完整:Host 聚合检查不在关键路径上,Client 聚合检查与 Client bundle、web 并行,冷启动比 master 快 46.7%(含无损提速 Note 的改动,2 个样本的中位数)。代价是更高的内存峰值、并发争用带来的少量额外 CPU、默认更少的成功输出,以及多个开发与前置命令不检查测试类型(见上文取舍)。 删除或改变 consumers 与 windows-build 两个必需 lane 的 `build`,必须先把两个聚合检查转交另一处阻塞型 owner。若 Typert 以后获得独立的生成步骤,步骤表新增该步骤,client-src 改为依赖它;命令语义与调度不变。 [Typert Remote 方法调用](../architecture/2026-08-02-typert-remote-method-calls.zh.md)描述的“Client 编译前先完成 Host 与 Remote 产物”由 client-src 对 host-bundle 的依赖保证。 -验证:`scripts/build.spec.ts` 覆盖类型剥离检查、已删除参数与非法范围的拒绝、`--profile` 与 `--scope test` 的冲突、范围选择、步骤依赖与环境;`compile-referenced-projects.spec.ts` 覆盖 project mode 只检查聚合程序、缺失声明的诊断,以及 `tsc -b --dry` 接受 `tsc -p` 写出的 tsbuildinfo;失败传播、输出缓冲与进程树终止沿用 run-gates 的调度实现及其测试;`run-gates.spec.ts`、`ci-workflow.spec.ts`、`preview-workflow.spec.ts`、`build-exe-for-python-sdk.spec.ts`、`dev-web.spec.ts` 与 `tests/ci-release-selfhosted.spec.ts` 固定各调用方的命令。 +验证:`scripts/build.spec.ts` 覆盖类型剥离检查、已删除参数与非法范围的拒绝、`--profile` 与 `--scope check` 的冲突、范围选择、步骤依赖与环境;`compile-referenced-projects.spec.ts` 覆盖 project mode 只检查聚合程序、缺失声明的诊断,以及 `tsc -b --dry` 接受 `tsc -p` 写出的 tsbuildinfo;失败传播、输出缓冲与进程树终止沿用 run-gates 的调度实现及其测试;`run-gates.spec.ts`、`ci-workflow.spec.ts`、`preview-workflow.spec.ts`、`build-exe-for-python-sdk.spec.ts`、`dev-web.spec.ts` 与 `tests/ci-release-selfhosted.spec.ts` 固定各调用方的命令。 diff --git a/.agents/notes/implemented/process/2026-10-11-build-src-test-split.i18n.yaml b/.agents/notes/implemented/process/2026-10-11-build-src-test-split.i18n.yaml deleted file mode 100644 index 8a1cab5a29..0000000000 --- a/.agents/notes/implemented/process/2026-10-11-build-src-test-split.i18n.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# Bilingual-pair consistency record for 2026-10-11-build-src-test-split.md (docs/i18n/README.md): per heading -# section, a hash of its English and Chinese blocks outside code blocks and generated regions. -# After editing either side, bring the other along and re-record with: -# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-10-11-build-src-test-split.md -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently: - en: db08cc8c6fbc40cc - zh: 740e25dede205595 -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/problem: - en: bc382ad1f1e71352 - zh: 44189bf0f69a0bf7 -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision: - en: a15c7c13f167e5ac - zh: 63bcc2ef60c2f344 -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/commands: - en: b8e78d0f04961757 - zh: 6d1603267f713dd4 -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/steps-and-dependencies: - en: 28407fff5f754c70 - zh: 8d116fb32322317e -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/scheduling-failure-and-output: - en: 5872f92bf06cb776 - zh: 274fcfbd971d5c5b -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/callers: - en: 932a69653dff2a17 - zh: 7ceac59cb9abc6f9 -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/measurements: - en: 53f9a558836c917c - zh: b372b301e5a9f23b -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/completeness-and-lossless-evidence: - en: 2d03c309e077c0de - zh: a8ce74fbc67fff6c -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/decision/intentional-trade-offs-not-lossless: - en: 6a6229e5c7fbda91 - zh: e38c0596e0fe184c -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/alternatives-considered: - en: d9492c56ba2a210a - zh: feafcad4f632a057 -/agent-note-split-build-src-and-build-test-with-build-running-both-concurrently/consequences: - en: 4b16a5b02479f4ff - zh: d75672318343a743 diff --git a/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.i18n.yaml b/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.i18n.yaml index d37640cea8..a299102cf4 100644 --- a/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.i18n.yaml +++ b/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.i18n.yaml @@ -9,8 +9,8 @@ en: 46551eb7e33c40ca zh: f8c1fc063fa77062 /agent-note-build-speed-ups-that-keep-outputs-unchanged/decision: - en: 8d64034e39485a44 - zh: b7a7eea401e2040e + en: 3948d6fc3fabf32e + zh: c3b8dcdfe8168275 /agent-note-build-speed-ups-that-keep-outputs-unchanged/decision/measurement-conditions: en: 2ab844073ec7c336 zh: b25e39f2f80b0d7e @@ -42,5 +42,5 @@ en: 186e42f2f5481c8d zh: 4a1c79a391a268b0 /agent-note-build-speed-ups-that-keep-outputs-unchanged/consequences: - en: aea05af999315af1 - zh: a8e6fca3be2306f9 + en: 93a96d3ed398f348 + zh: 93b7c7405be981a6 diff --git a/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.md b/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.md index a42fbb4d39..d1c6617a53 100644 --- a/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.md +++ b/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.md @@ -16,7 +16,7 @@ A cold build carries three fixed costs that do not affect output contents: ## Decision -The following eight speed-ups change neither the bytes of `lib/` and `apps/web/dist` outputs nor the set of type diagnostics that repository builds report. The only change in diagnostic ownership comes from item 4: Typert does not validate tsconfigs of packages it does not analyze, and only `tsc -b` reports those errors. [The build split note](2026-10-11-build-src-test-split.md) records the command split and concurrent scheduling. +The following eight speed-ups change neither the bytes of `lib/` and `apps/web/dist` outputs nor the set of type diagnostics that repository builds report. The only change in diagnostic ownership comes from item 4: Typert does not validate tsconfigs of packages it does not analyze, and only `tsc -b` reports those errors. [The build split note](2026-10-11-build-artifacts-check-split.md) records the command split and concurrent scheduling. | # | Decision | Affected stage | Measured median | Outputs and diagnostics | Intended behavior difference | |---|---|---|---|---|---| @@ -127,7 +127,7 @@ All numbers come from one shared Linux host (256 vCPU, 4 TB memory, load average ## Consequences -Client type checking spends substantially less CPU and wall time, and the Host and Client tsdown passes take substantially less wall time, without changing runtime resolution, published installation semantics, or any output byte. In the serial build segments (master → branch, one sample each), Host tsdown falls from 22.5 to 11.7 s, Client package compilation from 55.6 to 48.7 s, the Client aggregate check from 68.2 to 35.9 s, and Client tsdown from 13.9 to 10.5 s; the serial segments sum to 308.5 s on master and 254.9 s on the branch (see [the build split note](2026-10-11-build-src-test-split.md)). Host tsdown lies on the critical path of both `build` and `build:src`, Client tsdown on that of `build:src`, and the Client aggregate check is the last step of the `build` critical path. +Client type checking spends substantially less CPU and wall time, and the Host and Client tsdown passes take substantially less wall time, without changing runtime resolution, published installation semantics, or any output byte. In the serial build segments (master → branch, one sample each), Host tsdown falls from 22.5 to 11.7 s, Client package compilation from 55.6 to 48.7 s, the Client aggregate check from 68.2 to 35.9 s, and Client tsdown from 13.9 to 10.5 s; the serial segments sum to 308.5 s on master and 254.9 s on the branch (see [the build split note](2026-10-11-build-artifacts-check-split.md)). Host tsdown lies on the critical path of both `build` and `build:artifacts`, Client tsdown on that of `build:artifacts`, and the Client aggregate check is the last step of the `build` critical path. Costs: every workspace package shares one React types version; Typert does not validate tsconfigs of packages it does not analyze, and `tsc -b` owns those errors; Host tsdown max RSS grows by about 0.6 GiB, and under heavy shared-host load the worker can become Host tsdown's critical path. diff --git a/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.zh.md b/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.zh.md index 38ad3fe579..e37280a9c8 100644 --- a/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.zh.md +++ b/.agents/notes/implemented/process/2026-10-11-lossless-build-speedups.zh.md @@ -16,7 +16,7 @@ Status: implemented ## 决策 -以下八项提速不改变 `lib/`、`apps/web/dist` 的产物字节,也不改变仓库构建报告的类型诊断集合。唯一的诊断归属变化来自第 4 项:Typert 不校验它不分析的包 tsconfig,这些错误只由 `tsc -b` 报告。命令拆分与并发调度由[构建拆分 Note](2026-10-11-build-src-test-split.zh.md)记录。 +以下八项提速不改变 `lib/`、`apps/web/dist` 的产物字节,也不改变仓库构建报告的类型诊断集合。唯一的诊断归属变化来自第 4 项:Typert 不校验它不分析的包 tsconfig,这些错误只由 `tsc -b` 报告。命令拆分与并发调度由[构建拆分 Note](2026-10-11-build-artifacts-check-split.zh.md)记录。 | # | 决策 | 受影响阶段 | 实测中位数 | 产物与诊断 | 有意的行为差异 | |---|---|---|---|---|---| @@ -127,7 +127,7 @@ Status: implemented ## 后果 -Client 类型检查的 CPU 与 wall 时间、Host tsdown 与 Client tsdown 的 wall 时间都明显下降,且不改变 runtime 解析、发布安装语义或任何产物字节。串行构建的各段中(master → 本分支,各一个样本),Host tsdown 从 22.5 s 降到 11.7 s,Client 包编译从 55.6 s 降到 48.7 s,Client 聚合检查从 68.2 s 降到 35.9 s,Client tsdown 从 13.9 s 降到 10.5 s;串行各段合计在 master 上为 308.5 s,在本分支上为 254.9 s(见[构建拆分 Note](2026-10-11-build-src-test-split.zh.md))。Host tsdown 位于 `build` 与 `build:src` 的关键路径上,Client tsdown 位于 `build:src` 的关键路径上,Client 聚合检查是 `build` 关键路径的最后一步。 +Client 类型检查的 CPU 与 wall 时间、Host tsdown 与 Client tsdown 的 wall 时间都明显下降,且不改变 runtime 解析、发布安装语义或任何产物字节。串行构建的各段中(master → 本分支,各一个样本),Host tsdown 从 22.5 s 降到 11.7 s,Client 包编译从 55.6 s 降到 48.7 s,Client 聚合检查从 68.2 s 降到 35.9 s,Client tsdown 从 13.9 s 降到 10.5 s;串行各段合计在 master 上为 308.5 s,在本分支上为 254.9 s(见[构建拆分 Note](2026-10-11-build-artifacts-check-split.zh.md))。Host tsdown 位于 `build` 与 `build:artifacts` 的关键路径上,Client tsdown 位于 `build:artifacts` 的关键路径上,Client 聚合检查是 `build` 关键路径的最后一步。 代价:所有 workspace 包共用一个 React 类型版本;Typert 不校验它不分析的包 tsconfig,这些错误由 `tsc -b` 负责;Host tsdown 的最大 RSS 约增加 0.6 GiB,共享主机高负载时 worker 可能成为 Host tsdown 的关键路径。 diff --git a/.agents/skills/dsh-pre-push-checks/SKILL.md b/.agents/skills/dsh-pre-push-checks/SKILL.md index 5ca5fdce73..3d55a9dece 100644 --- a/.agents/skills/dsh-pre-push-checks/SKILL.md +++ b/.agents/skills/dsh-pre-push-checks/SKILL.md @@ -37,7 +37,7 @@ When the outgoing change adds or changes a resource-owning or asynchronous test, - **Expected-output placement:** a test whose selected recorded Session generation is replay input and expected persisted output belongs under top-level `snapshots/`, with `snapshot.yml` naming its shipped `dsh` profile and composition/header pin. Canonical parent files are `session[.vN].jsonl`, children are `session.[.vN].jsonl`, and the harness selects the highest generation per role. ARIA, geometry, generator, CLI, and unit expectations without that Session round trip stay beside their owning test under `tests/expected/`; do not place them in `snapshots/` or give them a `*.snapshot.ts` owner. Use the owning `test:expected`, `test:web`, or `test` lane. - **Profile and configuration placement:** cross-package behavior of a shipped `dsh` profile belongs under `apps/cli/tests/profiles/`; a package-specific Loader composition belongs under that package's `tests/fixtures/`. User-facing optional overlays live under `apps/cli/config/examples/` and pair with a guide under `docs/user/`. - **Package manifests, public exports, build configuration, worker/bin entries, or built runtime paths:** run `pnpm run build`, the relevant hygiene checks, and the owning built-artifact smoke. -- **Repository test and script types:** `pnpm run build:src` checks package projects only. Before claiming completion of TypeScript changes, run `pnpm run build:test` after the latest `build:src`, or run `pnpm run build`; `build:test` reads the existing `build:src` output and never rebuilds it. +- **Repository test and script types:** `pnpm run build:artifacts` checks package projects only. Before claiming completion of TypeScript changes, run `pnpm run build:check` after the latest `build:artifacts`, or run `pnpm run build`; `build:check` reads the existing `build:artifacts` output and never rebuilds it. - **Real provider or agent behavior:** run the relevant `pnpm run test:e2e` target when credentials are available; never print secrets. Do not manually repeat a passing check merely because commit or push follows. In particular, do not run typecheck immediately before pushing solely to duplicate the pre-push hook. diff --git a/.github/workflows/build-exe-for-python-sdk.yml b/.github/workflows/build-exe-for-python-sdk.yml index af6bc042a6..0c869d008a 100644 --- a/.github/workflows/build-exe-for-python-sdk.yml +++ b/.github/workflows/build-exe-for-python-sdk.yml @@ -266,7 +266,7 @@ jobs: DSH_BUILD_CLIENT_PROFILE: official run: >- pnpm exec tsx scripts/build-exe-for-python-sdk.ts --targets=${{ matrix.target }} - ${{ inputs.ci && github.event_name == 'pull_request' && '--build-src' || '' }} + ${{ inputs.ci && github.event_name == 'pull_request' && '--build-artifacts' || '' }} - name: Resolve platform outputs (POSIX) id: runtime-posix diff --git a/.github/workflows/build-preview-cloudflare.yml b/.github/workflows/build-preview-cloudflare.yml index bda0fe271d..c2ff5d3231 100644 --- a/.github/workflows/build-preview-cloudflare.yml +++ b/.github/workflows/build-preview-cloudflare.yml @@ -71,7 +71,7 @@ jobs: # build:preview packs the image through the packer's installed bin # (lib/bin.js), so neither half exists before the artifact build runs. - name: Build workspace - run: pnpm run build:src + run: pnpm run build:artifacts - name: Build the preview page and pack the VFS image env: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a5bba56208..726bd070a5 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -107,7 +107,7 @@ jobs: # the built artifact under plain Node, resolving plugins through real package # exports — the shape a real consumer runs. That requires a prior build. - name: Build (lib for the e2e example bins) - run: ${{ github.event_name == 'pull_request' && 'pnpm run build:src --profile official' || 'pnpm run build:official' }} + run: ${{ github.event_name == 'pull_request' && 'pnpm run build:artifacts --profile official' || 'pnpm run build:official' }} - name: Prepare Office runtime run: pnpm run prepare:primary-runtime --target linux-x64 --output .cache/e2e-office diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ccdc5f474..b9def1dec1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -146,7 +146,7 @@ jobs: run: pnpm run release:verify --family dsh - name: Build - run: ${{ github.event_name == 'pull_request' && 'pnpm run build:src --profile official' || 'pnpm run build:official' }} + run: ${{ github.event_name == 'pull_request' && 'pnpm run build:artifacts --profile official' || 'pnpm run build:official' }} # Concurrency here is rehearsal-only: the credentialed publish workflows # invoke release:pack without the flag and keep the strictly serial path. diff --git a/AGENTS.md b/AGENTS.md index e6aaadc0e2..724faca086 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -92,9 +92,9 @@ pnpm run test:snapshot:record # re-record expected outputs (needs key) pnpm run typecheck pnpm run lint pnpm run duplication # cross-file TypeScript clone detection -pnpm run build # build:src + build:test, concurrently -pnpm run build:src # artifacts: tsc emits lib/types, tsdown bundles runtime, Web -pnpm run build:test # typecheck aggregate tests/scripts against current build:src output +pnpm run build # build:artifacts + build:check, concurrently +pnpm run build:artifacts # artifacts: tsc emits lib/types, tsdown bundles runtime, Web +pnpm run build:check # typecheck aggregate tests/scripts against current build:artifacts output pnpm run hygiene # publint + workspace/package/dependency checks + NodeNext consumer check pnpm run check:windows-wine # ONLY when diagnosing a known Windows failure (needs wine); CI owns this signal pnpm run doc-sync # documentation gates (scripts/run-gates.ts) @@ -102,7 +102,7 @@ pnpm run test:docs # quick documentation checks (no build; doc-quick aggreg pnpm run website:build # VitePress build (doubles as dead-link check) pnpm dsh --profile headless "task" # run one task from source (needs DEEPSEEK_API_KEY) pnpm run demo:ptc -- "task" # headless PTC mode run (needs key) -pnpm run dev:web | dev:desktop # build:src, then launch; Web also rebuilds client bundles on edits. start:web | start:desktop skip the build +pnpm run dev:web | dev:desktop # build:artifacts, then launch; Web also rebuilds client bundles on edits. start:web | start:desktop skip the build make web|dev-web|desktop|dev-desktop|build # the same commands; ARGS='--no-open' forwards options ``` @@ -117,7 +117,7 @@ Before pushing, follow [dsh-pre-push-checks](.agents/skills/dsh-pre-push-checks/ - Match evidence to the surface: focused behavior tests, model/user-output snapshots, `doc-sync` for docs, built smokes for published paths, and real-API e2e for providers. - Never default to the full suite or repeat a passing check for commit or push. CI owns exhaustive coverage and the platform matrix; rehearse all locally only by explicit request, for CI diagnosis, or for an irreducibly repository-wide change. - `test:coverage`, not `test`, is the CI coverage gate ([why](docs/testing.md)). -- Develop with `build:src`; before claiming completion, run `build:test` after the latest `build:src`, or `build` ([layout](docs/development.md#typescript-project-layout)). +- Develop with `build:artifacts`; before claiming completion, run `build:check` after the latest `build:artifacts`, or `build` ([layout](docs/development.md#typescript-project-layout)). - **Web browser automation and GIF recording:** launch with `pnpm dsh web --patch apps/web/tests/pin-browse-picker.overlay.yml` to use the [in-page directory picker](apps/web/tests/pin-browse-picker.overlay.yml); omit this override only when testing native picker behavior explicitly. ## Secrets / .env diff --git a/apps/desktop/scripts/dev.ts b/apps/desktop/scripts/dev.ts index 6026a979c7..37515c20b0 100644 --- a/apps/desktop/scripts/dev.ts +++ b/apps/desktop/scripts/dev.ts @@ -93,7 +93,7 @@ async function launchElectron(): Promise { async function main(): Promise { const { values } = parseArgs({ options: { 'skip-build': { type: 'boolean', default: false } } }) if (!values['skip-build']) { - await runPackageScript('build:src', REPOSITORY_ROOT) + await runPackageScript('build:artifacts', REPOSITORY_ROOT) await runPackageScript('build', APP_ROOT) } for (const path of [ diff --git a/docs/development.i18n.yaml b/docs/development.i18n.yaml index e957025a94..cc037e4800 100644 --- a/docs/development.i18n.yaml +++ b/docs/development.i18n.yaml @@ -21,8 +21,8 @@ en: 3221d1076554001f zh: 29f166be6a5c04d9 /development-guide/contributor-reference/typescript-project-layout: - en: c5468ede5cde1a19 - zh: abe39689c6aa3db8 + en: b137fdddcc8ce2ba + zh: d9c078ba11734a21 /development-guide/contributor-reference/environment-variables: en: dba2c72ee6ffa930 zh: b65c27fbb9129ec8 @@ -39,8 +39,8 @@ en: 7ce6264a6a06dc4a zh: e12b684f2bef942b /development-guide/contributor-reference/application-commands: - en: 1f32ef693f8d1240 - zh: dbf33ffbc29e7233 + en: 98828fc2b972bf39 + zh: 1dc889a3d766bf35 /development-guide/contributor-reference/todo-markers: en: 85b38f78d87fc88f zh: 00d5eb625e7dfb61 diff --git a/docs/development.md b/docs/development.md index 0610dc6aa9..f5cf93ae56 100644 --- a/docs/development.md +++ b/docs/development.md @@ -74,7 +74,7 @@ Host and Client stay two aggregate programs because both sides declaration-merge Six packages split Host and Client tsconfigs: `api/remotes`, `api/gateway`, `api/session-controller`, `api/workspace-controller`, `client/connection`, and `session-query/session-log-export`. `api/remotes`' Host entry participates in the Host Typert graph while its Client entry imports generated `/remote` declarations; `session-log-export` keeps Node archive production out of its browser controller. Each split package-root `tsconfig.json` is therefore only a solution, and the two aggregates and direct consumers reference `tsconfig.host.json` or `tsconfig.client.json` respectively. The workspace `constraints` gate walks the reachable Project Reference graph and checks each referencing project's own compiler face: a single-config target remains valid from either face, while a split target must name the matching leaf rather than its solution root or opposite leaf; it discovers split packages from the presence of both leaf configs, so a new split joins the gate automatically. The [`api-remotes` README](../packages/api/remotes/README.md) and [`session-log-export` README](../packages/session-query/session-log-export/README.md) explain their splits. -`pnpm run build:src` emits every artifact in the generated dependency order, checking and compiling only the package references of each compiler face: +`pnpm run build:artifacts` emits every artifact in the generated dependency order, checking and compiling only the package references of each compiler face: ```sh pnpm run build:native-system @@ -86,26 +86,26 @@ tsdown --env.DSH_BUILD_FACE client pnpm run build:web ``` -`pnpm run build:test` runs only `tsc -p tsconfig.host.json` and `tsc -p tsconfig.client.json` concurrently. Project mode checks each aggregate's repository-wide test and script program against the package declarations already on disk and never compiles a reference, so `build:test` neither detects nor rebuilds missing or stale `build:src` output; missing output surfaces as compiler errors. `pnpm run build` runs both step sets as one [scheduled](../scripts/run-gates.ts) dependency graph: each aggregate check starts once its own face's package compilation finishes and overlaps the remaining artifact steps. In all three commands `DSH_GATE_CONCURRENCY` bounds concurrent steps (`1` runs them in dependency order) and `DSH_GATE_FAIL_FAST=1` stops the remaining steps at the first failure; otherwise a failure skips only its dependents, so independent errors are all reported. A failed step prints its own output, and any failure exits non-zero without writing the client build record. +`pnpm run build:check` runs only `tsc -p tsconfig.host.json` and `tsc -p tsconfig.client.json` concurrently. Project mode checks each aggregate's repository-wide test and script program against the package declarations already on disk and never compiles a reference, so `build:check` neither detects nor rebuilds missing or stale `build:artifacts` output; missing output surfaces as compiler errors. `pnpm run build` runs both step sets as one [scheduled](../scripts/run-gates.ts) dependency graph: each aggregate check starts once its own face's package compilation finishes and overlaps the remaining artifact steps. In all three commands `DSH_GATE_CONCURRENCY` bounds concurrent steps (`1` runs them in dependency order) and `DSH_GATE_FAIL_FAST=1` stops the remaining steps at the first failure; otherwise a failure skips only its dependents, so independent errors are all reported. A failed step prints its own output, and any failure exits non-zero without writing the client build record. Choose the command by what the change needs: | Situation | Command | |---|---| -| Iterating on package sources and needing runnable artifacts | `pnpm run build:src`; `dev:web` and `dev:desktop` run it before launching | -| Type-checking tests, scripts, benchmarks, or website code | `pnpm run build:test` after the latest `build:src`; rerun `build:src` first when package sources changed | -| Before declaring TypeScript work complete | `pnpm run build:test` after the latest `build:src`, or `pnpm run build` | +| Iterating on package sources and needing runnable artifacts | `pnpm run build:artifacts`; `dev:web` and `dev:desktop` run it before launching | +| Type-checking tests, scripts, benchmarks, or website code | `pnpm run build:check` after the latest `build:artifacts`; rerun `build:artifacts` first when package sources changed | +| Before declaring TypeScript work complete | `pnpm run build:check` after the latest `build:artifacts`, or `pnpm run build` | | Complete verification, as the required CI build lanes run it | `pnpm run build` | | Type checks without the Web and Desktop artifacts | `pnpm run typecheck` | | Lint or documentation code-block checks | `pnpm run lint` or `pnpm run doc-typecheck`; each prepares `host-libraries` itself | -PR artifact builders run `build:src`; required Linux and Windows normal builds and public `typecheck` also check the repository-wide test and script programs. CI benchmark preparation additionally uses `--noCheck` for its disposable library outputs, relying on those required builds for diagnostics; other artifact builders keep package checking ([rationale](../.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.md)). +PR artifact builders run `build:artifacts`; required Linux and Windows normal builds and public `typecheck` also check the repository-wide test and script programs. CI benchmark preparation additionally uses `--noCheck` for its disposable library outputs, relying on those required builds for diagnostics; other artifact builders keep package checking ([rationale](../.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.md)). Both tsdown passes match `vendor/*`, `packages/*/*`, and `apps/cli`; the Host pass also matches `apps/desktop-host`. They neither scan build artifacts to discover Client packages nor maintain a Host/Client package filter list. Package-local tsdown configs select entries for the current phase through `DSH_BUILD_FACE`: an ordinary Client plugin produces both its Node loader and browser bundle during the Client phase; `api-remotes` uses `hostPhase: true` to produce its Host entry early and only its browser bundle during the Client phase. Tsdown consumes only the JavaScript emitted to `lib/types` by the preceding tsc phase. Tsdown builds the matched workspace members concurrently, so `apps/desktop`, whose main bundle inlines workspace devDependencies from their `lib/` output, bundles in its own step after the Host pass ([Desktop README](../apps/desktop/README.md#bundled-workspace-dependencies)). Typert runs only during Host tsdown, seeded by `tsconfig.host.json`. It analyzes Host types and generates both Host reflection artifacts and the Host-for-Client Remote projection; Client tsdown does not start Typert. Consequently, Client package compilation always follows the Host bundle, and `pnpm run typecheck` runs the complete Host lib phase, including the Host aggregate check, before Client tsc. `lint`, `lint:fix`, and `doc-typecheck` read the generated declarations but not the Host aggregate check, so they prepare only Host package compilation and the Host bundle (`compile-referenced-projects.ts host-libraries`). -`pnpm run build` and `build:src` embed the root package version, the seven-character source commit, and a dirty marker when Git reports local changes; they also inherit other caller-supplied `DSH_CLIENT_*` values. `pnpm run build:official` is the cross-platform local equivalent of the CI and release artifact build and omits the local dirty marker. Each successful `build` or `build:src` writes a gitignored record that binds the exact public values to the Vite output and dynamic client bundles; release packing and built Web tests reject a missing record or artifacts changed by a later partial build. `pnpm run dev:web` runs `build:src` first (`--skip-build` reuses an existing artifact tree instead), then samples the current version and Git state once and shares that environment across every watcher stage for the session; it does not validate the complete-build record because the watcher stages rewrite its recorded artifacts. +`pnpm run build` and `build:artifacts` embed the root package version, the seven-character source commit, and a dirty marker when Git reports local changes; they also inherit other caller-supplied `DSH_CLIENT_*` values. `pnpm run build:official` is the cross-platform local equivalent of the CI and release artifact build and omits the local dirty marker. Each successful `build` or `build:artifacts` writes a gitignored record that binds the exact public values to the Vite output and dynamic client bundles; release packing and built Web tests reject a missing record or artifacts changed by a later partial build. `pnpm run dev:web` runs `build:artifacts` first (`--skip-build` reuses an existing artifact tree instead), then samples the current version and Git state once and shares that environment across every watcher stage for the session; it does not validate the complete-build record because the watcher stages rewrite its recorded artifacts. Static analysis and tests resolve workspace imports through the base `paths` map to `src` and must pass on a clean tree; gates that consume built `lib/` output declare that dependency explicitly. Generated Host-for-Client Remote declarations are the deliberate exception: the public `typecheck`, `lint`, and `doc-typecheck` commands generate them first, while internal `*:contracts-ready` scripts assume that an invoking public command or scheduler gate already depends on the Typert contract-generation pass or the complete build. See the [Typert Remote note](../.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.md) for the gate-preparation contract. @@ -178,7 +178,7 @@ pnpm run demo:ptc -- "summarize this workspace" ### Application commands -Web and Desktop share one command pair. `start:*` launches the artifacts of a prior `pnpm run build` or `build:src`; `dev:*` runs `build:src` first and then launches. Web additionally keeps client bundles rebuilt on source edits, because its Host runs from source while the browser loads built bundles: +Web and Desktop share one command pair. `start:*` launches the artifacts of a prior `pnpm run build` or `build:artifacts`; `dev:*` runs `build:artifacts` first and then launches. Web additionally keeps client bundles rebuilt on source edits, because its Host runs from source while the browser loads built bundles: ```sh pnpm run start:web # serve built Web artifacts through the source launcher (the same launch as pnpm dsh web) diff --git a/docs/development.zh.md b/docs/development.zh.md index c3eb3244af..28f56ddf9a 100644 --- a/docs/development.zh.md +++ b/docs/development.zh.md @@ -78,7 +78,7 @@ Host 与 Client 保持两个 aggregate program,是因为两侧在相同键下 拆分 Host/Client tsconfig 的包有六个:`api/remotes`、`api/gateway`、`api/session-controller`、`api/workspace-controller`、`client/connection` 与 `session-query/session-log-export`。`api/remotes` 的 Host 入口进入 Host Typert 图,而 Client 入口导入生成的 `/remote` 声明;`session-log-export` 则让 Node archive 生产代码不进入浏览器 controller。每个拆分包根 `tsconfig.json` 因此只作为 solution,两个 aggregate 和直接消费方分别引用 `tsconfig.host.json` 或 `tsconfig.client.json`。workspace `constraints` 门禁遍历可达的 Project Reference 图,并按各引用 project 自身的 compiler face 检查:只有单一配置的目标可由任一 face 引用,拆分配置的目标则必须引用匹配的 leaf,不得引用 solution 根或另一侧 leaf;该门禁按「两个 leaf 配置同时存在」自动发现拆分包,所以新拆分的包会自动纳入管辖。[`api-remotes` README](../packages/api/remotes/README.zh.md) 与 [`session-log-export` README](../packages/session-query/session-log-export/README.zh.md)分别说明其拆分。 -`pnpm run build:src` 按生成依赖顺序产出全部产物,只检查并编译各 compiler face 的包 reference: +`pnpm run build:artifacts` 按生成依赖顺序产出全部产物,只检查并编译各 compiler face 的包 reference: ```sh pnpm run build:native-system @@ -90,26 +90,26 @@ tsdown --env.DSH_BUILD_FACE client pnpm run build:web ``` -`pnpm run build:test` 只并发运行 `tsc -p tsconfig.host.json` 与 `tsc -p tsconfig.client.json`。project mode 依据磁盘上已有的包声明检查各 aggregate 的仓库级测试和脚本 program,从不编译 reference,因此 `build:test` 既不检测也不重建缺失或过期的 `build:src` 产物;产物缺失时表现为编译器错误。`pnpm run build` 把两组步骤作为一张[调度](../scripts/run-gates.ts)依赖图运行:每个 aggregate 检查在本 face 的包编译完成后立即开始,与其余产物步骤重叠执行。三个命令中,`DSH_GATE_CONCURRENCY` 限制并发步骤数(`1` 按依赖顺序执行),`DSH_GATE_FAIL_FAST=1` 在第一个失败时停止其余步骤;否则失败只跳过其依赖方,因此会报告所有相互独立的错误。失败步骤打印自身输出,任一失败都以非零状态退出且不写 client build record。 +`pnpm run build:check` 只并发运行 `tsc -p tsconfig.host.json` 与 `tsc -p tsconfig.client.json`。project mode 依据磁盘上已有的包声明检查各 aggregate 的仓库级测试和脚本 program,从不编译 reference,因此 `build:check` 既不检测也不重建缺失或过期的 `build:artifacts` 产物;产物缺失时表现为编译器错误。`pnpm run build` 把两组步骤作为一张[调度](../scripts/run-gates.ts)依赖图运行:每个 aggregate 检查在本 face 的包编译完成后立即开始,与其余产物步骤重叠执行。三个命令中,`DSH_GATE_CONCURRENCY` 限制并发步骤数(`1` 按依赖顺序执行),`DSH_GATE_FAIL_FAST=1` 在第一个失败时停止其余步骤;否则失败只跳过其依赖方,因此会报告所有相互独立的错误。失败步骤打印自身输出,任一失败都以非零状态退出且不写 client build record。 按改动需要选择命令: | 场景 | 命令 | |---|---| -| 迭代包源码并需要可运行的产物 | `pnpm run build:src`;`dev:web` 与 `dev:desktop` 启动前会先执行它 | -| 检查测试、脚本、benchmarks 或 website 代码的类型 | 在最近一次 `build:src` 之后运行 `pnpm run build:test`;包源码有改动时先重新运行 `build:src` | -| 声明 TypeScript 工作完成之前 | 在最近一次 `build:src` 之后运行 `pnpm run build:test`,或运行 `pnpm run build` | +| 迭代包源码并需要可运行的产物 | `pnpm run build:artifacts`;`dev:web` 与 `dev:desktop` 启动前会先执行它 | +| 检查测试、脚本、benchmarks 或 website 代码的类型 | 在最近一次 `build:artifacts` 之后运行 `pnpm run build:check`;包源码有改动时先重新运行 `build:artifacts` | +| 声明 TypeScript 工作完成之前 | 在最近一次 `build:artifacts` 之后运行 `pnpm run build:check`,或运行 `pnpm run build` | | 完整验证(与 CI 必需的构建 lane 相同) | `pnpm run build` | | 只做类型检查、不需要 Web 与 Desktop 产物 | `pnpm run typecheck` | | lint 或文档代码块检查 | `pnpm run lint` 或 `pnpm run doc-typecheck`;它们各自先准备 `host-libraries` | -PR 产物 builder 运行 `build:src`;必需的 Linux 与 Windows 普通构建以及公开 `typecheck` 还检查仓库级测试和脚本 program。CI benchmark 准备阶段还对其临时 library 输出使用 `--noCheck`,由这些必需构建负责诊断;其他产物 builder 保留包检查([理由](../.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.zh.md))。 +PR 产物 builder 运行 `build:artifacts`;必需的 Linux 与 Windows 普通构建以及公开 `typecheck` 还检查仓库级测试和脚本 program。CI benchmark 准备阶段还对其临时 library 输出使用 `--noCheck`,由这些必需构建负责诊断;其他产物 builder 保留包检查([理由](../.agents/notes/implemented/process/2026-10-05-pr-artifact-typechecks.zh.md))。 两次 tsdown 都匹配 `vendor/*`、`packages/*/*` 与 `apps/cli`,Host 阶段另外匹配 `apps/desktop-host`;两者都不扫描构建产物来发现 Client 包,也不维护 Host/Client 包过滤表。包内 tsdown 配置根据 `DSH_BUILD_FACE` 决定当前阶段的入口:普通 Client 插件在 Client 阶段同时生成 Node loader 与 browser bundle;`api-remotes` 通过 `hostPhase: true` 提前生成 Host 入口,再在 Client 阶段只生成 browser bundle。tsdown 只消费 `lib/types` 中由前置 tsc 发射的 JavaScript。tsdown 并发构建匹配到的 workspace 成员;`apps/desktop` 的主进程 bundle 需要从工作区 devDependencies 的 `lib/` 产物内联这些包,因此在 Host 阶段之后单独一步打 bundle([Desktop README](../apps/desktop/README.zh.md#bundled-workspace-dependencies))。 Typert 只在 Host tsdown 中以 `tsconfig.host.json` 为种子运行。它分析 Host 类型并生成 Host 反射产物及 Host-for-Client Remote 投影;Client tsdown 不启动 Typert。因此 Client 包编译总在 Host bundle 之后;`pnpm run typecheck` 先执行包含 Host aggregate 检查的完整 Host lib 阶段,再运行 Client tsc。`lint`、`lint:fix` 与 `doc-typecheck` 读取生成的声明但不依赖 Host aggregate 检查,因此只准备 Host 包编译与 Host bundle(`compile-referenced-projects.ts host-libraries`)。 -`pnpm run build` 与 `build:src` 会内联根包版本、七位源码 commit,并在 Git 报告本地变化时内联 dirty 标记;调用方提供的其他 `DSH_CLIENT_*` 值也会被继承。`pnpm run build:official` 是与 CI 和 release 产物构建等价的跨平台本地命令,并省略本地 dirty 标记。每次 `build` 或 `build:src` 成功后都会写入一份被 gitignore 的记录,把精确公开值与 Vite 输出及动态 client bundle 绑定;release 打包和 built Web 测试会拒绝缺少记录或被后续局部构建改动的产物。`pnpm run dev:web` 会先执行 `build:src`(`--skip-build` 则复用现有产物树),再读取一次当前版本和 Git 状态,并在本次会话的所有 watcher stage 之间共享该环境;它不会校验完整构建记录,因为 watcher stage 会重写记录覆盖的产物。 +`pnpm run build` 与 `build:artifacts` 会内联根包版本、七位源码 commit,并在 Git 报告本地变化时内联 dirty 标记;调用方提供的其他 `DSH_CLIENT_*` 值也会被继承。`pnpm run build:official` 是与 CI 和 release 产物构建等价的跨平台本地命令,并省略本地 dirty 标记。每次 `build` 或 `build:artifacts` 成功后都会写入一份被 gitignore 的记录,把精确公开值与 Vite 输出及动态 client bundle 绑定;release 打包和 built Web 测试会拒绝缺少记录或被后续局部构建改动的产物。`pnpm run dev:web` 会先执行 `build:artifacts`(`--skip-build` 则复用现有产物树),再读取一次当前版本和 Git 状态,并在本次会话的所有 watcher stage 之间共享该环境;它不会校验完整构建记录,因为 watcher stage 会重写记录覆盖的产物。 静态分析和测试通过 base 的 `paths` 映射把工作区 import 解析到 `src`,且必须在干净树上通过;消费构建产物 `lib/` 的门禁显式声明该依赖。生成的 Host-for-Client Remote 声明是有意设置的例外:公共 `typecheck`、`lint` 和 `doc-typecheck` 命令会先生成这些声明,而内部 `*:contracts-ready` 脚本假定调用它的公共命令或调度器门禁已经依赖 Typert 约定生成阶段或完整构建。门禁准备约定见 [Typert Remote Agent Note](../.agents/notes/implemented/architecture/2026-08-02-typert-remote-method-calls.zh.md)。 @@ -183,7 +183,7 @@ pnpm run demo:ptc -- "summarize this workspace" ### 应用命令 -Web 与 Desktop 共用同一对命令。`start:*` 启动上一次 `pnpm run build` 或 `build:src` 的产物;`dev:*` 先执行 `build:src` 再启动。Web 还会在源码修改时持续重建 client bundle,因为它的 Host 从源码运行,而浏览器加载的是构建产物: +Web 与 Desktop 共用同一对命令。`start:*` 启动上一次 `pnpm run build` 或 `build:artifacts` 的产物;`dev:*` 先执行 `build:artifacts` 再启动。Web 还会在源码修改时持续重建 client bundle,因为它的 Host 从源码运行,而浏览器加载的是构建产物: ```sh pnpm run start:web # serve built Web artifacts through the source launcher (the same launch as pnpm dsh web) diff --git a/package.json b/package.json index c6624c83a9..85c760c076 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "scripts": { "prepare:primary-runtime": "tsx scripts/primary-runtime/prepare.ts", "build": "tsx scripts/build.ts", - "build:src": "tsx scripts/build.ts --scope src", - "build:test": "tsx scripts/build.ts --scope test", + "build:artifacts": "tsx scripts/build.ts --scope artifacts", + "build:check": "tsx scripts/build.ts --scope check", "build:bench": "npm run build:native-system && npm run build:lib && tsdown --config-loader native --config benchmarks/tsdown.config.ts", "build:official": "tsx scripts/build.ts --profile official", "build:lib": "pnpm run build:lib:host && pnpm run build:lib:client", diff --git a/scripts/build-exe-for-python-sdk.spec.ts b/scripts/build-exe-for-python-sdk.spec.ts index f44e0c1b19..0a9d1659a4 100644 --- a/scripts/build-exe-for-python-sdk.spec.ts +++ b/scripts/build-exe-for-python-sdk.spec.ts @@ -25,7 +25,7 @@ function run(env: NodeJS.ProcessEnv, ...args: string[]) { describe('Python runtime executable builder CLI', () => { it.each([ { flags: [], command: 'run build' }, - { flags: ['--build-src'], command: 'run build:src' }, + { flags: ['--build-artifacts'], command: 'run build:artifacts' }, ])('validates the closure before $command and deploys only after building', ({ flags, command }) => { const result = run( { npm_execpath: 'C:\\tools\\pnpm.cjs' }, @@ -57,14 +57,14 @@ describe('Python runtime executable builder CLI', () => { const result = run( { npm_execpath: entrypoint, DSH_PYTHON_BUILD_COMMANDS: commands }, '--skip-build', - '--build-src', + '--build-artifacts', '--targets=node24-macos-arm64', ) expect(result.error).toBeUndefined() expect(result.signal).toBeNull() expect(result.status).not.toBe(0) - expect(result.stderr).toContain('--skip-build and --build-src cannot be combined') + expect(result.stderr).toContain('--skip-build and --build-artifacts cannot be combined') expect(result.stdout).toBe('') expect(existsSync(commands)).toBe(false) }) @@ -83,7 +83,7 @@ describe('Python runtime executable builder CLI', () => { const result = run( { npm_execpath: entrypoint, DSH_PYTHON_BUILD_COMMANDS: commands }, - '--build-src', + '--build-artifacts', '--targets=node24-macos-arm64', ) diff --git a/scripts/build-exe-for-python-sdk.ts b/scripts/build-exe-for-python-sdk.ts index 65c3ab0280..62fb7e3727 100644 --- a/scripts/build-exe-for-python-sdk.ts +++ b/scripts/build-exe-for-python-sdk.ts @@ -165,8 +165,8 @@ class BuildCli { readonly targets: readonly Target[], /** Skip the package build; lib/ artifacts must already exist. */ readonly skipBuild: boolean, - /** Build through `pnpm run build:src`, which omits the aggregate test and script typechecks. */ - readonly buildSrc: boolean, + /** Build through `pnpm run build:artifacts`, which omits the aggregate test and script typechecks. */ + readonly buildArtifacts: boolean, /** Print every command and config patch instead of executing. */ readonly dryRun: boolean, ) {} @@ -190,8 +190,8 @@ class BuildCli { console.log(BuildCli.usage()) process.exit(0) } - if (values['skip-build'] && values['build-src']) { - throw new Error('build-exe-for-python-sdk: --skip-build and --build-src cannot be combined.') + if (values['skip-build'] && values['build-artifacts']) { + throw new Error('build-exe-for-python-sdk: --skip-build and --build-artifacts cannot be combined.') } const targets = values.targets === undefined ? [Target.host()] @@ -205,7 +205,7 @@ class BuildCli { } seen.add(key) } - return new BuildCli(targets, values['skip-build'], values['build-src'], values['dry-run']) + return new BuildCli(targets, values['skip-build'], values['build-artifacts'], values['dry-run']) } private static parseRaw(argv: string[]) { @@ -214,7 +214,7 @@ class BuildCli { options: { 'targets': { type: 'string' }, 'skip-build': { type: 'boolean', default: false }, - 'build-src': { type: 'boolean', default: false }, + 'build-artifacts': { type: 'boolean', default: false }, 'dry-run': { type: 'boolean', default: false }, 'help': { type: 'boolean', default: false }, }, @@ -228,7 +228,7 @@ class BuildCli { ' --targets= pkg targets, e.g. node24-linux-x64,node24-linux-arm64,node24-macos-arm64,node24-macos-x64,node24-win-x64.', ' Default: the host platform only (on node24).', ' --skip-build skip `pnpm run build` (lib/ artifacts must already exist).', - ' --build-src run `pnpm run build:src` instead, omitting the aggregate test and script typechecks.', + ' --build-artifacts run `pnpm run build:artifacts` instead, omitting the aggregate test and script typechecks.', ' --dry-run print every command and config patch without executing.', ' --help print this help.', '', @@ -272,7 +272,7 @@ class SingleExeBuild { console.log('build-exe-for-python-sdk: skipping pnpm run build (--skip-build)') return } - const args = this.cli.buildSrc ? ['run', 'build:src'] : ['run', 'build'] + const args = this.cli.buildArtifacts ? ['run', 'build:artifacts'] : ['run', 'build'] await this.runPnpm('build', args) } diff --git a/scripts/build.spec.ts b/scripts/build.spec.ts index 60eb8e3c1c..21e45c0697 100644 --- a/scripts/build.spec.ts +++ b/scripts/build.spec.ts @@ -33,14 +33,14 @@ describe('repository build CLI', () => { it('selects the complete build by default and one step set by scope', () => { expect(parseBuildArguments([])).toEqual({ scope: 'complete', profile: undefined }) - expect(parseBuildArguments(['--scope', 'src', '--profile', 'official'])).toEqual({ scope: 'src', profile: 'official' }) - expect(parseBuildArguments(['--scope', 'test'])).toEqual({ scope: 'test', profile: undefined }) + expect(parseBuildArguments(['--scope', 'artifacts', '--profile', 'official'])).toEqual({ scope: 'artifacts', profile: 'official' }) + expect(parseBuildArguments(['--scope', 'check'])).toEqual({ scope: 'check', profile: undefined }) }) it.each([ - [['--scope', 'complete'], '--scope must be src or test'], - [['--scope', 'lib'], '--scope must be src or test'], - [['--scope', 'test', '--profile', 'official'], '--profile selects artifact values and does not apply to --scope test'], + [['--scope', 'complete'], '--scope must be artifacts or check'], + [['--scope', 'lib'], '--scope must be artifacts or check'], + [['--scope', 'check', '--profile', 'official'], '--profile selects artifact values and does not apply to --scope check'], ])('rejects %j', (args, message) => { expect(() => parseBuildArguments(args)).toThrow(message) }) @@ -48,7 +48,7 @@ describe('repository build CLI', () => { describe('repository build steps', () => { it('keeps artifact steps in serial dependency order', () => { - expect(graph('src')).toEqual({ + expect(graph('artifacts')).toEqual({ 'native': [], 'host-src': ['native'], 'host-bundle': ['host-src'], @@ -60,8 +60,8 @@ describe('repository build steps', () => { }) it('checks both aggregate programs without artifact predecessors', () => { - expect(graph('test')).toEqual({ 'host-test': [], 'client-test': [] }) - expect(buildGates(root, 'test').map(gate => gate.args.slice(-2))).toEqual([ + expect(graph('check')).toEqual({ 'host-check': [], 'client-check': [] }) + expect(buildGates(root, 'check').map(gate => gate.args.slice(-2))).toEqual([ ['-p', 'tsconfig.host.json'], ['-p', 'tsconfig.client.json'], ]) @@ -69,9 +69,9 @@ describe('repository build steps', () => { it('starts each aggregate check after its own package compilation in the complete build', () => { expect(graph('complete')).toEqual({ - ...graph('src'), - 'host-test': ['host-src'], - 'client-test': ['client-src'], + ...graph('artifacts'), + 'host-check': ['host-src'], + 'client-check': ['client-src'], }) }) diff --git a/scripts/build.ts b/scripts/build.ts index f3792e0a49..39a56a4145 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -1,8 +1,8 @@ /** * Build repository artifacts, type-check the aggregate test and script programs, or both. * - * `--scope src` emits every artifact and binds the client outputs to their public environment; - * `--scope test` type-checks only the Host and Client aggregate programs against the package + * `--scope artifacts` emits every artifact and binds the client outputs to their public environment; + * `--scope check` type-checks only the Host and Client aggregate programs against the package * declarations already on disk; the default runs both step sets as one dependency graph, so the * aggregate checks overlap the remaining artifact steps. */ @@ -25,7 +25,7 @@ import { runAggregate } from './run-gates.ts' import type { Gate } from './run-gates.ts' /** Steps one build invocation runs: every artifact and check, only artifacts, or only aggregate checks. */ -export type BuildScope = 'complete' | 'src' | 'test' +export type BuildScope = 'complete' | 'artifacts' | 'check' /** Parsed command-line selection of one build invocation. */ export interface BuildArguments { @@ -38,7 +38,7 @@ export interface BuildArguments { * Parse the build command line. * @param args - Arguments after the script path. * @returns The selected scope and requested client build profile. - * @throws If an option is unknown, the scope is not `src` or `test`, or `--profile` accompanies `--scope test`. + * @throws If an option is unknown, the scope is not `artifacts` or `check`, or `--profile` accompanies `--scope check`. */ export function parseBuildArguments(args: readonly string[]): BuildArguments { const { values } = parseArgs({ @@ -49,12 +49,12 @@ export function parseBuildArguments(args: readonly string[]): BuildArguments { }, allowPositionals: false, }) - if (values.scope !== undefined && values.scope !== 'src' && values.scope !== 'test') { - throw new Error(`build: --scope must be src or test, got ${JSON.stringify(values.scope)}; omit it for the complete build`) + if (values.scope !== undefined && values.scope !== 'artifacts' && values.scope !== 'check') { + throw new Error(`build: --scope must be artifacts or check, got ${JSON.stringify(values.scope)}; omit it for the complete build`) } const scope: BuildScope = values.scope ?? 'complete' - if (scope === 'test' && values.profile !== undefined) { - throw new Error('build: --profile selects artifact values and does not apply to --scope test') + if (scope === 'check' && values.profile !== undefined) { + throw new Error('build: --profile selects artifact values and does not apply to --scope check') } return { scope, profile: values.profile } } @@ -78,7 +78,7 @@ export function buildGates( ): Gate[] { const env = { ...environment } const gates: Gate[] = [] - if (scope !== 'test') { + if (scope !== 'check') { gates.push(pnpmGate('native', 'native addon', ['run', 'build:native-system'], [], env)) for (const step of libraryBuildSteps(root, 'all')) { gates.push({ @@ -93,10 +93,10 @@ export function buildGates( } gates.push(pnpmGate('web', 'Web build', ['run', 'build:web'], ['client-bundle'], env)) } - if (scope !== 'src') { + if (scope !== 'artifacts') { for (const face of ['host', 'client'] as const) { gates.push({ - id: `${face}-test`, + id: `${face}-check`, label: `${face} aggregate check`, displayCommand: `tsc -p tsconfig.${face}.json`, command: process.execPath, @@ -143,7 +143,7 @@ async function main(): Promise { workers: Math.min(gates.length, available), source: `${String(available)} available CPU(s)`, }) - if (scope === 'test') { + if (scope === 'check') { const gates = buildGates(root, scope) return runAggregate(name, gates, workers(gates)) } diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 1857457a56..e68ccf3cbb 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -92,7 +92,7 @@ describe('CI workflow', () => { }) it('selects artifact builds only for PR release and API rehearsals', () => { - const official = "${{ github.event_name == 'pull_request' && 'pnpm run build:src --profile official' || 'pnpm run build:official' }}" + const official = "${{ github.event_name == 'pull_request' && 'pnpm run build:artifacts --profile official' || 'pnpm run build:official' }}" const vendor = "${{ github.event_name == 'pull_request' && 'pnpm exec tsx scripts/compile-referenced-projects.ts host-libraries' || 'pnpm run build:lib:host' }}" for (const [file, jobName, expected] of [ ['release.yml', 'pack', official], @@ -102,7 +102,7 @@ describe('CI workflow', () => { const job = workflowJob(loadWorkflow('.github/workflows/' + file), jobName) if (!Array.isArray(job.steps)) throw new TypeError(`${jobName} must define steps`) const step = job.steps.filter(isRecord).find(candidate => typeof candidate.run === 'string' - && (candidate.run.includes('build:src') || candidate.run.includes('host-libraries'))) + && (candidate.run.includes('build:artifacts') || candidate.run.includes('host-libraries'))) expect(step?.run).toBe(expected) for (const event of ['pull_request', 'push', 'workflow_dispatch']) { const expression = expected.slice('${{ '.length, -' }}'.length) @@ -110,7 +110,7 @@ describe('CI workflow', () => { expect(selected).toBe(event === 'pull_request' ? file === 'release-vendor.yml' ? 'pnpm exec tsx scripts/compile-referenced-projects.ts host-libraries' - : 'pnpm run build:src --profile official' + : 'pnpm run build:artifacts --profile official' : file === 'release-vendor.yml' ? 'pnpm run build:lib:host' : 'pnpm run build:official') } } @@ -121,20 +121,20 @@ describe('CI workflow', () => { if (!Array.isArray(job.steps)) throw new TypeError('Python runtime builder must define steps') const steps = job.steps.filter(isRecord) const execute = steps.find(step => step.name === 'Build single-exe') - expect(steps.some(step => step.run === 'pnpm run build:src')).toBe(false) + expect(steps.some(step => step.run === 'pnpm run build:artifacts')).toBe(false) expect(execute).toMatchObject({ env: { DSH_BUILD_CLIENT_PROFILE: 'official' }, }) if (typeof execute?.run !== 'string') throw new TypeError('Python executable builder must define a command') const expression = [...execute.run.matchAll(/\$\{\{ ([^{}]+) \}\}/g)] .map(match => match[1]).find(value => value?.startsWith('inputs.ci')) - expect(expression).toBe("inputs.ci && github.event_name == 'pull_request' && '--build-src' || ''") + expect(expression).toBe("inputs.ci && github.event_name == 'pull_request' && '--build-artifacts' || ''") if (expression === undefined) throw new TypeError('Python executable builder must select its build mode') for (const ci of [false, true]) { for (const event of ['pull_request', 'push', 'workflow_dispatch']) { const context = { inputs: { ci }, github: { event_name: event } } const selected: unknown = runInNewContext(expression, context, { timeout: 1000 }) - expect(selected).toBe(ci && event === 'pull_request' ? '--build-src' : '') + expect(selected).toBe(ci && event === 'pull_request' ? '--build-artifacts' : '') } } }) diff --git a/scripts/dev-web.ts b/scripts/dev-web.ts index 78d9182df2..9aa3bc0690 100644 --- a/scripts/dev-web.ts +++ b/scripts/dev-web.ts @@ -2,7 +2,7 @@ * The Web development loop: build once, serve through `dsh web`, and keep every * browser-side artifact rebuilt on source edits, in one terminal. * - * Stages, in order: `pnpm run build:src` (skipped by `--skip-build`), three + * Stages, in order: `pnpm run build:artifacts` (skipped by `--skip-build`), three * long-lived watchers, then `dsh web` (skipped by `--no-serve`). The watchers * exist because the compile shell links built lib products rather than * sources: `tsc -b tsconfig.client.json --watch` emits `lib/types` (the tsdown @@ -16,11 +16,11 @@ * stat-polls the bundles it serves and broadcasts `rebuilt` frames itself, so * any process that rewrites `lib/client.js` files triggers reloads. * - * MUST NOT run beside `pnpm run build` or `build:src`: they write the same `lib/` and + * MUST NOT run beside `pnpm run build` or `build:artifacts`: they write the same `lib/` and * `apps/web/dist/` trees. The build stage here finishes before any watcher starts. * * Usage: `pnpm run dev:web [--skip-build] [--no-serve] [--poll[=ms]] [dsh web arguments]`. - * `--skip-build` requires the artifact tree from a prior `build` or `build:src`: every + * `--skip-build` requires the artifact tree from a prior `build` or `build:artifacts`: every * watcher is incremental over the previous stage's output and none of them * bootstraps a missing tree. `--no-serve` keeps only the watchers, for a * `dsh web` started elsewhere. `--poll` switches the source watchers to polling @@ -80,7 +80,7 @@ export function devWebBuildEnvironment( /** Resolved `dev-web` command line: the script's own flags plus the arguments forwarded to `dsh web`. */ export interface DevWebArguments { - /** Skip the `pnpm run build:src` that otherwise precedes the watchers. */ + /** Skip the `pnpm run build:artifacts` that otherwise precedes the watchers. */ readonly skipBuild: boolean /** Start `dsh web`; false keeps only the rebuild watchers beside an already running server. */ readonly serve: boolean @@ -324,7 +324,7 @@ function spawnStage( * @returns the build's exit code, or null when a signal ended it. */ async function runBuild(): Promise { - const invocation = pnpmInvocation(['run', 'build:src']) + const invocation = pnpmInvocation(['run', 'build:artifacts']) const result = await execa(invocation.command, invocation.args, { cwd: repoRoot, stdio: 'inherit', reject: false }) return result.exitCode ?? null } @@ -366,7 +366,7 @@ if (isMain) { const buildExit = options.skipBuild ? 0 : await runBuild() if (shutdownRequested()) process.exit(await shutdown.promise) if (buildExit !== 0) { - console.error(`dev-web: pnpm run build:src exited (code ${String(buildExit)})`) + console.error(`dev-web: pnpm run build:artifacts exited (code ${String(buildExit)})`) process.exit(1) } diff --git a/scripts/preview-workflow.spec.ts b/scripts/preview-workflow.spec.ts index 3ca77c6141..f62bf415d0 100644 --- a/scripts/preview-workflow.spec.ts +++ b/scripts/preview-workflow.spec.ts @@ -29,9 +29,9 @@ describe('PR preview workflow', () => { expect(workflow.env.DSH_TELEMETRY_DISABLED).toBe('1') const commands = preview.steps.map(step => step.run) expect(commands).toContain('pnpm install --frozen-lockfile') - expect(commands).toContain('pnpm run build:src') + expect(commands).toContain('pnpm run build:artifacts') expect(commands).toContain('pnpm --filter @deepseek-ai/dsh-web-frontend run build:preview') - expect(commands.indexOf('pnpm run build:src')).toBeLessThan(commands.indexOf('pnpm --filter @deepseek-ai/dsh-web-frontend run build:preview')) + expect(commands.indexOf('pnpm run build:artifacts')).toBeLessThan(commands.indexOf('pnpm --filter @deepseek-ai/dsh-web-frontend run build:preview')) expect(preview.steps.filter(step => step.uses?.startsWith('actions/cache'))).toHaveLength(1) expect(preview.steps.find(step => step.uses === 'actions/cache/restore@v6.1.0')?.with).toMatchObject({ key: "${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }}", diff --git a/scripts/run-gates.spec.ts b/scripts/run-gates.spec.ts index 6c1e22a449..e8d8385c8a 100644 --- a/scripts/run-gates.spec.ts +++ b/scripts/run-gates.spec.ts @@ -865,9 +865,9 @@ describe('Node compatibility graph', () => { withEnv('DSH_NODE_COMPAT_SKIP_TYPECHECK', skipTypecheck, () => withPnpmEntrypoint(() => gatesForMode('node-compat')))) - expect(subject.filter(item => item.displayCommand === 'pnpm run build:src')).toHaveLength(1) + expect(subject.filter(item => item.displayCommand === 'pnpm run build:artifacts')).toHaveLength(1) expect(subject.find(item => item.id === 'build')?.args).toEqual([ - '/private/pnpm.cjs', 'run', 'build:src', + '/private/pnpm.cjs', 'run', 'build:artifacts', ]) expect(subject.map(item => item.id)).not.toContain('build:web') expect(subject.find(item => item.id === 'cli-lazy-search-startup-smoke')).toMatchObject({ diff --git a/scripts/run-gates.ts b/scripts/run-gates.ts index f2c3fac01b..2a024dcf47 100644 --- a/scripts/run-gates.ts +++ b/scripts/run-gates.ts @@ -424,7 +424,7 @@ function nodeCompatGates(): Gate[] { } return [ ...typecheck, - pnpmScript('build', 'build:src', { + pnpmScript('build', 'build:artifacts', { ...typecheck.length === 0 ? {} : { needs: ['typecheck'] }, }), ...nodeCompatSmokeGates({ cliSmoke: true }), diff --git a/scripts/tests/ci-release-selfhosted.spec.ts b/scripts/tests/ci-release-selfhosted.spec.ts index 6590ab2a7c..de9de1055c 100644 --- a/scripts/tests/ci-release-selfhosted.spec.ts +++ b/scripts/tests/ci-release-selfhosted.spec.ts @@ -147,7 +147,7 @@ for (const [file, jobIds] of [['release.yml', ['dependencies', 'pack']], ['relea expect(job.steps[0]?.with?.['fetch-depth']).toBe(0) expect(commands).toContain('pnpm run release:verify --family ' + family) expect(commands).toContain(family === 'dsh' - ? "${{ github.event_name == 'pull_request' && 'pnpm run build:src --profile official' || 'pnpm run build:official' }}" + ? "${{ github.event_name == 'pull_request' && 'pnpm run build:artifacts --profile official' || 'pnpm run build:official' }}" : "${{ github.event_name == 'pull_request' && 'pnpm exec tsx scripts/compile-referenced-projects.ts host-libraries' || 'pnpm run build:lib:host' }}") expect(commands).toContain('pnpm run release:pack --family ' + family + ' --out ' + output + ' --concurrency 8') expect(commands).toContain('pnpm run release:verify-packed-install --family ' + family + ' --from ' + output