Development Workflow
From finishing code to merging into main, there are several checkpoints: local build and test, the complete harness suite, changeset, and CI publishing. This page walks through the day-to-day workflow in that order.
Environment Setup
- Node.js
^20.19.0or>=22.12.0 - pnpm 9 (repo pins
packageManager: pnpm@9.0.2)
pnpm install # Install all workspace dependenciesCommon Commands
pnpm dev # Start examples/minimal-bot (Sandbox + Console); recommended for first-time verification
pnpm dev:full # Start examples/full-bot (L4 reference)
pnpm dev:test # Start examples/test-bot (maintainer kitchen sink)
pnpm build # Turbo builds all packages in order: basic -> packages -> plugins
pnpm test # Full Vitest run
pnpm type-check # tsc --noEmit (tsconfig.typecheck.json)
pnpm lint # ESLintWhen verifying a single package, prefer pnpm --filter <pkg> build|test instead of running a full build by default.
If you get an error about @zhin.js/scaffold-wizard not being found before working on CLI or create-zhin-app, build it first (output goes to lib/; Node cannot resolve it before building):
pnpm --filter @zhin.js/scaffold-wizard build # or pnpm prepare:cliHarness CI Gates (pnpm check:all)
The most valuable command to run before committing is pnpm check:all: it runs every check registered in scripts/check-all-harness.mjs (including type-check, lint, and unit tests). All must pass to go green -- CI runs the exact same suite. If CI runs a separate coverage job, you can set HARNESS_SKIP_TEST=1 to skip the pnpm test portion and avoid running tests twice.
Below are the checks grouped by responsibility (the command in parentheses can be run individually).
Quality Baseline
| Check | Description |
|---|---|
Type Check (pnpm type-check) | tsc --noEmit |
Lint (pnpm lint) | ESLint (.ts/.tsx) |
Unit Tests (pnpm test) | Full Vitest run |
Production Config (pnpm check:prod) | No debug code in production config |
Architecture & Dependencies
| Check | Description |
|---|---|
Architecture Layers (pnpm check:architecture) | Layer dependency direction (basic -> kernel -> ai -> core -> agent -> zhin) |
Dependency Policy (pnpm check:dependency-policy) | Scaffold dependency policy, Changesets config, and internal peer ranges |
Release Plan (pnpm check:release-plan) | Patch-only by default; minor/major require an owner approval record |
No Koa Import (pnpm check:no-koa) | Plugins must not directly import koa |
Install Size (pnpm check:install-size) | zhin.js IM core production node_modules <= 10MB |
API Snapshots & Plugin Spec
| Check | Description |
|---|---|
API Surface (pnpm check:api-surface) | Public API surface snapshot |
Plugin Runtime API (pnpm check:plugin-runtime-api) | Convention-based plugin runtime API surface snapshot |
Plugin Spec (pnpm check:plugin) | Plugins conform to standard spec |
Plugin Agent Publish (pnpm check:plugin-agent-publish) | Plugins with agent/ have proper publish checklist (files, prepublishOnly, peer deps) |
Publish Repository (pnpm check:publish-repository) | Publishable packages have repository.url matching github.com/zhinjs/zhin (npm provenance) |
Agent Tool Schema (pnpm check:agent-tool-schema) | agent/tools inputSchema matches defineAgentTool/execute types |
No Package-Root skills/ (pnpm check:no-package-skills) | Plugin packages must not have top-level skills/; use agent/skills/*.md instead |
IM Chain & Runtime Conventions
| Check | Description |
|---|---|
IM Send Path (pnpm check:harness-paths) | Must not bypass the Adapter.sendMessage unified chain |
IM Session SSOT (pnpm check:im-session-ssot) | IM scene/session identity resolution uses core SSOT |
usePlugin Top-Level (pnpm check:use-plugin-top-level) | usePlugin() must be at module top level |
getPlugin Runtime (pnpm check:get-plugin-runtime) | getPlugin() is forbidden inside runtime callbacks |
Workroom SSOT (pnpm check:workroom-ssot) | Workroom state must go through the Journal + CAS Kernel; parallel mutable authorities are forbidden |
AI Layer
| Check | Description |
|---|---|
getModel Import Disambiguation (pnpm check:get-model-imports) | Runtime code uses getLlmTransportModel, not the ambiguous getModel |
Legacy AI Exports (pnpm check:legacy-ai-exports) | @zhin.js/ai no longer exports SessionManager and similar symbols |
Provider Gateway (pnpm check:provider-gateway) | LLM gateway sdk/contextWindow preset contract |
A2A Mesh (pnpm check:a2a-mesh) | No residual MCP Agent Mesh v1 symbols |
Adapter Contracts
| Check | Description |
|---|---|
Rich Segment Adapters (pnpm check:rich-segments) | outboundRichSegmentPolicy declaration and contract tests |
AI Outbound Adapters (pnpm check:ai-outbound) | aiOutboundExtensions declaration and contract tests |
Interactive Segments (pnpm check:interactive-segments) | interactivePolicy declaration and contract tests |
Segment Adapters (pnpm check:segments) | defineAdapter segments declaration contract (sandbox must pass) |
Documentation Consistency
| Check | Description |
|---|---|
Doc Links (pnpm check:doc-links) | Documentation relative links are not broken |
Doc Orphans (pnpm check:doc-orphans) | All site Markdown files are in sidebar or allowlist |
ADR Manifest (pnpm check:adr-manifest) | ADR README and sidebar cover all ADRs |
README Exports (pnpm check:readme-exports) | README imports match package exports |
Config Docs (pnpm check:config-docs) | Config documentation aligns with DEFAULT_CONFIG key fields |
Generated Config Reference (pnpm check:config-reference) | Generated configuration fields stay aligned with Runtime source and plugin JSON Schema |
Source-owned Config Enums (pnpm check:config-enums) | Source-owned configuration enums stay aligned across Runtime/plugin Schema, generated reference, and narrative docs |
Troubleshooting Center (pnpm check:troubleshooting) | The incident catalog stays aligned with bilingual Symptom → Cause → Action → Verification pages |
Install Tiers SSOT (pnpm check:install-tiers-ssot) | Chinese README.zh-CN.md Install tiers table matches docs/snippets/install-tiers.md |
Adapter Docs Sync (pnpm check:adapter-docs) | Platform adapter docs sync with plugins/adapters/*/README.md (fix with pnpm sync:adapter-docs) |
Platform Tiers SSOT (pnpm check:platform-tiers-ssot) | Capability tiers/adapter index matches scripts/adapter-meta.mjs |
Deployment Templates (pnpm check:deployment-templates) | Compose, systemd, and Kubernetes templates match their Chinese and English download entries |
Smoke Tests
| Check | Description |
|---|---|
Stable Smoke (pnpm check:stable) | Sandbox + Agent core unit tests + minimal-bot contract |
L4-CI (pnpm check:l4-ci) | L4 deterministic subset (orchestration/memory/full-bot contract); full pnpm check:l4 runs nightly |
Changeset Workflow
The repo uses changesets for version management and changelogs (config in .changeset/config.json: baseBranch: main, access: public). Any change that affects the behavior of a published package must include a changeset:
pnpm release # = pnpm changeset; interactively select affected packages and semver level, generates .changeset/*.md
pnpm bump # = pnpm changeset version; consumes changesets, bumps version numbers, writes CHANGELOG
pnpm pub # = pnpm changeset publish; publishes to npmDuring daily development you only need pnpm release to commit the changeset file; bump and pub are executed by CI.
The default release policy permits patch releases only. pnpm check:release-plan inspects the complete Changesets plan and fails on any unapproved minor or major, including bumps inferred through dependency propagation. A non-patch release requires the version owner to record the changeset filename, package scope, release type, approver, and reason in .changeset/version-policy.json. .github/CODEOWNERS assigns owner review for that policy file.
Internal peer dependencies use workspace:^ so compatible internal releases publish as caret ranges instead of exact versions. Private examples do not participate in Changesets versioning or tags.
1.1 stable line
The official npm ecosystem uses the 1.1.x stable line. Eighty-one packages whose 1.1.0 version was still available start at 1.1.0. Seven packages that had already published 1.1.0 use their next available 1.1.x patch because npm does not permit version reuse. Historical higher versions remain available to preserve existing lockfiles and are marked as superseded by the corresponding 1.1.x stable release. Each official package points both latest and stable to that release; routine releases then increment patch only.
| Package whose 1.1.0 is reserved | Stable version |
|---|---|
@zhin.js/adapter | 1.1.12 |
@zhin.js/agent | 1.1.23 |
@zhin.js/ai | 1.1.33 |
@zhin.js/client | 1.1.5 |
@zhin.js/console-protocol | 1.1.5 |
@zhin.js/core | 1.1.35 |
@zhin.js/plugin-runtime | 1.1.9 |
Publishing (GitHub CI)
Publishing is driven by .github/workflows/publish.yml: on push to main or via Actions → workflow_dispatch.
First publish for new packages
npm no longer allows this pipeline to create a package name for the first time. Maintainers must publish once with a token; later versions go through changesets.
pnpm check:unpublished
pnpm build
(cd path/to/pkg && npm publish --access public)Then re-run Build and Publish (push main or workflow_dispatch).
PR gates are in .github/workflows/ci.yml (Node 22/24 matrix), which also runs pnpm check:all.
Debugging
- Log level: Set
log_leveltodebuginzhin.config.yml(default isinfo) to see detailed framework internal logs. - Console logs page: Runtime logs are written to the
SystemLogdatabase model viaDatabaseLogTransport. The Remote Console logs page reads from it. Open console.zhin.dev in your browser, fill in the API Base URL (e.g.http://127.0.0.1:8086) and Bearer Token (http.tokenconfig) to view. - Single-package debugging:
pnpm --filter <pkg> testwith-t '<test name>'to filter tests;pnpm test:watchto enter watch mode.