Safe release coordination for repositories where “CI passed” is necessary, but no longer sufficient.
Patchlog is a release control plane for protected Git repositories. It turns repository state into an immutable, content-addressed plan; requires approval of that exact plan before mutation; separates the version-bump pull request from final tagging; proves required checks for the exact protected commit; and leaves machine-readable evidence connecting reviewed intent to public release state.
It does not replace CI, GoReleaser, language-specific build tools, registries, or package managers. Those systems remain the execution plane. Patchlog coordinates their trust boundaries.
Most teams do not need another release tool. If one CI job already transforms one commit into one trustworthy artifact, keep that system.
Patchlog becomes useful when a release begins to resemble a distributed transaction across systems that do not share a transaction coordinator:
- Git owns commits, branches, and tags;
- branch protection owns merge policy;
- CI owns check results;
- provider APIs own releases;
- registries and package managers own distribution;
- changelogs and documentation own the human record.
Each subsystem can report success while the release as a whole is still incorrect. CI may be green for one commit while a tag points to another. A version bump may be reviewed, then applied to a changed worktree. A provider release may exist while one registry publication failed. A retry may repeat a non-idempotent operation. Automation increases throughput, but throughput does not prove correspondence.
Patchlog therefore optimizes for a stronger invariant:
approved prepare plan → exact version mutation
green protected SHA → exact immutable tag
approved final plan → verifiable release identity
The core value is not generating more output. It is closing the epistemic gap between “this is what we approved” and “this is what the world received.”
| Keep the simpler system | Patchlog becomes valuable |
|---|---|
| One deterministic CI job owns the whole release | The release crosses several independently failing systems |
| Tags are created automatically from an already trusted commit | Humans still coordinate bumps, merge state, tags, or retries |
| A failed release is cheap to discard and repeat | A wrong, duplicated, or unverifiable release is expensive |
| No protected-branch or audit requirement exists | Required CI must be proven for the exact released SHA |
| The project is an early prototype | The CLI contract, supply chain, or operational record matters |
Patchlog is deliberately unnecessary for trivial release paths. Its design center is the point where informal choreography has become operational risk.
Install Patchlog, enter a clean repository with a discoverable version source, and ask it to explain the next release:
go install github.com/fxdv/patchlog/cmd/patchlog@latest
cd your-repository
patchlog release --dry-runThe command either returns an immutable plan and the exact approval command, or rejects the release before mutation with a classified reason and a concrete next step. You do not need to study the advanced feature set before obtaining the first useful result.
Git history + repository state + provider policy
│
▼
immutable prepare plan
│
approve exact fingerprint
│
transactional bump branch
│
pull request + required CI
│
protected merge
│
immutable finalize plan
│
approve exact fingerprint
│
exact-SHA policy proof + tag push
│
▼
existing build and distribution tooling
│
▼
checksums + attestations + release receipt
The protocol has four properties.
patchlog release --dry-runThis is the universal entry point. Patchlog detects whether the repository
needs prepare or finalize, evaluates every deterministic preflight, and
prints a sha256: approval fingerprint.
Dry-run does not write version files, Git refs, caches, reports, changelogs, or remote resources. Its immutability is tested by comparing filesystem and Git state before and after orchestration runs.
For an approval service or audit archive, emit the versioned JSON contract:
patchlog release --dry-run --plan-json --quiet > reviewed-plan.jsonShell redirection is owned by the caller; the Patchlog process remains
side-effect-free. The schema is
release-plan/v1.
Approval is not a generic “yes, release now.” It names one exact plan:
patchlog release prepare --approve sha256:<prepare-fingerprint>The fingerprint covers the release phase, HEAD commit, protected and release branch identities, current and target versions, tag, requested actions, mutation targets, exact file paths and modes, before/after file hashes, and stable commit-policy evidence.
If relevant state changes between review and apply, the fingerprint changes. The previous approval is rejected rather than reinterpreted.
Protected repositories have two different decisions:
- Prepare: approve the exact version mutation and push it to an isolated release branch.
- Finalize: after the pull request is merged, approve tagging the exact protected-branch commit that passed required CI.
Patchlog does not collapse those decisions into one command because the merge itself is a governance boundary. A squash, rebase, merge queue, or subsequent protected-branch update can legitimately change commit identity.
Prepare:
patchlog release --dry-run
patchlog release prepare --approve sha256:<prepare-fingerprint>Patchlog creates release/vX.Y.Z, transactionally applies only the files named
by the bump plan, commits through an isolated Git index, and pushes the review
branch without creating a release tag.
Open the pull request, require CI, merge it, and wait for post-merge CI on the protected branch.
Finalize:
patchlog release --dry-run
patchlog release finalize --approve sha256:<finalize-fingerprint>Finalize requires the local protected branch, its remote-tracking branch, and the remote branch to identify the same commit. It then proves that GitHub’s required checks succeeded for that exact SHA, creates an annotated tag bound to the approved plan fingerprint, revalidates policy immediately before the network boundary, and pushes only the immutable tag.
A release is not trusted merely because its publication step returned success. Patchlog’s reference release workflow closes the trust loop:
- rerun the required quality gate;
- prove
tag == "v" + VERSION; - prove the checked-out commit equals the immutable tag target;
- build explicit platform archives with specialized build tooling;
- publish
SHA256SUMS; - attach signed provenance attestations;
- publish a versioned release receipt;
- download and validate every checksum;
- verify provenance;
- execute an archive and verify its reported version;
- verify
go install ...@versionand stable@latest; - install and test the Homebrew formula and Scoop manifest.
The receipt schema is
release-receipt/v1. Patchlog’s
own workflow checksums and attests the receipt so it can be verified separately
from the workflow that created it.
Patchlog cannot make GitHub, a registry, and every package manager participate in a literal atomic commit. It can make the local boundary transactional, preflight remote actions before mutation, and report partial external completion without pretending rollback occurred.
- Version bumping returns the exact changed-file list; release files are never
inferred from incidental
git statusoutput. - Multi-file bumps roll back if any write fails.
- Prepare commits through an isolated index, so unrelated staging state cannot enter the release commit.
- Repository and remote state are revalidated immediately before apply.
- Finalize verifies commit policy during planning, during apply, and again immediately before tag push.
- If the final policy check fails, Patchlog removes the local tag and performs no remote push.
- If an irreversible remote step succeeds before a later remote failure, Patchlog reports both the completed operation and the failure boundary.
- HTTP retries are method- and idempotency-aware; external response bodies are bounded.
The result is not magical atomicity. It is explicit state, bounded mutation, and failures that remain diagnosable.
For the stable protected contract, GitHub is the supported policy provider. Patchlog reads classic branch protection and applicable repository rules, collects required check runs and commit statuses for the exact finalize SHA, honors the required GitHub App integration ID when supplied, and fails closed when evidence is missing or incomplete.
Required-workflow rules whose workflow identity cannot yet be proven are rejected rather than approximated with a same-named status check.
Minimal provider configuration:
repo: your-org/your-repository
release:
protected_branch: main
branch_prefix: release/
tag_prefix: v
provider:
type: github
repo: your-org/your-repository
token: ${GITHUB_TOKEN}Protected prepare does not require provider access. Protected finalize needs read access to branch administration and checks for private repositories or policies unavailable anonymously. Provider publication may require additional contents permission.
GitLab and Gitea remain available through explicit direct compatibility mode; they are not presented as equivalent protected-policy implementations.
Patchlog can auto-detect common language manifests, but a root VERSION file
is the simplest stable contract:
1.2.3
For an explicit language-neutral source:
bump:
auto_detect: false
files:
- VERSIONThe bump planner owns the exact file list. Patchlog does not derive release files from a dirty worktree.
Rust virtual workspaces with a root [workspace] table but no root
[package].version are rejected before mutation. Patchlog does not guess which
members share a release. Teams may use a root VERSION file or retain
language-aware workspace coordination in existing tooling.
Requires Go 1.22 or newer:
go install github.com/fxdv/patchlog/cmd/patchlog@latest
patchlog --versionbrew tap fxdv/tap
brew install patchlog
patchlog --versionThe Homebrew tap consumes a checksum-pinned formula rendered by the release workflow and tests installation before updating the tap.
scoop bucket add fxdv https://github.com/fxdv/scoop-bucket
scoop install fxdv/patchlog
patchlog --versionThe Scoop bucket follows the same manifest-only model. Artifact construction remains delegated to the release workflow; package repositories distribute checksum-pinned manifests.
Archives and SHA256SUMS are published on the
GitHub releases page. Verify
provenance with:
gh attestation verify <archive-or-receipt> --repo fxdv/patchlogThe reporting path remains independent of release coordination:
patchlog
patchlog --from v0.1.0 --to HEAD
patchlog --from v0.1.0 --format json --out release.jsonPlain patchlog is read-only. It can classify conventional commits, render
Markdown or JSON, organize changes, and generate release notes without
performing release mutations.
The protected core does not call AI, publish to Confluence, collect product telemetry, or enable experimental people metrics. Optional capabilities have focused subcommands:
patchlog ai
patchlog confluence
patchlog metrics
patchlog labs --gamify- AI is opt-in and discloses when repository-derived content leaves the machine. Inputs are secret-redacted, file-excluded, and size-limited.
- Confluence and changelog publishing cannot silently join the protected transaction.
- Repository metrics are diagnostic, not release gates.
- DPI, health, individual grades, percentiles, and gamification live under the
explicitly experimental
labsboundary.
The descriptive proxies are named as proxies:
TOUCHED_TEST_FILE_RATIO, CHANGE_COMPLEXITY_PROXY,
CROSS_CUTTING_CHANGE_RISK, RELEASE_CONTRIBUTION_CONCENTRATION, and
RELEASE_COMMIT_SPAN_HOURS. True coverage belongs to CI coverage artifacts;
true dependency risk needs a language-aware graph; cycle and lead time require
pull-request and deployment timestamps.
Some repositories intentionally allow one process to bump, commit, tag, push, and publish. That workflow remains available, but it must be named:
patchlog release direct --bump auto --tag --push --publish --dry-run
patchlog release direct --bump auto --tag --push --publish \
--approve sha256:<fingerprint>No flag implicitly selects direct mode. For example,
patchlog release --bump patch is rejected; the protected manual override is:
patchlog release prepare --bump patch --dry-runThis distinction prevents a familiar option from silently weakening the repository’s governance model.
- YAML decoding is strict; unknown or misspelled fields fail.
- Credential-bearing endpoints require HTTPS unless an explicit insecure development override is configured.
- Tokens are expanded from environment variables and excluded from plans, fingerprints, receipts, and diagnostics.
- Redirects and URL schemes are restricted for external operations.
- Registry and provider responses have explicit size limits.
- Dry-run disables cache writes.
- Patchlog sends no usage telemetry.
- CI actions are pinned to immutable revisions.
See SECURITY.md for vulnerability reporting and configuration and security for integration-specific controls.
Patchlog is pre-1.0. Version 0.2.0 is defined as a stability milestone for the protected CLI, strict configuration schema, deterministic plan/apply behavior, automated post-release verification, and real-repository evidence—not as a feature-count milestone.
Current published engineering measurements cover two maintainer-controlled hosted workflows:
- median time to first successful plan: 322.5 seconds;
- plan-to-release completion: 2/2;
- releases requiring recovery: 0/2;
- successful releases without manual Git intervention: 1/2;
- classified preflight rejections across five attempts:
version_detection1.
These numbers demonstrate the measurement method, not market adoption. Repositories controlled by Patchlog’s maintainer do not satisfy the independent validation gate. Evidence from three repositories controlled by unrelated maintainers remains required before declaring 0.2.0 stable.
Patchlog does not grade individuals. Coverage percentages, proxy metrics, and experimental labs outputs are not release gates.
See the product measurements, real-repository evidence, and 0.2.0 stability contract.
- Quick start
- Configuration and security
- Advanced workflows
- Complete CLI and feature reference
- Architecture
- Package-manager publishing
bash scripts/gate.shThe quality gate runs formatting, vet, builds, race-enabled package tests,
integration tests, orchestration E2E tests, and release-tool verification. CI
also tests the supported Go 1.22 minimum and uploads coverage artifacts for
cmd/patchlog, internal, and pkg; coverage remains diagnostic.
MIT — see LICENSE.