A patch is the unit of change.

SourbyCraft is developed against a pinned upstream and released through explicit gates. The workflow is slower than “build and hope” on purpose: the failure modes live in the seams between ownership domains.

ToolchainGradle · Java 25
TreePaper · patch-first
ReleaseSigned · reproducible
Current gateBuild 49 · open

Change, prove, gate, label.

Every step produces an artifact or a check. There is no “trust me” stage between a developer’s laptop and a published release.

01 / Change

Start at the upstream seam.

A change begins as a patch against the pinned Paper tree. We prefer the smallest diff at the boundary that owns the behavior. Generated sources are not hand-edited.

patches/upstream pinminimal seam
02 / Prove

Test the contract, not the happy path.

Unit and integration coverage follows the behavior: ownership, shutdown, entity and chunk lifecycle, cross-region paths. A compile is not evidence that a region can be safely ticked.

unitintegrationboot + shutdown
03 / Gate

Run the release gates in order.

A release candidate must build cleanly, boot, shut down, pass the reproducibility job, and produce a signature, manifest, and SBOM. A failed gate stops publication.

reproduciblesignedSBOM
04 / Label

Say what the release is.

REL means the jar is published. STABLE is a separate claim reserved for a later gate. Hotfixes are backports, not a new stability label.

RELnot STABLEhotfix

Reproducible from a clean checkout.

The release job does not reuse a developer’s working directory. It applies the patches to a fresh checkout, builds, boots, and compares the hash. The artifact is signed only after that check passes.

release check · reference
# Work from a clean tree with the pinned upstream.
./gradlew applyPatches
# Change the smallest seam that owns the behavior.
./gradlew rebuildPatches
./gradlew build

# The release job reruns this from a fresh checkout and refuses
# to publish if the reproducible hash does not match.

What must be true before the jar moves.

A release candidate is not “good enough” because the developer believes it. Each gate has an observable result, and a failed gate is a stop, not a warning.

CompileClean build from the pinned tree
Unit + integrationBehavior, not only compilation
Boot + shutdownA real server boot and a clean stop
Region ownershipCross-region paths and thread checks
ReproducibleFresh-checkout hash match
Signature + SBOMEd25519 signature, manifest, CycloneDX
DocsInstall, boundary, and release notes
No false claimNo unverified benchmark or STABLE label

Green CI is necessary. It is not sufficient.

A build can pass while a region boundary is wrong. The workflow keeps the distinction between “it compiled” and “the runtime contract holds” visible.