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, knip, 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. 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-wheelbuilds the keyless SDK wheel.runtime-linux-x64,runtime-linux-arm64,runtime-macos-arm64each 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.publish-pythonuploads the four.whlfiles to the project's Package Registry withtwine, guarded by a tag-vs-package.jsonversion assertion. GitLab does not overwrite an existing version, so each release needs a freshpython-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):
| Job | Runs | What it does |
|---|---|---|
node-24 / static | Linux enterprise pool | pnpm run check:ci:static |
node-24-coverage | Linux | pnpm run check:ci:coverage (exhaustive coverage) |
node-24-consumers | Linux | pnpm run check:ci:consumers (snapshots, artifacts, Playwright-based web) |
node-compat (matrix) | ubuntu-latest | Node 22.19 and 26: check:node-compat |
python-sdk | ubuntu-latest | Python 3.10 keyless suite via uv |
python-runtime | reusable build | release-shaped Linux x64 exe build |
windows | ubuntu-latest | Wine: runs scripts/wine-windows-gates.sh (Windows Node under Wine) |
windows-native | real Windows | check:ci:windows-complete |
serial-linux-selfhosted / serial-windows | self-hosted | push-only standby drills (hot-standby evidence for the failover pools) |
all-checks-passed | ubuntu-latest | if: always() aggregate verdict; fails on any non-success needed job |
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:
$ git log --oneline --grep=release -15
abe560f81e release(dsh)
8c1e8d9890 build(release): publish the dsh family publicly
8a954b2eca release(dsh): 0.1.0-rc.3
60b04b6ef7 release(dsh): 0.1.0-rc.2
22ab3beac1 release(dsh): 0.1.0-rc.1
7bedce822f release(vendor)scripts/release/ implements it:
| Script | Role |
|---|---|
bump.ts | Bump 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.ts | Verify release version against the workspace state |
pack.ts | Build and pack tarballs into a dist/npm dir |
verify-packed-install.ts | Install the packed tarballs (plus vendor and Landlock tarballs) and prove they resolve |
publish.ts | Upload the exact packed bytes to npm |
The .github/workflows/release.yml workflow 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), then a separate manual publish job backed by the environment: npm-publish gate uploads the bytes when dispatched from a dsh-v* tag. The vendored framework family releases through its own release-vendor.yml; the Landlock native package through native/landlock-run. THIRD_PARTY_NOTICES.md is regenerated by lefthook whenever a dependency edit changes the payload.
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, whitespacegit 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.
knip unused-code checks (knip.json)
npm run knip (knip --treat-config-hints-as-errors) detects unused dependencies/files/exports across the workspace. Vendor and python/sdk-runtime are ignored; several binaries (bwrap, icacls, musl-gcc, python3, sandbox-exec, tar, taskkill, where.exe) and deps (@yarnpkg/cli-dist, lightningcss) are whitelisted.
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, knip, 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. 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
| Tool | Version (from root package.json devDependencies) |
|---|---|
| vitest | ^4.1.8 |
| oxlint | |
| oxlint-tsgolint | |
| knip | ^6.16.1 |
| jscpd | ^5.0.12 |
| lefthook | ^2.1.9 |
| tsx | ^4.22.4 |
| typescript | ^6.0.3 |
| publint | ^0.3.21 |
| tsdown | ^0.22.2 |
Further reading
- Testing strategy — what each
check:ci:*gate actually runs. - Runtime invariants — the
verify-package-invariantsgate 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.tsandscripts/release/bump.ts— gate orchestration and family versioning.lefthook.yml— the exact pre-commit/pre-push jobs and their globs.