Skip to content

DeepSeek Harness ships two CI surfaces (GitHub Actions for the TypeScript monorepo and its Python SDK; GitLab CI for the native Python wheels) plus a rich set of local developer toold — lefthook git hooks, oxlint, jscpd, and the invariant/vendored-link hygiene gates. Releases follow family-based version bumps (release/dsh-* / release/vendor-*) and commit the version into the repository before a human tags — the upstream repo keeps no CHANGELOG, so every release is a mechanical version-bump commit. This page maps the whole engineering pipeline.

GitLab CI: Python release shape ​

.gitlab-ci.yml runs only for Python release tags (python-v<major>.<minor>.<patch>…). Two stages, build and publish:

  • sdk-wheel builds the keyless SDK wheel.
  • runtime-linux-x64, runtime-linux-arm64, runtime-macos-arm64, runtime-windows-x64 each build the bundled single-exe runtime for its target, then verify it: a Python smoke (python scripts/smoke-python-runtime.py), glibc <= 2.28 via readelf for Linux, manylinux smoke through a Docker image, and a macOS deployment-target check; the Windows lane runs scripts/build-exe-for-python-sdk.ts --targets=node24-win-x64 and smokes the installed-wheel path under a wheel venv.
  • publish-python uploads the five .whl files (SDK wheel + four runtime wheels) to the project's Package Registry with twine, guarded by a tag-vs-package.json version assertion and a find release -name '*.whl' count check. GitLab does not overwrite an existing version, so each release needs a fresh python-v<version> tag.

The tag-vs-version guard is central: test "$CI_COMMIT_TAG" = "python-v$DSH_VERSION" || exit 1.

GitHub Actions: the PR gate ​

.github/workflows/ci.yml is the required pull-request pipeline. It is sophisticated about runner pools and concurrency; the key lanes (all Node 24 unless noted):

JobRunsWhat it does
node-24 / staticLinux enterprise poolpnpm run check:ci:static
node-24-coverageLinuxpnpm run check:ci:coverage (exhaustive coverage)
node-24-consumersLinuxpnpm run check:ci:consumers (snapshots, artifacts, Playwright-based web)
node-compat (matrix)ubuntu-latestNode 22.19, 24.9, and 26: check:node-compat
python-sdkubuntu-latestPython 3.10 keyless suite via uv
python-runtimereusable buildrelease-shaped Linux x64 exe build
windowsubuntu-latestWine: runs scripts/wine-windows-gates.sh (Windows Node under Wine)
windows-build / windows-coveragereal Windowssplit halves of the former monolithic windows-native job (check:ci:windows-blocking; build first, then check:ci:coverage) — one slow job no longer blocks the rest
windows-native-testsreal WindowsWindows-specific native test files (pwsh loader, workflow worker, subprocess exit, sqlite differential)
windows-observationalreal Windowscheck:ci:windows-observational, continue-on-error (non-blocking)
all-checks-passedubuntu-latestif: always() aggregate verdict; fails on any non-success needed job

The former push-only serial standby drills (serial-linux-selfhosted / serial-windows) moved out of the PR verdict into the new ci-master.yml (master-push + manual workflow_dispatch for larger-runner/consolidated-runner benchmarks); ci-master.yml deliberately sits outside the PR verdict because needs cannot reach across workflow files.

The gate scripts come from scripts/run-gates.ts. check:ci, check:ci:linux-primary, check:ci:static, check:ci:coverage, check:ci:snapshot, check:ci:artifacts, check:ci:consumers, and check:ci:windows-* are all thin npm-script sugar over separate run-gates.ts orchestrations. Env knobs bound concurrency: DSH_GATE_CONCURRENCY, DSH_COVERAGE_MAX_WORKERS, DSH_SNAPSHOT_MAX_CONCURRENCY, DSH_E2E_MAX_WORKERS, DSH_OXLINT_THREADS, DSH_PUBLINT_CONCURRENCY.

Notable CI policies baked into the workflow env: DSH_TELEMETRY_DISABLED=1 (CI runs never report to the production telemetry endpoint), fetch-depth for the archive gate, Playwright Chromium for the web gates, prepare-ci-bubblewrap.sh to unrestrict the namespace for the sandbox suites, and a failover mechanism via repository variables DSH_CI_FAILOVER_LINUX / DSH_CI_FAILOVER_WINDOWS that retarget jobs onto in-house self-hosted pools.

The release process (visible from git) ​

The release flow is versioned and tag-only:

bash
$ git log --oneline --grep=release -15
99f6f02fec Merge pull request #2620 from deepseek-harness/release/dsh-0.1.0-rc.7
bb4ca698d6 release(dsh): 0.1.0-rc.7
887c4977db Merge pull request #2546 from deepseek-harness/fix/publish-deporder
d5be1d62c9 feat(release): count publish progress against the whole release set
0e50fa290c fix(release): state what the echo helper does, and drop two dead claims
7b973e27c8 feat(release): reject a module-scope load of an optional dependency
9fa0575ccc fix(release): print the publish order and the peer edges it drops
70eb76eaec fix(release): keep npm's own output in the publish log
47399764c5 fix(release): order publication by every installed dependency section
fb82698709 Merge pull request #2531 from deepseek-harness/release/dsh-0.1.0-rc.6
15148dbd9a release(dsh): 0.1.0-rc.6
47f943859b Merge pull request #2519 from deepseek-harness/feat/npm-public
abe560f81e release(dsh): 0.1.0-rc.5
8c1e8d9890 build(release): publish the dsh family publicly
124aa5f01a Merge pull request #2521 from deepseek-harness/release/dsh-0.1.0-rc.3

scripts/release/ implements it:

ScriptRole
bump.tsBump one family's version and commit it (--family dsh shares one version across members + root; --family vendor has one line per package but ships the whole family)
verify.tsVerify release version against the workspace state
pack.tsBuild and pack tarballs into a dist/npm dir
verify-packed-install.tsInstall the packed tarballs (plus vendor and Landlock tarballs) and prove they resolve
publish.tsUpload the exact packed bytes to npm

The .github/workflows/release.yml workflow (plus the vendored/native variants) runs release:verify → build → release:pack → release:verify-packed-install on every PR/push (a pack-only proof that the whole publish set still packs). Publication is a manual act: the dsh family publishes through release-publish.yml and the vendored framework through release-vendor-publish.yml — both are workflow_dispatch-only, repack the current tree at dispatch time, and are meant to be run explicitly from a dsh-v* (resp. vendor-*) tag. Neither workflow listens to pull_request/push, and both declare contents: read — CI never writes the repo. The Landlock native package releases through its own native/landlock-run workflows. THIRD_PARTY_NOTICES.md is regenerated by lefthook whenever a dependency edit changes the payload. There is no CHANGELOG: each release is a mechanical version-bump commit (release(dsh): … / release(vendor): …).

Developer tooling ​

lefthook git hooks (lefthook.yml) ​

postinstall runs node scripts/install-lefthook.mjs. Local hooks are deliberately fast checkpoints; CI owns the full matrix:

  • pre-commit: staged-lint (tsx scripts/run-oxlint.ts --config .oxlintrc.staged.json), translation pairing for *.i18n.yaml, archived-agent-notes check, whitespace git diff --cached --check, vendor manifest guard, and third-party-notices regeneration (re-generates + git add THIRD_PARTY_NOTICES.md).
  • pre-merge-commit: translation pairing + archived notes.
  • pre-push: pnpm run typecheck.

oxlint (run-oxlint.ts, .oxlintrc.json) ​

npm run lint = build:lib:host then tsx scripts/run-oxlint.ts . (the contracts-ready variant runs after the client contract build). .oxlintrc.json turns correctness rules off at the top and re-enables strict, mostly type-aware rules scoped per override; it ignores vendor/**, native/**, and *.config.ts. There is a stricter --fix staged variant (.oxlintrc.staged.json) for the hook.

jscpd duplication (jscpd.json) ​

npm run duplication runs jscpd --config .jscpd.json packages scripts with minTokens: 60, minLines: 6, mode: "mild", and an ignorePattern for explicit /* jscpd:ignore-start */ … /* jscpd:ignore-end */ blocks (used heavily in invariant companions and exports).

hygiene bundle ​

pnpm run hygiene chains the deep gates: rescope-vendor:check, publint, constraints, verify-dsh-package-licenses, verify-package-invariants, verify-built-package-invariants, verify-cordis-config, verify-node-next-types, verify-runtime-closure, verify-vendored-links. (knip was removed repo-wide — no knip.json, no knip script, no CI lane; unused-code detection is no longer part of the gates.) These guarantee the workspace is internally consistent before anything ships.

Contribution process ​

CONTRIBUTING.md states the project is early-stage and does not accept external pull requests yet; contributions happen through GitHub Discussions, ecosystem plugins (via the dsh-plugin topic), and community content. It is a deliberate gate that keeps PR review effort focused on the internal team while growing the ecosystem around the harness.

Tooling ​

ToolVersion (from root package.json devDependencies)
vitest^4.1.8
oxlint
oxlint-tsgolint
jscpd^5.0.12
lefthook^2.1.9
tsx^4.22.4
typescript^6.0.3
publint^0.3.21
tsdown^0.22.2

knip is intentionally absent: it was removed repo-wide.

Further reading ​

  • Testing strategy — what each check:ci:* gate actually runs.
  • Runtime invariants — the verify-package-invariants gate and companions.
  • Vendored libraries — verify-vendored-links, rescope-vendor, and the family releases.
  • .gitlab-ci.yml — the Python wheel pipeline and its tag guard.
  • scripts/run-gates.ts and scripts/release/bump.ts — gate orchestration and family versioning.
  • lefthook.yml — the exact pre-commit/pre-push jobs and their globs.