This guide covers the local setup needed to work on DeepSeek Harness and understand the local hooks, daily checks, and CI gates.
node >=24; CI runs the matrix on Node 24 and 26.pnpm@11.7.0 in package.json; run corepack enable if pnpm --version does not resolve through Corepack.Install dependencies from the repo root:
pnpm install
The install also runs the root postinstall script, which installs lefthook from the repo dev dependency through scripts/install-lefthook.mjs; the wrapper uses lefthook's reviewed --force mode so linked worktrees with an existing core.hooksPath do not fail normal pnpm run … commands.
If hooks are missing because dependencies were restored from cache or postinstall was skipped, install them manually:
pnpm exec lefthook install --force
Run typecheck once after a fresh clone:
pnpm run typecheck
That first typecheck builds declaration output used by type-aware linting for vendored packages. Without it, pnpm run lint can report unresolved-type no-unsafe-* errors even when source code is fine.
If you are preparing to push from a fresh clone or worktree, also build once:
pnpm run build
pnpm run hygiene includes publint, which validates package entrypoints against the built lib/*.js files. A fresh worktree has no bundled JS until pnpm run build runs.
The real DeepSeek adapter and coding-agent demo read credentials from the environment or from a gitignored .env at the repo root:
DEEPSEEK_API_KEY=sk-...
DEEPSEEK_BASE_URL=https://... # optional
DEEPSEEK_BASE_URL is optional and defaults to the public API. Never commit real credentials. The real-API e2e suites self-skip when DEEPSEEK_API_KEY is not set.
lefthook is configured in lefthook.yml as an early local checkpoint before review:
pre-commit runs staged-file ESLint fixes, pnpm run typecheck, and the vendor manifest guard.pre-push runs pnpm run test, pnpm run test:snapshot, pnpm run hygiene, pnpm run doc-sync, and pnpm run verify-module-graph.The vendor manifest guard checks that changes under vendor/*/src are staged with the matching vendor/README.md manifest update. See vendor/README.md before editing vendored code.
These hooks do not exactly mirror CI. Notably, pre-push runs unit tests without coverage, while CI runs pnpm run test:coverage; CI also runs an echo-agent smoke test and exercises the matrix on Node 24 and 26.
The GitHub workflow runs these gates on each pull request:
pnpm install --frozen-lockfilepnpm run constraintspnpm run typecheckpnpm run lintpnpm run doc-syncpnpm run verify-module-graphpnpm run test:coveragepnpm run test:snapshotpnpm run buildpnpm run knip && pnpm run publintpnpm run hygiene is the local shorthand for pnpm run knip && pnpm run publint && pnpm run constraints; CI splits pnpm run constraints into its own earlier step, then runs pnpm run knip && pnpm run publint after pnpm run build.
Use these from the repo root:
pnpm run test # unit tests
pnpm run test:coverage # unit tests with per-file coverage gates
pnpm run test:e2e # real-API tests; self-skips without DEEPSEEK_API_KEY
pnpm run typecheck # build declarations, then typecheck source, tests, and examples
pnpm run lint # eslint .
pnpm run lint:fix # eslint . --fix
pnpm run doc-typecheck # compile checked TypeScript snippets in Markdown docs
pnpm run gen-cordis-catalog # regenerate docs/cordis-catalog/events-and-services.md from source
pnpm run verify-cordis-catalog # fail if the cordis events/services catalog is stale
pnpm run verify-md-wrap # fail on hard-wrapped prose paragraphs in docs/README markdown
pnpm run verify-type-equiv # fail if a ```ts type-equiv doc block drifts from its source type
pnpm run doc-sync # doc-typecheck, cordis-catalog freshness, markdown wrap/link, and type-equiv verification
pnpm run gen-module-graph # regenerate docs/module-graph.md from package peerDeps
pnpm run verify-module-graph # fail if docs/module-graph.md is stale
pnpm run build # build declarations and JS bundles
pnpm run hygiene # knip, publint, and workspace constraints
When changing package public behavior, update the relevant README or JSDoc in the same change. pnpm run doc-sync catches checked TypeScript snippets, cordis events/services catalog drift, and hard-wrapped markdown prose, but broader prose/API sync still needs review.
The echo demo does not need API credentials:
pnpm run demo:echo
The coding-agent demo uses the real DeepSeek adapter and needs DEEPSEEK_API_KEY in the environment or repo-root .env:
pnpm run demo:coding
Use one of three comment tags to flag known issues in the code, ordered by urgency:
FIXME — an issue that should block a new release. A release should not ship with an open FIXME unless reviewers explicitly agree the change can be merged anyway.TODO — an issue that should be fixed soon, once we have the resources.XXX — an issue that we may fix someday; lowest priority, no commitment.Pick the tag that matches the urgency so anyone scanning the code can tell a release blocker from a someday-maybe.
ts type-equiv)The core data structures docs paste real type definitions so a reader sees the exact shape. To keep a paste from drifting when source changes, fence it as ts type-equiv ` (instead of `ts) and register it in scripts/type-equiv.manifest.json with the source file and symbol it mirrors:
{ "doc": "docs/core-data-structures/session.md", "symbol": "SessionEvent", "source": "packages/core/session/src/types.ts" }
pnpm run verify-type-equiv (part of doc-sync) then extracts that symbol's declaration from source via the TypeScript parser and asserts the block matches it (whitespace- and comment-insensitive, so a doc block may show a clean definition and the prose can carry the semantics). It also enforces a 1:1 correspondence: every ts type-equiv block has exactly one manifest entry and vice-versa, so a block can't go silently unchecked and a stale entry can't linger. doc-typecheck skips ts type-equiv blocks (they aren't standalone-compilable) and excludes them from its opt-out ratio. When you change a documented type, the gate fails until you update the paste; when you add or remove a block, update the manifest in the same change.
Read docs/architecture.md before changing anything under packages/. The codebase is built around Cordis plugins, event-sourced sessions, typed service seams, and explicit extension points.