Release process¶
Versioning¶
qkt uses semantic versions while pre-1.0:
- minor (
0.X.0): a substantial phase or intentionally breaking public change - patch (
0.X.Y): a compatible feature set, sub-phase, or hotfix
VERSION is the single release version consumed by Gradle, the CLI, generated
scaffolds, distribution names, and container metadata. Change it through a reviewed
PR before creating a tag.
Distribution channels¶
| Channel | Source | Intended use |
|---|---|---|
GitHub release and ghcr.io/elitekaycy/qkt:vX.Y.Z |
immutable vX.Y.Z tag |
canonical stable install |
ghcr.io/elitekaycy/qkt:latest |
main |
current promoted main |
ghcr.io/elitekaycy/qkt:edge |
testing |
integration-tested staging |
ghcr.io/elitekaycy/qkt:dev |
main |
authoring image with editor tools |
Generated deployment projects pin v<VERSION>. Do not publish a qkt build whose tag
differs from VERSION: both release and Docker workflows run
scripts/verify-version-tag.sh, and the essentials workflow tests that gate.
Promotion¶
Changes reach a release through the protected branch flow:
- Merge reviewed feature branches into
dev. - Let the green
check.ymlmerge the exactdevcommit intotestingwhile preserving prior testing promotion history. - Wait for
integration.ymland the:edgeimage on the exacttestingSHA. - Resolve the edge image to its immutable
ghcr.io/.../qkt@sha256:...digest and validate it with one of the two evidence profiles below. A normalpaper-soakprofile runs against the demo MT5 canary for at least 48 continuous hours or five trading days. A finitelive-parityprofile may qualify the promotion without that elapsed-time requirement, but it must exercise real demo strategy runs across indicators, math, DSL, order types, ticks, bars, fills, and Insights, and retain exact replay comparisons with zero unexplained mismatches or outcomes. Both profiles retain periodic/health, finalqkt reconcile --json, andqkt golden captureoutput with zero dropped records. - On the trusted soak host, derive the attestation:
qkt soak report ema-canary \
--testing-sha "$testing_sha" --image "$image_digest" \
--started-at "$started_at" --completed-at "$completed_at" \
--trading-days "$trading_days" --health health.jsonl \
--reconciliation reconciliation.json --golden golden.zip \
--coverage coverage.json --parity parity.json --insights insights.json \
--out /var/lib/qkt/soak/attestation.json
For live-parity, the attestation additionally sets attestationType to
live-parity, includes a parity object with positive counts for
strategiesTested, indicatorsTested, mathScenariosTested,
dslScenariosTested, orderTypesTested, totalTicks, totalBars, fills,
parityComparisons, and insightsEvents, and zero values for
parityMismatches, unexplainedRejections, and unexplainedOrderOutcomes.
Its artifact set is six sibling files: health, journal,
reconciliation, coverage, parity, and insights; every file is hashed
in artifactSha256. The verifier requires at least ten minutes of recorded
run time and verifies every hash before promotion.
- Dispatch
paper-soak.ymlon thetestingref. It runs on the trustedqkt-paper-soakself-hosted runner, validates hashes and image revision, and publishes immutable promotion evidence:
gh workflow run paper-soak.yml --ref testing \
-f attestation_path=/var/lib/qkt/soak/attestation.json
- Run
promote-to-main.yml. It requires successful integration and paper-soak runs for the exact currenttestingSHA, revalidates the downloaded evidence, and creates or reuses atesting -> mainPR naming the soaked digest. - Merge the promotion PR after its required checks.
- Wait for
integration.ymlanddocker.ymlon the promotedmainSHA.
Do not push directly to dev, testing, or main.
Research and deployment consumers must pin the digest recorded in the promotion
PR. The moving :edge tag is a discovery channel, not promotion evidence.
Tag And Publish¶
Tag the exact promoted main commit only after the main checks pass:
git fetch origin
release_sha="$(git rev-parse origin/main)"
version="$(tr -d '\r\n' < VERSION)"
scripts/verify-version-tag.sh "v$version"
git tag -a "v$version" "$release_sha" -m "release: v$version"
git push origin "v$version"
The tag push starts two pipelines:
release.ymlbuilds and verifies the ordinary tarball, self-contained Linux bundle, self-contained Windows zip, and VS Code extension, then creates the GitHub release with generated notes.docker.ymlsmoke-tests the runtime image and publishes the versioned GHCR image with provenance and an SBOM.
The GitHub release event then starts the Winget submission workflow. Do not run
gh release create or upload Gradle artifacts manually during the normal path; that
duplicates and can race the automated publisher.
Verification¶
Before announcing the release, verify all of the following:
gh release view "v$version"
gh run list --workflow release.yml --limit 1
gh run list --workflow docker.yml --limit 1
docker pull "ghcr.io/elitekaycy/qkt:v$version"
docker run --rm --entrypoint qkt "ghcr.io/elitekaycy/qkt:v$version" --version
The release must contain the Linux tarballs and Windows zip, and qkt --version from
the image must report the tagged version and release SHA.
Failure And Hotfix Policy¶
Tags are annotated and immutable. Never force-update or delete a published tag. If the tag is wrong, fix forward with the next patch version and explain the superseded release in its notes.
For a hotfix, branch from dev, merge through dev -> testing -> main, increment
VERSION, and repeat the same tag workflow. There are no backport branches before
1.0; users update to the latest patch.