Autistic stability for ADHD projects. Mathematical planning. Saga recovery.
dispat turns conventional commits into versions, then builds and publishes changed packages in dependency order.
Mathematical version rules make the plan repeatable: identical history and configuration produce the same versions.
Saga recovery preserves recorded publications so the next run can finish the remaining packages. Preview the work with dispat status before releasing.
Build and publish stages run your own shell commands, so a package in any language can take part. Automatic manifest editing covers a defined list of supported formats. dispat works with one package, a monorepo, or several repositories joined through a control repository or a choreographed fleet. Independent source histories and choreographed fleets require the 1.11 release candidate; the default installers select the latest stable release.
Install the binary, run dispat init in your Git repository, and edit the generated dispat.json for your packages.
The setup guide walks through your first release.
# Linux and macOS
curl -fsSL https://raw.githubusercontent.com/yohimik/dispat/main/install.sh | sh# ...or with wget
wget -qO- https://raw.githubusercontent.com/yohimik/dispat/main/install.sh | sh# Windows, in PowerShell
irm https://raw.githubusercontent.com/yohimik/dispat/main/install.ps1 | iexThe install script downloads the binary for your platform and verifies its checksum. No language runtime is required.
Coding agents should also use the version-matched agent work guide.
# ...or with npm (Node.js required)
npm install --global @dispat/bin --allow-scripts=@dispat/binThe npm package installs the same binary for your platform. The flag above approves its installer because npm 12 blocks dependency install scripts by default.
If an earlier install skipped the script, repair it from any directory with
npm explore --global @dispat/bin -- node build/bin/postinstall.js. A failed dispat launch also prints a repair
command for the exact installed path. Use npm to update or remove this installation.
# ...or on GitHub Actions
- uses: yohimik/dispat@v1
- run: dispat --log-format jsonPull container images for any other CI system: yohimik/dispat-alpine, -ubuntu, -debian, and -dind. See
dispat in CI.
dispat brings version planning, builds, publication, and recovery into one dependency graph. It is especially useful when a release crosses toolchains or destinations, and completing it requires more than running a build in order.
- Deep dependency chains across toolchains. Release
base image → runtime image → application image, orGo module → Go module → binary → Docker image → derived image, with the same scheduler. Each provider can require its consumers to wait for publication before building: setisBuildWaitingPublish: truewhen a consumer needs a version fetched from a registry. Local workspace builds can instead use the provider's build output. The graph carries these requirements through every level. See the Go and Docker examples. - Saga-style release recovery. Publication consists of separate external writes. dispat records each successful package publication with a Git tag and uses those records to plan unfinished work on a rerun. A package failure leaves completed publications in place while unaffected graph branches can continue. If an upload succeeds before its record exists, inspect that destination before retrying. Publish scripts must reconcile their own partial writes; there is no automatic rollback across registries. See the recovery experiments.
- Useful for a single-package project. One package can still ship a binary, a container, release notes, and a website. Use the same versioning, channels, and recovery model without introducing a monorepo. Give independently recoverable deliverables their own package records, or make a multi-destination publish script resume each pending destination. See single-package releases.
- Monorepos and polyrepos use the same release model. A monorepo declares packages and their dependencies in one
checkout. A control repository can assemble separate repositories through
Git submodules and coordinate them with one graph. In its source-history mode (
polyrepo: true), dispat reads each linked repository's own commits and tags, those repositories own their release records, and a submodule pointer move establishes the source range instead of counting as a package change. In pointer-history mode, the default, the control repository's pointer-update commits carry the release intent, and its own history holds the versions, changelogs, tags and records. Pick the mode that matches where your release intent is already written. A fleet without a control repository activates automatically when the entry states its own repository identity; its optional roster lists other peers.--polyrepo=falseis the local escape for one invocation. - Your manifests and commands remain the inputs. Build and publish stages run the shell commands you configure, so
the language of a package is your choice rather than dispat's. Configure the toolchain's checks, credentials, and
artifact validation in those stages. Reading manifests is the separate half: dispat parses a
defined list of formats across Go, npm, Cargo, Python, Docker, mobile and
game projects, derives dependency edges from them with
dispat compute, and rewrites the versions it supports throughautoVersion. Commit messages provide version intent and release notes. - Plan only the work the release needs. Git history and release tags determine the changed packages and affected
consumers. Unchanged packages stay outside the release plan. BuildKit layers, Go's build cache, and other existing
caches can speed up the selected stages; dispat does not require a separate task-cache service. Preview the package
versions and scope with
dispat statusbefore releasing through CI.
$ dispat status
12:04:05 INF ● changed bump=minor package=core version="1.2.3 -> 1.3.0"
12:04:05 INF ● changed bump=patch package=app dueToProviders=[core] version="0.8.1 -> 0.8.2"
12:04:05 INF release plan ready packages=3 releasing=2The same configuration supplies version, build, publish, and announcement stages, with hooks for project-specific work. dispat coordinates their order and release records; your existing tools produce and publish the artifacts.
dispat draws on tools and ideas that make complex work easier to inspect, compose, and recover:
- Linux and Git guided the CLI design: focused commands, explicit inputs, useful exit codes, and tools that work
together. The shell tools used on Linux inspired
dispat ifanddispat for, which expose familiar conditional and looping control flow as commands. Linux's permission classes (others, group, owner) also inspired the configuration scopes: global defaults for all packages, space settings for a group of packages, and a package's own settings. Git also supplies the history and release records that let you inspect how a release was planned and what it completed. - Docker Compose, Swarm, and Kubernetes inspired the move from local configuration to coordinated systems. Compose describes services in YAML, and compatible service definitions can become a Swarm stack running across machines. Kubernetes uses declarative manifests to describe cluster workloads. dispat brings that idea to releases: start with packages in one repository, then link repositories into a coordinated release workflow while keeping each repository's package configuration.
- Database recovery and sagas inspired the approach to reliable releases: record completed work, coordinate concurrent runs, and recover after partial failure. Garcia-Molina and Salem's Sagas (1987) describes long transactions made of smaller, independently committed steps. dispat applies that structure to publishing: it writes a Git tag after each successful publish and uses those records to plan unfinished work. A release lock coordinates concurrent runs, without a separate release database or lock service. If a publisher succeeds before its tag is written, check the destination before retrying. See recovery behavior.
- Lerna, and the workspaces of npm and pnpm it grew up beside. Between them they proved that many packages in one repository can share a dependency graph. They also proved that versioning and publishing all of them can be one command. dispat takes that idea beyond JavaScript and rebuilds it around an explicit dependency graph and an explicit error model.
- Conventional Commits: commit messages as machine-readable release intent.
The dispat parser,
pkg/ccme, implements a strict superset of Conventional Commits 1.0.0 that adds the monorepo dimension. It treats scopes as packages and handles propagation depth and prerelease channels.
- Agent work guide: if you are a coding agent working with dispat, start with
dispat --versionand read the versioned guide linked bydispat --help. Use the latest published guide patch on that CLI's major/minor release line, not the guide from another release line or the default branch. If you cannot check for newer patches, use the pinned guide from help. Keep configuration and API references pinned to the installed CLI release, and follow the guide's CI/CD release workflow. - dispat: the CLI itself. Read a terminal tour, the key features, and the full documentation. This includes getting started, an example per package manager, concepts, CLI, configuration, commit messages, script environment, architecture, and coverage.
- ccme: the dispat Conventional Commits extension as a standalone Go parser. It includes the vendored specification, the two-axis propagation grammar, performance notes, and fuzzing.
- models: the public configuration model. External tooling uses this to author dispat configs as typed values and marshal them to loadable files.
- config: the configuration loader as a standalone Go library. It parses JSON, YAML, and TOML into
one tree, composes files through
$ref, finds the file a command was run beneath, and decodes through setter tables with no reflection at all, which is what lets it link under TinyGo. This is the library behind dispat's own config reading. - manifest: the shared manifest vocabulary. It defines dependency kinds, manifest file-name rules, and name normalisation. This keeps the reader and writer halves in exact agreement.
- scanner: the manifest reader as a standalone Go library. It parses package.json, go.mod,
Cargo.toml, pyproject.toml, composer.json, pom.xml, the .NET project, nuspec and packages family, pubspec.yaml,
Gemfile, .gemspec, and requirements files into one ecosystem-neutral shape. It also reads Dockerfiles, compose files,
and the mobile platforms (Info.plist, project.pbxproj, Podfile, and .podspec on iOS; AndroidManifest.xml, Gradle
version catalogs, and build scripts on Android). This is the library behind
dispat compute, auto-versioning, and thedispat scannercommand. - writer: the manifest writer. It makes targeted, validated edits for the supported writable
manifests and preserves their supported structure and formatting. Format-specific tools such as the Go formatter
may normalize the file. Writes are atomic. This is the library behind auto-versioning and the
dispat writercommand. - docker: the four container images. Each is a dispat package whose
docker-compose.ymlis its manifest. The build stage runsdocker compose build, and the publish stage runsdocker compose build --push. - infra: the Google Cloud footprint that serves dispat.dev, written only from CI
through the dispat release: Terraform plans in the build stage, applies in the publish stage, and each applied state
is an
infra/v*tag. - Integration tests: the black-box suite that compiles the real binary and drives it against disposable git repositories. Read about setup, running, results, and the test plan.
- npm package: install the CLI globally or in a Node.js project, repair an installation whose scripts were disabled, and read how npm package versions track the native binary.
- docs: the documentation site itself, released by dispat like any other package. Learn how to run it locally, why its build is the link checker, and how a version snapshot and a deploy are cut.
- dispat itself: this repository is a polyglot Go, npm, and Docker workspace. It is released by the dispat binary
built from its own checkout, and it has been since the very first release. The first stable run cut eleven packages
in one release. It rewrote the go.mod files of six Go modules to the released versions, regenerated go.sum files, and
tagged each module the way Go expects (
pkg/ccme/v1.0.0,services/dispat/v1.0.0). This keepsgo install github.com/yohimik/dispat/services/dispat@latestworking. It also attached six cross-compiled binaries to the CLI's GitHub release, published the four container images, and released the versioned documentation site. Every package received its own tag, changelog entry, and GitHub release. The release also announces itself: itsannouncestage renders a card from the release notes it just wrote and posts it to Instagram, LinkedIn, and Discord with crier, from the same Actions run, which the announcement page describes. - crier: a single-package repository, one Go module and one binary, which
renders HTML templates to images and video and posts them to fourteen social platforms. It shows that dispat needs no
monorepo. Its first release was a breaking change on the
rcchannel, and eighteen release candidates followed in two days, each with its own changelog entry and its own GitHub release flagged as a prerelease. One transition commit then graduated the train to 1.0.0: the stable version is computed over the whole train rather than counted by hand, and the graduation collects every candidate's notes into the one entry stable readers see. This is the train and graduation flow that Kubernetes runs its own releases through, here driven by conventional commits alone. A movingv1alias tag is scoped to the stable channel, souses: yohimik/crier@v1never picks up a candidate. crier also shows how a release integrates with dispat: theannouncestage of the run renders a card from the release-notes variables dispat sets and posts it from the binary the run just built, and its release-changelog example wires the same card into any dispat release as one more publish step. It installs withdispat install yohimik/crier.
Bring your questions, issues, or projects you release with dispat to the community. Come and say hello: discord.gg/83PwVSCCmk.
You can also open bugs and feature requests as GitHub issues, whichever suits you better.
Official dispat release binaries are available under MIT, including the dispat executable in the official container images. Third-party components retain their own license terms.
Source files that reference or incorporate the GPL-covered CCME specification, algorithms or proofs are licensed under GPL-3.0-or-later and carry an explicit SPDX notice. The CCME specification and its accompanying material are also GPL-3.0-or-later. The CCME parser remains MIT, including its specification references. Other source files remain MIT unless they have a separate license notice.
The MIT grant for official binaries does not offer an MIT alternative for GPL-designated source files. See LICENSE for the scope of each grant. Previously distributed copies retain their original licenses.

