Cut releases from a reviewed PR, and make CI run the thing it ships - #64
Merged
Conversation
Releases were cut by hand, and nothing in CI ran the artefact people deploy. A release is now one choice and one review. `Create release PR` bumps the version and promotes `## Unreleased` to a numbered section; merging the pull request it opens is what publishes. Merging builds one image, signs a build provenance attestation for its digest, tags the commit and creates the Release with `container-images.json` so a deployment names a digest rather than a tag somebody could move. The notes are the section a person wrote: nothing is generated from commit subjects, because a commit subject is written for the reader of a diff and these are for somebody deciding whether to upgrade. Publishing checks the pull request rather than the commit message. The head branch must match `release/publish/vX.Y.Z`, be in this repository, and carry the `release` label. A fork can name a branch anything; it cannot add a label. Three checks were added, because the existing ones cannot see the image at all. `migrations` refuses a schema change with no migration and a snapshot that has drifted. `image` builds the container, boots it, and fails if it does not answer or if a supervised service is respawning. `smoke` runs the journey in tests/smoke, which existed and ran nowhere, against a real deployment. A single `verify` check covers every job, so branch protection needs one entry and a new job is covered without anybody updating a list. The smoke journey skips itself with a warning when no licence is configured. A check that fails for a reason nobody can fix is a check people learn to ignore.
davidmckayv
requested review from
MikeRyanDev,
guidovizoso and
tylerslaton
as code owners
August 21, 2026 00:44
…e journey Three corrections to how this was first written, all of them found by running it. The image check under-configured the container and it died at start-up. `loadConfig` refuses to run without Intelligence and a licence, so the boot now supplies both as placeholders: it only checks they are present and well-formed, nothing is contacted at start-up, and /api/capabilities reads config alone. The wait is 150s rather than 60s, which a cold embedded PostgreSQL plus migrations plus Chromium needs. The smoke journey is gone from CI, and will not be coming back. It asserts `licenseStatus` is valid, and a licence is signed for the machine it was issued for, so no hosted runner can satisfy it and no placeholder can fake it. It is now the one step a person takes before merging a release, on a machine that has a licence, and the release PR asks for the result. A check that cannot pass is worse than an absent one. There is no GitHub App for this organisation, so the release PR is opened with the built-in token. That means the PR arrives without its own checks, because a pull request opened by a workflow does not trigger them. Rather than work around that, publishing now runs the whole suite against the release commit before it builds anything. The checks gate the release instead of the proposal for one, which is where they were always more useful. No secrets are needed by any of this. GITHUB_TOKEN is the only one referenced.
davidmckayv
added a commit
that referenced
this pull request
Aug 21, 2026
Audited every markdown file against everything that landed today, including the work that was not mine. `docs/coworkers.md` still told people to point `MANAGED_AGENT_AG_UI_URL` at `4200`. #33 made `agent-langgraph` on `4201` the default precisely because the proof-of-concept hand-writes the protocol and leaves the tool loop to whatever is watching, so following that page produced the shape the change moved away from. Three environment variables the server reads were in `.env.example` and nowhere in the configuration reference: `AGENT_STALL_TIMEOUT_MS` from #19, which is the only thing that notices a Bot's stream going silent; `AGENT_TOOL_TOKEN` from #34, without which no framework Bot may call a granted tool back; and `APP_DIST_DIR`, which the container sets so one process serves both halves. Both documentation indexes had fallen behind their own directory and listed neither `deployment.md` nor `releasing.md`. `docs/development.md` gains the migration workflow the checks in #64 now enforce: never hand-edit a generated migration, write a data step with `--custom`, and what to do when `drizzle-kit migrate` hangs and exits non-zero with nothing printed, which is the journal naming a file a rebase renamed. `drizzle-kit check` calls that state fine, because it compares schemas rather than asking whether the journal and the directory agree. The README keeps its shape: what this is, how to run it, how to deploy it, and where to read the rest.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Releases were cut by hand, and nothing in CI ran the artefact people deploy.
A release is one choice and one review.
Create release PRbumps the version and promotes## Unreleasedto a numbered section. Merging the pull request it opens is what publishes: one imageto
ghcr.io/copilotkit/openbot, a build provenance attestation signed for its digest, the tag, and aRelease carrying
container-images.jsonso a deployment names a digest rather than a tag somebodycould move. Notes are the section a person wrote. Nothing is generated from commit subjects.
Publishing trusts the pull request, not the commit message. The head branch must match
release/publish/vX.Y.Z, be in this repository, and carry thereleaselabel. A fork can name abranch anything; it cannot add a label.
Three new checks, because the existing ones cannot see the image.
migrationsrefuses a schemachange with no migration and a drifted snapshot.
imagebuilds the container, boots it, and fails ifit does not answer or if a supervised service is respawning.
smokerunstests/smoke, whichexisted and ran nowhere, against a real deployment. One
verifycheck covers every job.Modelled on OpenTag's release plumbing, with two deliberate departures: its changelog is generated
from
git log, which would bulldoze the hand-written one here; and its own e2e harness is wired intonothing, which is the gap this closes rather than copies.
Where it runs
a version computed before the other existed.
Boundary and audit
the API, and the tag is created through the API so no git credential is persisted in a runner.
Changelog
Unreleased.Proof
Checked against current guidance rather than copied: every action is at its latest release and
SHA-pinned, verified against the GitHub API.
actions/attest-build-provenanceis v4.2.2, not the v3that search results suggest;
docker/setup-buildx-actionwas bumped to v4.3.0. Attestation binds thedigest, never a tag.
Run locally:
zizmor --min-severity=lowon all four workflows: no findings. It caught two of mine first, acache-poisoning vector and a persisted git credential. Both are fixed rather than suppressed: the
smoke job no longer caches the bun binary, and the tag is created through the API.
bun -epasses argv as[bun, ...args], soreading it positionally from the front yielded
undefinedand every bump silently became a patch.Verified:
0.0.1-> patch0.0.2, minor0.1.0, major1.0.0.## Unreleasedabove## 0.0.2, and the notes extractor pulls 40non-blank lines back out for the release body.
drizzle-kit checkclean andgeneratereports no unwritten migration, so both new gates pass onthis tree rather than failing on arrival.
bun run typecheckexit 0,biome lintandbiome formatclean,bun test745 pass 0 fail,bun run test:cimeets its floor.Not verified: I cannot execute GitHub Actions locally, so the workflows are validated by YAML
parse, zizmor, pinned-version checks and by running their underlying commands. The first
Create release PRshould be run withdry_runfirst.Two things are needed before the smoke job can pass:
COPILOTKIT_LICENSE_TOKENandINTELLIGENCE_API_KEYas repository secrets, andDEVOPS_BOT_CLIENT_ID/DEVOPS_BOT_PRIVATE_KEYfor the release PR. Without the licence, smoke skips with a warning rather than failing.