● vrp-ops / the new release system · target state draft · illustrative week · 2026-10-13 → 10-17
staging production hostaway ✕ stopped by a check hover any mark for details
0 · branches, tags, environments, flagsdeploying code and turning on a feature are separate steps
branch: main
The only long-lived branch. Every PR merges here, small and often.
Each commit is built once. Staging runs the newest build. The branches staging, production and hostaway are gone.
tag: r-1015.1
A release: the commit a production rollout was cut from. Never moves.
One manifest per tag in R2 lists its builds. Name: r-<month><day>.<release of the day>.
release branch
Hotfix only, when main is not releasable. Starts at a tag, gets cherry-picks, ends with a new tag (r-1015.1-hf1).
Deleted once the next release includes the fix.
environment
Not a branch. A pointer to the tag it runs: environments/production.json → r-1015.1.
Deploy = move the pointer forward. Rollback = move it back, for API, UI and tasks together.
feature flag
Decides who sees a feature: per environment, per team, or a share of teams. Has an owner and an expiry date.
Turning a feature off is a flag change in seconds, not a rollback.
1 · one week: git on top, environments belowreleases start whenever none is running, builds are verified, and people are around
main → tags
A release tags the newest commit whose end-to-end tests passed. It starts as soon as the previous rollout finished, Mon–Fri 08:00–18:00, not after 13:00 on Fridays. PRs tagged write path wait for approval.
staging
Runs every build of main, 10 min after merges settle. End-to-end tests and manual QA happen here. Time on staging is not a check by itself.
production
New version first at 0 %: synthetic tests hit it through the version override header. Then 10 → 30 → 50 → 100 % with automatic rollback.
hostaway
Moves once a week to a tag ≥ 24 h old on production with no incident since. Changelog to Hostaway's developers and Frank about 2 days ahead.
2 · building a feature newthe code ships dark with every release; the flag decides who sees it
  1. Small PRs to main, flag off. No long-lived feature branches. Each PR is reviewed, tested and released like any other; production runs the code without anyone seeing it. A preview deployment per PR lets people try a change before it merges.
  2. Data changes expand only. New tables and columns ship early; nothing old is dropped while the flag exists. Turning the flag off must leave every row it created valid.
  3. API changes are additive. New fields and endpoints are fine. Replacing a field means: add the new one, deprecate the old one, then grace period.
  4. Turn it on in steps from the console: staging → our own team → pilot customers → 25 % of production teams → all. Errors and accounting checks are compared between teams with the flag on and off.
  5. Hostaway last. Its teams get the flag only after Hostaway's developers had the changelog, and only on a tag Hostaway already runs.
  6. Remove the flag within 30 days of reaching 100 %. An agent opens the PR that deletes the old path; contract migrations follow once no live tag needs the old shape.
3 · the path of one changeeach step has a check; a failed check stops the change there
4 · build once, promote the buildnothing is rebuilt per environment
CI on merge
builds only components whose content changed
api · webhook · tracking
portal routers · vrt-mcp
ecosystem workers · docs
ui app · hostaway embed
No build bakes in its environment. The UI reads its API host at runtime, so one UI build serves every environment.
──▶
R2 · vrp-releases (write once, never overwritten)
builds/<component>/<content-hash>/
bundle · source map · commit · lockfile hash
releases/r-1014.3.json
builds per component · wrangler config per env · openapi.json · migrations (expand / contract) · PRs · risk tags
environments/production.json
live tag · Cloudflare version ID per Worker · UI deployment · rollout step
│ promote = upload the same bundle as a new version, then move traffic ▼
Not portable: Trigger.dev tasks are built per environment from the tag's commit and lockfile, staged, then promoted once the API reaches 100 %. NPM packages are published once per tag. Every Sentry event and metric carries the tag.
5 · one release runsame order for production and hostaway
6 · hotfixmain releasable → express release · otherwise branch from the live tag
  1. Fix H merges to main first, so no later release can lose it.
  2. If main is releasable (everything on it passed its checks and approvals), start an express release from main: same steps, 2 min holds.
  3. If not, as here, where N waits for approval: branch release/r-1015.1 from the live tag, cherry-pick H only.
  4. CI builds the branch; only components H touched get new builds → tag r-1015.1-hf1, same 0 % step and rollout.
  5. The next release from main replaces it; the branch is deleted. Hostaway gets its own hotfix only if affected.
Hotfixes may only add (columns, indexes). No drops, no API schema change.
7 · when an alert fires after a rollout newroll back only what the release caused
8 · shared parts and version skew newshared parts must work with the oldest live tag
Hostaway runs up to a week behind, so the oldest live tag is usually Hostaway's. CI checks each shared part against the manifests of every live tag.
9 · where things runthe devbox works; it is never the only way to stop a bad release
10 · what stops a bad change, and whenlater layers catch what earlier ones miss