mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(test): publish lint probes atomically
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-07-31-coverage-exempt-heavy-suites.md
|
||||
2026-07-31-coverage-exempt-heavy-suites.md: b27039e529dad12eb82637a749577f4922c22f03
|
||||
2026-07-31-coverage-exempt-heavy-suites.zh.md: 99c707f079c215f196b5378f8e4739dce4076846
|
||||
2026-07-31-coverage-exempt-heavy-suites.md: 35642c41c0140b5a39be7da4668b33b70f858a85
|
||||
2026-07-31-coverage-exempt-heavy-suites.zh.md: cefade080581e4c20a71269bef638e12559153ae
|
||||
|
||||
@@ -19,7 +19,7 @@ The `ci-coverage` aggregate splits into two parallel gates; every test still run
|
||||
- **Instrumented gate** (`test:coverage`): sets `DSH_COVERAGE_EXEMPT_HEAVY=1`, which makes `vitest.config.ts` drop the exempt suites from both projects' excludes; every remaining file runs instrumented and carries the entire threshold proof. The variable is injected through the gate's own env (the existing `Gate.env` mechanism), not the workflow-global environment, so the uninstrumented gate beside it and any local `vitest run` never see it and behave unchanged.
|
||||
- **Uninstrumented gate** (`test:coverage-exempt-heavy`): runs exactly the exempt suites through paired positional filters, keeping the correctness signal whole.
|
||||
|
||||
Linux coverage CI and native Windows CI use [in-job partitioned coverage](2026-08-18-in-job-partitioned-coverage.md) inside the instrumented gate. Its merged report carries the same threshold proof; the exempt gate and its membership rules remain unchanged. Linux overlaps four partition children, two exempt workers, and up to eight corpus children, so this combined fan-out is the first check if that lane regresses. Native Windows runs the exempt gate after the instrumented merge, while the lightweight observational inventory overlaps the exempt work, so the full-corpus child does not compete with sixteen coverage processes. The Oxlint contract suite keeps temporary package probes valid under concurrent source checks and hides its script-only probes from glob discovery.
|
||||
Linux coverage CI and native Windows CI use [in-job partitioned coverage](2026-08-18-in-job-partitioned-coverage.md) inside the instrumented gate. Its merged report carries the same threshold proof; the exempt gate and its membership rules remain unchanged. Linux overlaps four partition children, two exempt workers, and up to eight corpus children, so this combined fan-out is the first check if that lane regresses. Native Windows runs the exempt gate after the instrumented merge, while the lightweight observational inventory overlaps the exempt work, so the full-corpus child does not compete with sixteen coverage processes. The Oxlint contract suite atomically publishes scanner-valid temporary package probes and hides its script-only probes from glob discovery.
|
||||
|
||||
`scripts/coverage-exempt.ts` is the single roster point, holding the membership contract and the filter/exclude pairs so the two sides cannot drift.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Web Worker 转换语料库在原生 Windows 上暴露了同一类浪费:`trans
|
||||
- **插桩 gate**(`test:coverage`):设 `DSH_COVERAGE_EXEMPT_HEAVY=1`,`vitest.config.ts` 据此从两个 project 的 exclude 中剔除豁免套件,其余全部文件照旧插桩并承担全部阈值证明。经 gate 自带 env 注入(既有 `Gate.env` 机制),不进 workflow 全局环境,因此并排的无插桩 gate 和本地直跑 `vitest run` 都看不到该变量、行为不变。
|
||||
- **无插桩 gate**(`test:coverage-exempt-heavy`):用配对的 positional filter 恰好运行豁免套件,保证正确性信号不缩水。
|
||||
|
||||
Linux 覆盖率 CI 与原生 Windows CI 在插桩门禁内部使用 [job 内分区覆盖率](2026-08-18-in-job-partitioned-coverage.zh.md)。其合并报告承担相同的阈值证明;豁免门禁及其成员资格规则保持不变。Linux 会让 4 个分区子进程、2 个豁免 worker 与最多 8 个语料库子进程重叠,因此该通道变慢时应先检查这组并发。原生 Windows 在插桩报告合并后运行豁免门禁,同时让轻量观测性清单与豁免工作重叠,因此完整语料库子进程不会与 16 个覆盖率进程争用资源。Oxlint 约定套件让包内临时探针满足并发源码检查,并把只属于脚本的探针对 glob 发现隐藏。
|
||||
Linux 覆盖率 CI 与原生 Windows CI 在插桩门禁内部使用 [job 内分区覆盖率](2026-08-18-in-job-partitioned-coverage.zh.md)。其合并报告承担相同的阈值证明;豁免门禁及其成员资格规则保持不变。Linux 会让 4 个分区子进程、2 个豁免 worker 与最多 8 个语料库子进程重叠,因此该通道变慢时应先检查这组并发。原生 Windows 在插桩报告合并后运行豁免门禁,同时让轻量观测性清单与豁免工作重叠,因此完整语料库子进程不会与 16 个覆盖率进程争用资源。Oxlint 约定套件会原子发布满足源码扫描要求的包内临时探针,并把只属于脚本的探针对 glob 发现隐藏。
|
||||
|
||||
`scripts/coverage-exempt.ts` 是唯一名单点,集中持有成员资格约定与 filter/exclude 配对,防止两侧漂移。
|
||||
|
||||
|
||||
+2
-2
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md
|
||||
2026-08-08-native-windows-pull-request-ci.md: 6a39503f68316e3d9687645248ffa7ed8b453216
|
||||
2026-08-08-native-windows-pull-request-ci.zh.md: 07db589a3f0537306c1d2f206504097920411dd5
|
||||
2026-08-08-native-windows-pull-request-ci.md: d3b37bdcc19b7b06aee2aa4a067cfd317617ab67
|
||||
2026-08-08-native-windows-pull-request-ci.zh.md: 01edfece893d1d6f4cbb14a7d061e372313327f3
|
||||
|
||||
@@ -18,7 +18,7 @@ Every pull request also starts a separate `windows-native` job named `windows no
|
||||
|
||||
The native job retains its own unmasked result. [The aggregate-dependency decision](2026-08-22-native-windows-blocks-pull-request-aggregate.md) makes that result a dependency of `all checks passed`; this note owns the job's execution topology and complete inventory. Workspace build, production-site, and 100%-per-file coverage failures make the native job fail. Static, documentation, package, built-artifact, lint, and snapshot inventories run in the same job as observational gates: their failures remain visible without changing the native aggregate result because Linux owns their blocking verdict.
|
||||
|
||||
The 16-core lane admits eight concurrent outer gates. Workspace build, production-site validation, and sixteen-process instrumented coverage start immediately. Exempt-heavy coverage needs the build and waits for the merged coverage verdict, so its four Vitest workers and up to eight corpus children do not compete with the partition phase. The lightweight observational inventory also waits for coverage, then overlaps the exempt work; temporary package probes satisfy concurrent source checks, while script-only probes use hidden filenames. `publint` is capped at eight workers when the observational inventory starts. Every Vitest project uses forked workers because Node 24's CJS lexer fatal reproduced in shared worker threads on Windows and POSIX. Both coverage gates set Vitest's default per-test and polling budgets to 30 seconds because unrelated process, Git, SQLite, watcher, grammar, and static-gate fixtures can exceed 15 seconds only under the complete lane's concurrent Windows instrumentation. The script-only translation-pairing merge suite runs in the exempt-heavy gate because it imports only `scripts/` sources and child processes; V8 instrumentation contributes no threshold coverage there but magnifies Git-process latency. Lefthook concurrency fixtures retain their outcomes with 30-second case budgets and a 10-second process-ready probe, while the installer allows five seconds for a preempted lock owner to publish its record after exclusive creation. Directory-picker composition gives its debounced config write an explicit 15-second poll budget; workspace-context composition fixtures use a test-owned signal without an unrelated one-second deadline. These lane-scoped budgets preserve asserted outcomes, while the 120-minute job deadline still bounds a stuck run. The LSP sources and the ACL-sandbox sources remain in the Windows denominator: stub-based failure-path suites carry every in-process ACL-sandbox file to 100%, and only the runner entry stays excluded — it executes exclusively as a spawned child outside the instrumented run, its behavior pinned end-to-end by the runner suite. Narrow annotated V8 ignores cover only unreachable branches (peer-platform arms and lifecycle-unreachable guards), with their behavior tests retained on the owning platform.
|
||||
The 16-core lane admits eight concurrent outer gates. Workspace build, production-site validation, and sixteen-process instrumented coverage start immediately. Exempt-heavy coverage needs the build and waits for the merged coverage verdict, so its four Vitest workers and up to eight corpus children do not compete with the partition phase. The lightweight observational inventory also waits for coverage, then overlaps the exempt work; temporary package probes are atomically published with scanner-valid contents, while script-only probes use hidden filenames. `publint` is capped at eight workers when the observational inventory starts. Every Vitest project uses forked workers because Node 24's CJS lexer fatal reproduced in shared worker threads on Windows and POSIX. Both coverage gates set Vitest's default per-test and polling budgets to 30 seconds because unrelated process, Git, SQLite, watcher, grammar, and static-gate fixtures can exceed 15 seconds only under the complete lane's concurrent Windows instrumentation. The script-only translation-pairing merge suite runs in the exempt-heavy gate because it imports only `scripts/` sources and child processes; V8 instrumentation contributes no threshold coverage there but magnifies Git-process latency. Lefthook concurrency fixtures retain their outcomes with 30-second case budgets and a 10-second process-ready probe, while the installer allows five seconds for a preempted lock owner to publish its record after exclusive creation. Directory-picker composition gives its debounced config write an explicit 15-second poll budget; workspace-context composition fixtures use a test-owned signal without an unrelated one-second deadline. These lane-scoped budgets preserve asserted outcomes, while the 120-minute job deadline still bounds a stuck run. The LSP sources and the ACL-sandbox sources remain in the Windows denominator: stub-based failure-path suites carry every in-process ACL-sandbox file to 100%, and only the runner entry stays excluded — it executes exclusively as a spawned child outside the instrumented run, its behavior pinned end-to-end by the runner suite. Narrow annotated V8 ignores cover only unreachable branches (peer-platform arms and lifecycle-unreachable guards), with their behavior tests retained on the owning platform.
|
||||
|
||||
The 16-core allocation is the measured capacity point for this inventory. Exact-head trials with four, three, and two concurrent workers inside one instrumented Vitest process exposed unreliable fixtures and worker exits, while separate single-worker child processes retain process isolation. Sixteen-shard samples and the final hosted run complete instrumented coverage in 112.66–131.33 seconds; the job gives that phase the host before starting exempt work. A 32-core comparison reduced aggregate gate time by only 1.47 seconds and still triggered the CJS-lexer fatal inside a fork worker, so additional cores did not provide a reliable wall-clock improvement.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Status: implemented
|
||||
|
||||
原生作业保留自身未被掩盖的结果。[聚合依赖决策](2026-08-22-native-windows-blocks-pull-request-aggregate.zh.md)让该结果成为 `all checks passed` 的依赖项;本文负责该作业的执行拓扑与完整清单。工作区构建、生产网站和逐文件 100% 覆盖率检查失败会使原生作业失败。静态检查、文档、包、构建产物、lint 与快照清单在同一作业内作为观测性门禁运行;其失败保持可见,但不会改变原生聚合结果,因为这些检查的阻断性判定由 Linux 负责。
|
||||
|
||||
16 核通道最多同时运行 8 道外层门禁。工作区构建、生产网站验证与 16 进程插桩覆盖率会立即启动。豁免重型覆盖率依赖构建并等待覆盖率报告合并,因此其 4 个 Vitest worker 与最多 8 个语料库子进程不会和分区阶段争用资源。轻量观测性清单同样等待覆盖率,随后与豁免工作重叠;包内临时探针满足并发源码检查,只属于脚本的探针则使用隐藏文件名。观测性清单启动时,`publint` 最多使用 8 个 worker。每个 Vitest 项目都使用 fork worker,因为 Node 24 的 CJS lexer 致命故障可在 Windows 与 POSIX 的共享 worker 中复现。两项覆盖率门禁都将 Vitest 默认的单测试和轮询时间预算设为 30 秒,因为在完整通道并发的 Windows 插桩下,多个互不相关的进程、Git、SQLite、watcher、语法和静态门禁 fixture(测试前置数据)可能超过 15 秒。translation-pairing 合并套件只导入 `scripts/` 源码和子进程,因此放入豁免重型套件门禁;V8 插桩不会为它贡献任何阈值覆盖率,却会放大 Git 进程延迟。Lefthook 并发 fixture 保留原有结果,采用 30 秒单用例预算与 10 秒进程就绪探测;安装器则允许被抢占的 lock 持有者在独占创建后用 5 秒发布记录。directory-picker 组合为防抖配置写入提供显式的 15 秒轮询预算;workspace-context 组合 fixture 使用测试自有、没有无关 1 秒截止时间的信号。这些只属于该通道的预算保留了原有断言结果,120 分钟的 job 截止时间仍会约束卡死的运行。LSP 源码与 ACL 沙箱源码仍计入 Windows 分母:基于 stub 的失败路径套件把每个进程内 ACL 沙箱文件都带到 100%,只有 runner 入口保持排除——它只作为 spawn 出的子进程在插桩运行之外执行,其行为由 runner 套件端到端钉住。窄范围且带注释的 V8 ignore 只覆盖不可达分支(另一平台专属分支、生命周期内不可达的防御守卫),其行为测试仍保留在所属平台。
|
||||
16 核通道最多同时运行 8 道外层门禁。工作区构建、生产网站验证与 16 进程插桩覆盖率会立即启动。豁免重型覆盖率依赖构建并等待覆盖率报告合并,因此其 4 个 Vitest worker 与最多 8 个语料库子进程不会和分区阶段争用资源。轻量观测性清单同样等待覆盖率,随后与豁免工作重叠;包内临时探针会以满足源码扫描要求的完整内容原子发布,只属于脚本的探针则使用隐藏文件名。观测性清单启动时,`publint` 最多使用 8 个 worker。每个 Vitest 项目都使用 fork worker,因为 Node 24 的 CJS lexer 致命故障可在 Windows 与 POSIX 的共享 worker 中复现。两项覆盖率门禁都将 Vitest 默认的单测试和轮询时间预算设为 30 秒,因为在完整通道并发的 Windows 插桩下,多个互不相关的进程、Git、SQLite、watcher、语法和静态门禁 fixture(测试前置数据)可能超过 15 秒。translation-pairing 合并套件只导入 `scripts/` 源码和子进程,因此放入豁免重型套件门禁;V8 插桩不会为它贡献任何阈值覆盖率,却会放大 Git 进程延迟。Lefthook 并发 fixture 保留原有结果,采用 30 秒单用例预算与 10 秒进程就绪探测;安装器则允许被抢占的 lock 持有者在独占创建后用 5 秒发布记录。directory-picker 组合为防抖配置写入提供显式的 15 秒轮询预算;workspace-context 组合 fixture 使用测试自有、没有无关 1 秒截止时间的信号。这些只属于该通道的预算保留了原有断言结果,120 分钟的 job 截止时间仍会约束卡死的运行。LSP 源码与 ACL 沙箱源码仍计入 Windows 分母:基于 stub 的失败路径套件把每个进程内 ACL 沙箱文件都带到 100%,只有 runner 入口保持排除——它只作为 spawn 出的子进程在插桩运行之外执行,其行为由 runner 套件端到端钉住。窄范围且带注释的 V8 ignore 只覆盖不可达分支(另一平台专属分支、生命周期内不可达的防御守卫),其行为测试仍保留在所属平台。
|
||||
|
||||
16 核配置是这项清单经实测选定的容量规格。在单个插桩 Vitest 进程内使用 4 个、3 个和 2 个并发 worker 的分支头精确试验暴露出不稳定的 fixture 与 worker 退出,而相互独立的单 worker 子进程保留进程隔离。16 分片样本与最终托管运行会在 112.66–131.33 秒内完成插桩覆盖率;作业会先把宿主资源交给该阶段,再启动豁免工作。32 核对比仅将聚合门禁时间缩短 1.47 秒,且仍在 fork worker 内触发 CJS lexer 致命故障,因此增加核心数没有带来可靠的墙钟时间改善。
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { join, relative } from 'node:path'
|
||||
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'
|
||||
import { basename, dirname, join, relative } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { flattenDiagnosticMessageText, parseConfigFileTextToJson } from 'typescript'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
@@ -44,6 +44,21 @@ function hiddenProbeName(prefix: string, suffix: string, extension = '.ts'): str
|
||||
return `.${prefix}-${suffix}${extension}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a complete probe so concurrent repository scans never read a partial write.
|
||||
* @param path - Final probe path that the owning project must discover.
|
||||
* @param source - Complete TypeScript source to publish.
|
||||
*/
|
||||
async function publishProbe(path: string, source: string): Promise<void> {
|
||||
const staging = join(dirname(path), `.${basename(path)}.staging`)
|
||||
try {
|
||||
await writeFile(staging, source)
|
||||
await rename(staging, path)
|
||||
} finally {
|
||||
await rm(staging, { force: true })
|
||||
}
|
||||
}
|
||||
|
||||
async function writeContractConfig(suffix: string): Promise<string> {
|
||||
const path = join(repositoryRoot, `.oxlintrc.contract-${suffix}.json`)
|
||||
await writeFile(path, JSON.stringify({ extends: ['./.oxlintrc.json'], ignorePatterns: [] }))
|
||||
@@ -76,7 +91,7 @@ probePromise()
|
||||
const paths: Array<readonly [label: string, path: string, tsconfig: string]> = []
|
||||
for (const [label, parent, tsconfig, extension = '.ts'] of probes) {
|
||||
const path = join(repositoryRoot, parent, `oxlint-contract-${suffix}${extension}`)
|
||||
await writeFile(path, source)
|
||||
await publishProbe(path, source)
|
||||
paths.push([label, relative(repositoryRoot, path), tsconfig])
|
||||
}
|
||||
const clientScript = 'scripts/client-bundle-purity.spec.ts'
|
||||
|
||||
Reference in New Issue
Block a user