Developer preview. Pre-admission. Not for sensitive, proprietary, regulated, or production code. Expect breaking changes and data loss. Read the limits.

Promotion contract

This is the contract between main and the product branch of the one repository. main holds the source, tests, and evidence. product receives the projection. A change reaches the product branch through that projection, not a merge. The source tree is main. The target tree is built from a declared profile. The same profile and the same gate run for every promotion, and a refusal stops publication.

This contract governs a developer preview. It does not make the preview production-ready, audited, or safe for sensitive code. The product branch is still pre-admission.

The rule

The product branch is not edited directly and is not merged into. A source commit is checked on the floor, then carried through the promotion door. The rule is recorded as UR-12 in the decision set.

The profile is promotion/main-to-product.json. It declares:

Field Value in the current profile Meaning
source.ref refs/heads/main The commit being carried
target.ref refs/heads/product The branch that receives the projection
remote origin Where both refs are published: this repository
trigger manual No local hook starts it; the trusted publish workflow will promote after each land (pending: the publisher, lane GH-5; until it lands nothing promotes)
boundary.overlay { folder: product-only/, kinds: [*.md] } The product-only files (its README.md): each ships at its path without the prefix, and only the declared kinds may
transforms (makefile) Makefile, targets install typecheck build guardian dev status stop clean The product’s Makefile is derived from main’s: the kept rules verbatim, the others (tests, guards, secret checks) dropped. It has one source
safety.maxDeletedFraction 0.1 The largest share of the target a projection may delete before it refuses

The profile is read from the source commit, not from the target or the working tree. A change to the profile takes effect when it is committed on the source. Every promotion commit names the profile and its exact blob (Promotion-Profile:) and the main commit it was computed from (Projected-From:).

The projection

The target tree is computed from the source. It is not a merge result and it is not a copy of the whole source tree.

The current profile excludes tests/, bench/, docs/, and promotion/, plus internal design and evidence folders. A shipped module that imports an excluded path is refused. The documentation site is built separately; docs/ does not travel through this promotion profile.

The projection refuses a missing entry point, a dangling import, an import into an excluded path, an undeclared package dependency, or a submodule it cannot project. A computed import or a glob that matches nothing is reported as an opaque edge, not guessed.

The deterministic gate

The gate covers these stages. A refusal at any stage stops publication. An up-to-date or unchanged answer is ok, not a failure.

Order Stage What it checks A refusal means
1 Profile Strict bridlewise-promotion-profile/v4 schema; no path both included and excluded; valid spawn bases; declared commands exist The profile is invalid. Fix it on the source.
2 Boundary Reachability from entry points and included modules; exclusions; package declarations; gitlinks The source would ship a broken or undeclared module. Fix the source or the boundary.
3 Derived files Kept package scripts; pruned script segments; lockfiles re-resolved against the projected manifests A manifest or lockfile transform cannot represent the source. Fix the source manifest.
4 Plan safety Target edits since the last promotion; the deletion budget; the never-rule; an up-to-date or unchanged projection The target has edits the source does not reproduce, the projection deletes too much, or the source was merged with the target. Fix the source or use a reasoned override.
5 Setup web-deps, engine-deps, guardian, bundle in the candidate A setup command failed on the projected tree. Read its output and fix the source.
6 Converge harness-sync --with-bundle, repeated until a pass changes nothing A generator does not reach a fixed point, or a command writes an undeclared path. Fix the generator.
7 Verify web-typecheck, engine-typecheck, engine-check --skip-typecheck, web-build A gate failed on the exact tree, or a gate changed the tree it judges. Nothing is committed.
8 Commit The repository’s own hooks certify the exact tree; one commit with the target and source as parents; a receipt names every command The commit door refused what the earlier gates missed. Fix the source and add the missing check.
9 Publish One atomic push of the commit and its receipt; target checkout fast-forward The push or landing refused. The commit may stay pinned for a resume. Never force.

The candidate is kept on a failed gate so the output can be read. An integrity refusal means stop and preserve the evidence. It is not an invitation to retry until the state looks different.

What is automated

A reference-transaction hook watches the source ref. When the profile’s trigger is on-source-publish and the source ref moves, the hook starts an unattended promotion in the background. The hook does not trigger on the promoter’s own fetch or push.

The unattended run performs the projection, the gate, the commit, the atomic push, and the target checkout fast-forward. It takes no overrides. A locked or held state, or a target checkout with overlapping edits, is reported as a not-now answer; the next run resumes from the refs. A published commit is never rebuilt.

Automatic promotion pauses after a rollback. It stays paused until a person releases the hold with a reason.

The source repository’s continuous integration is separate from promotion. It runs the typechecks, the secret scan, the public smoke suite, the cockpit and engine builds, and the guardian build and tests. It does not run the full corpus-backed suite, the live provider suite, or E2E on every push.

The public release gate

The product release workflow calls a reusable floor gate pinned by commit. The gate runs the public floor suite, checks the product CI, CodeQL, docs links, and secret scan at the tag commit, and reads the product code scanning and Dependabot queues. It writes promotion-receipt.json. A missing, pending, failed, or unknown required check blocks the release. The status snapshot on this site is not that gate.

What requires a person

From a test to a release

  1. Reproduce. Make the failure visible on the floor. If the bug is real, the reproduction fails before the fix.
  2. Fix the owner. Change the module that owns the behavior. Do not add a retry, fallback, or special case that hides the failure.
  3. Add prevention. Add a test or guard that protects the class of failure, not only the example. A guard states its class, plant, driven behavior, and liveness check.
  4. Run the focused checks. Run the test file, then the domain suite, then the typechecks and secret scan that cover the change. Run the full suite when its prerequisites exist.
  5. Commit on testing. Push the source commit. The source CI runs; the reference hook may start a promotion.
  6. Project and gate. The promoter reads the profile from the source commit, computes the candidate, and runs setup, converge, and verify. A refusal publishes nothing.
  7. Commit and publish. One promotion commit records the profile blob, the source commit, the receipt, and any accepted target edits. One atomic push publishes the commit and receipt. The target checkout fast-forwards.
  8. Release. A maintainer tags the product repository. The release workflow builds the published assets and checksums. The notes repeat the preview warning.

The test result does not travel with the commit. The promotion gate rechecks the projected tree with its declared build and harness gates. It does not rerun the full test suite. A green source run is not a voucher for a candidate that was never built.

Failure reports

A reusable reporter watches the public workflows in both repositories. A failure opens one issue per workflow and branch or pull request, with the run link and the failed job names. Repeated failures comment on the same open issue. A later success for the same scope closes it. The reporter does not copy logs, secrets, or private paths into the issue.

Refusal classes

Class Meaning What happens
ok Nothing to do, or done A plan, a publication, or an up-to-date answer
input Bad usage, missing profile, or invalid profile Nothing is written; fix the request or profile
precondition A boundary error, target edit, deletion budget, or divergence Nothing is published; fix the source or use a reasoned override
failed A real defect on the exact candidate tree The candidate is kept; fix the source
errored The environment failed, such as a busy or offline machine The change is not blamed; a pinned commit can resume
integrity A state that must not happen Stop and preserve the evidence; do not publish
parked The remote has the commit, but the target checkout has overlapping edits The next run or a fast-forward lands it

The full catalogue and the one remedy for each code live in the promotion source. Read the code and the remedy in the refusal instead of guessing from the exit status.

What this contract does not prove

Use the evidence commands to inspect the refusal and the release. Read the status snapshot for the last generated state, with its fallback states.

The logbook index

Search the record

Type to search the curated record.

↑↓ move Enter open Esc close