Skip to main content

Release candidate qualification

This record is the contract for the next promoted release candidate. It gives the procedure that freezes a candidate, the gates that must pass on that exact candidate, and the proposed list of release blockers. Issue #1401 owns it.

This record does not authorize a release. It does not tag, sign, publish or promote anything. Releasing is the release procedure, and it stays unchanged: a candidate that passes every gate here is released by that procedure, from a vX.Y.Z tag on main.

Status, 2026-10-25. v1.0.6, v1.0.7 and v1.0.8 are released, and all three contain #1540. All three pass aspect verify-release with no problems, so the condition of blocker B1 is met. The proposed next ecosystem candidate is v1.0.8 at 3831a25d, tag object b4f543b4, with the amd64 delivery image sha256:960d3ad1…. Its protocol dependency is 0.7.0 at 1b282837, its Rust client 0.7.1 at ba3443d0, and its schema registry 0.4.3 at ac4fe56e. The draft manifest ecosystem-v1.0.8.json and the proposed freeze record candidate-freeze-3831a25d-proposed.json wait for maintainer review. Freezing is the maintainer’s decision. The CLI, operator and website components of the draft still carry their baseline values, and the review must set them. Until a candidate is frozen, no run here is candidate evidence. The remaining qualification runs are scheduled with the 1.1 release qualification.

The eight-gate preflight exposed failures in single-node transaction configuration, CLI admin response handling and topic configuration, audit placement during operator evacuation, and the schema adapter’s test ownership. Those fixes are merged in the website, CLI and broker. Broker #1573 also moves feature validation before recovery writes, so the loaded lifecycle uses the startup-safe v1.0.6 baseline. Every gate must run again on the final candidate; preflight passes do not qualify v1.0.8.

The cloud qualification workflow provides the performance and 72-hour soak workers. Independent pilot acceptance still requires the named human operators and results in the pilot plans.

Release and ecosystem claims

Krabka makes two separate claims, as Releasing and Ecosystem qualification say:

  • A broker release is a signed image and a GitHub release for a commit whose main CI run passed. This claim alone does not qualify an ecosystem.
  • An ecosystem-qualified stack is a broker release whose frozen candidate passed every gate in Candidate gates, with the sibling set that the candidate manifest names.

Describe a stack as ecosystem-qualified only when every gate below passed on the frozen candidate and every agreed release blocker is closed. A partial pass, an older run, or a run on another revision is not enough. On 2026-10-25 no 1.x release is ecosystem-qualified. The last qualified stack is krabka-broker v0.6.1 with the sibling set in ecosystem-eight-gate-baseline.json.

Freeze a candidate

A candidate is three things that must not move while it is qualified:

  1. The krabka-broker commit. It is on main, and its ci.yml push run passed gate.
  2. Every sibling revision that the commit’s [patch.crates-io] block pins. These revisions are compiled into the image.
  3. The amd64 image digest that delivery recorded for the commit, in the delivery-image artifact of that run. The artifact expires after 90 days.

Do these steps in sequence:

  1. Pick the commit. Its ci.yml push run must be success. Get that run’s delivery-image artifact:

    run="$(gh api "repos/krabka-io/krabka-broker/actions/workflows/ci.yml/runs?head_sha=${COMMIT}&event=push" \
      --jq '[.workflow_runs[] | select(.conclusion == "success")][0].id')"
    artifact="$(gh api "repos/krabka-io/krabka-broker/actions/runs/${run}/artifacts" \
      --jq '.artifacts[] | select(.name == "delivery-image") | .id')"
    gh api "repos/krabka-io/krabka-broker/actions/artifacts/${artifact}/zip" >delivery-image.zip
    unzip -o delivery-image.zip
  2. Freeze the commit, the pins and the digest. The task checks the digest against the registry, and lists each component of the manifest whose revision is not the pin that the image compiles in:

    aspect freeze-candidate --commit "${COMMIT}" \
      --delivery-digest delivery-image.digest \
      --manifest qualification/ecosystem-eight-gate-baseline.json \
      --output candidate-freeze.json
  3. When the task prints a DRIFT line, write a reviewed candidate manifest. Start it with aspect check-qualification --new-candidate, and set every component to its pinned revision and to the artifacts built from that revision. The workflow’s broker_revision and broker_digest overrides replace the broker component only. With drift, they describe siblings that the image was not built from.

  4. Commit the candidate manifest and the freeze record under qualification/ in one pull request. From that merge on, every gate below runs against these values and no others.

  5. Rebuild nothing. A fix to a blocker makes a new commit, and a new commit is a new candidate. Start again at step 1, and run every gate again.

The dry run of these steps on 948e3cc8 is in candidate-freeze-948e3cc8-dry-run.json. It is not a candidate. It was written by the schema 1 task, which compared every [patch.crates-io] pin with the manifest, so it lists all ten pinned siblings as drift. Seven of them, krabka-gateway among them, are patches that nothing in the dependency graph uses: Cargo.lock keeps them under [[patch.unused]], and the image does not contain them. The task now records them as unused_patches and compares only the siblings that the image compiles in. Those are krabka-protocol, krabka-client-rs and krabka-schema-registry, and all three still differ from the qualified baseline manifest. So the next qualification needs a reviewed manifest that names those three revisions; the broker overrides alone are not correct.

Candidate gates

Each gate must pass on the frozen candidate. “Needs” names what the run needs that a pull-request runner does not have. The status column is the state on main on 2026-10-18, and it is not candidate evidence.

Gate Workflow and job, or command Evidence Status on main, 2026-10-18 Needs
Required CI ci.yml job gate, on the candidate’s push run. It needs cpd, ci, coverage, supply-chain, cargo, wasm, checks, proofs, select, docker and kubernetes. The run, and its delivery-image artifact Green on 948e3cc8, run 39058863421. Red on f2305998, run 39053924323: a nextest timeout. 045b10d4 is in progress, and two container groups failed to pull images on the remote executor. Nothing extra
Creusot proofs ci.yml job proofs The job log Green on 045b10d4, run 39165644154 Docker on the runner
Stateright models ci.yml job ci, the model targets of bazel test //..., and kraft_model_test and bucket_model_test The job log Green on 948e3cc8, run 39058863421 Nothing extra
Full container matrix, with the Kafka 4.3.1 JVM acceptance suites ci.yml job docker on a workflow_dispatch of the candidate commit. A dispatch runs every suite (aspect container-suites with no --base). The apache_kafka_4_3_1 suites include jvm_acceptance_cli, jvm_acceptance_durability, jvm_acceptance_reassign, jvm_acceptance_sasl and jvm_bootstrap_controller. The run Red. Nightly run 39114726914 on 948e3cc8: jvm_acceptance_legacy+6, librdkafka_conformance+6 and minio_roundtrip failed to build, because image pulls on the remote executor were cancelled. The other groups passed. Issue #1493. Docker, remote executor
GSSAPI ci.yml job gssapi, on the same dispatch The run Green in run 39114726914 Docker Compose
Upgrade from the previous release ci.yml job upgrade, on the same dispatch The run Red. Cancelled at its 90-minute limit on 2026-10-16, 10-17 and 10-18. It stops after Records delete operation completed. Before that it failed on 2026-10-13 and 10-14. Docker, GHCR read
Four-hour soak ci.yml job soak, on the same dispatch The job log Red. a_three_broker_cluster_holds_its_resources_over_a_soak failed on 2026-10-16, 10-17 and 10-18. On all three brokers the log directory rose by about 4.89 MB over the second half, against an allowance of 4,194,304 bytes. Docker
Criterion regression lane ci.yml job bench, on the same dispatch The benchmark-regression-evidence artifact Green in run 39114726914 Nothing extra
Regression budgets (#1435) packaging/performance/qualify.sh budget before_after <reference> and qualify.sh budget pinned_kafka, on the benchmark host. benchmarks.yml job performance-budget-contract validates the contract only. The results record that Performance regression budgets defines Not run on a candidate. Two calibration runs of rf3-regression-v1 on main measured the spreads, and the first exceeded two variance budgets. In run 39865512647 at fd007630, rf3-regression-v2 pinned_kafka passed and before_after exceeded the CPU variance budget. The contract is now rf3-regression-v3, which has no run yet. See Performance budget results and #1395. benchmarks.yml dispatch with budget, on a 32-core cloud worker
72-hour soak (#1393) The soak_contract suite with KRABKA_SOAK_CONTRACT=bench/soak/rf3-72h.yaml, then krabka-soak-check judge. Performance qualification gives the commands. evidence.json and verdict.json Not run. The five-minute self-check is green on 948e3cc8. The 72-hour run has never run (#1393). A 16 CPU, 64 GiB, 500 GiB host for three days
Ecosystem qualification, eight gates qualification.yml with execute=true and the reviewed candidate manifest. The gates are installation, operator-lifecycle, cli-admin, observability-recovery, disaster-recovery, schema-evolution, registry-migration and snapshot-retention. A qualification-<run> prerelease, its manifest.json, report.md and SHA256SUMS Not run on any 1.x commit. The last run of all eight gates was run 36293960620 on 2026-09-27, for the v0.6.1 baseline. Docker, Kind, Helm
Recovery (#1397) The disaster-recovery gate above, plus the declared RPO and RTO, classic and diskless, TLS, SASL and ACL cases that #1397 plans The gate bundle, and the #1397 record Planned. #1397 has no evidence yet. Docker, MinIO
Lifecycle (#1396) The operator-lifecycle gate and the upgrade job above, plus the loaded rolling upgrade, certificate rotation and reassignment on published digests that #1396 plans The gate bundle, and the #1396 record Planned. #1396 has no evidence yet. Kind, published images
Operator acceptance (#1399) Independent operators run the migration and recovery plans of #1398 without maintainer help The operator reports that #1399 names Planned. No operator is named and no evidence exists. Pilot access, real hosts
Support documents (#1400) The support matrix, measured envelopes and known limitations that #1400 plans, at the candidate commit The documents at the tagged commit, pinned by aspect verify-release Drafted in the support matrix and the measured envelope. No row is candidate yet, because no candidate is frozen. Nothing extra
Provenance aspect verify-release --tag <tag> --delivery-digest delivery-image.digest, after the release release-verification.json Red for v1.0.0 to v1.0.5. Green for v1.0.6, v1.0.7 and v1.0.8, which carry the #1509 fix. See Provenance verification. cosign 3, Docker CLI, gh

Since 2026-10-18. The status column above is not current. These runs changed it, and none is candidate evidence:

  • Required CI passed gate at v1.0.8 in push CI 39518736364. On main at 315c2985, push CI 40160834355 failed gate: coverage failed feature_finalization_test::validate_only_does_not_persist and dynamic_voters_test::remove_voter_shrinks_quorum.
  • The full container matrix, GSSAPI, the upgrade lane and the four-hour soak passed at v1.0.8 in dispatch 39519220181, and at v1.0.7 in dispatch 39493788635.
  • The criterion regression lane failed in both dispatches on a measured verdict: at v1.0.8, broker/produce_acks/32part/acks_all at 1.16 times the v1.0.5 reference, against a 0.113 tolerance; at v1.0.7, log/read/past_end_returns_empty at 1.79 times. In the nightlies of 10-22 to 10-24 it failed in its harness, because the cargo log named no bench executable, so no comparison ran.
  • The regression budgets, the eight gates, recovery and lifecycle are in B8, B5 and B10. Provenance is green for v1.0.6 to v1.0.8.

Rules for the gates:

  • Run every gate on the frozen commit and image digest. A gate that ran on another commit gives no evidence for the candidate.
  • Keep every failed run. A failure is a blocker until a later run on a new candidate passes, or the maintainer agrees that the item is a known limitation.
  • Re-run a failed job once only to show an infrastructure failure, such as the image pull failures of run 39114726914. A second failure is a real failure.
  • Do not skip, disable or ignore a test to make a gate pass.

Provenance verification

release.yml does these steps for a vX.Y.Z tag:

  1. It gets the amd64 digest from the delivery-image artifact of the successful main push run, and checks it against the registry with aspect check-delivery-image.
  2. It builds and pushes the arm64 manifest, and joins both children by digest into the vX.Y.Z index.
  3. It signs the index with cosign sign, and attests the CycloneDX SBOM with cosign attest --type cyclonedx and the provenance with cosign attest --type slsaprovenance. The identity is the keyless GitHub OIDC identity of release.yml at the ref that ran it.
  4. It runs cosign verify and cosign verify-attestation against the index, and uploads sbom.cdx.json to the GitHub release.

aspect verify-release repeats these checks from outside, against a release that exists. It also checks the content of the attestations, and pins the support documents at the tagged commit:

git fetch origin tag v1.0.5
aspect verify-release --tag v1.0.5 \
  --delivery-digest delivery-image.digest \
  --output release-verification
# A release that a workflow_dispatch from main re-ran:
aspect verify-release --tag v1.0.0 --identity-ref refs/heads/main
# The support documents must be the bytes an earlier record pinned:
aspect verify-release --tag vX.Y.Z --expected-support candidate/release-verification.json

The task fails when any of these is false:

  • The index is signed by release.yml under the identity of the ref that ran it.
  • The index has one amd64 child and one arm64 child.
  • The provenance statement is SLSA v1, its subject is the index, and its predicate names the tagged commit, the tag ref, the arm64 child, and the amd64 child as the tested delivery image.
  • The SBOM attestation’s subject is the index, and its predicate is the release’s sbom.cdx.json asset, compared as JSON.
  • The amd64 child is the digest of the delivery-image artifact, when --delivery-digest is given.
  • Each support document exists at the tagged commit, and matches the record that --expected-support names, when one is given.

The support documents are README.md, CHANGELOG.md, SECURITY.md, docs/persisted_formats.md, docs/format-divergences.md, docs/KIP_MATRIX.md, docs/config-reference.md, docs/releasing.md, docs/qualification.md, and every file under docs/operations/. The record gives the SHA-256 of each one. A page added under docs/operations/ later, such as the #1400 support matrix, is pinned with no change to the task. To bind the documents to the delivered bytes, keep the candidate’s record, and check the release against it with --expected-support. Because the tag names the commit and the provenance names the same commit and the delivered digests, a pass ties the documents to the image bytes.

Use cosign 3. cosign 2 finds no signature on these releases, because sigstore/cosign-installer v4 signs with cosign 3, which stores the signature as an OCI referrer.

Result for v1.0.5

The task ran on 2026-10-18 against v1.0.5 and v1.0.0. The record for v1.0.5 is release-verification-v1.0.5.json.

Check v1.0.5
Index sha256:2a81d1ba38de4e8ffb032fedf67952845185158a7607ac76ba38fa162dc37e50 at commit ac9deff3
Children amd64 sha256:8c515d33…, arm64 sha256:303ced00…
Signature, release.yml@refs/tags/v1.0.5 Pass
Signature under another identity (refs/heads/main) Fails, as it must
amd64 child against the delivery-image artifact of run 38516831673 Pass
SBOM attestation against sbom.cdx.json (sha256:c123ba49…) Pass
Provenance Fail. Attested as https://slsa.dev/provenance/v0.2 with an empty predicate ({})
Support documents 58 documents pinned

v1.0.0, which a dispatch from main released, gives the same result under --identity-ref refs/heads/main.

The provenance defect is in release.yml. It writes a SLSA v1 predicate (buildDefinition and runDetails) but attests it with --type slsaprovenance. cosign parses that type into the SLSA v0.2 schema, so every field is dropped, and the statement carries no commit, no ref and no image source. cosign verify-attestation --type slsaprovenance still passes, because it checks the signature and the type, not the content. A local cosign attest-blob test with cosign 3.0.2 shows that the type https://slsa.dev/provenance/v1 keeps the predicate as written. The fix is blocker B1 below, in #1509. It does not change the attestations of v1.0.0 to v1.0.5, so those releases keep an empty provenance predicate. For them, the release run log and the delivery-image artifact are the provenance evidence.

Result for v1.0.6, v1.0.7 and v1.0.8

The three releases after #1509 pass. The task wrote release-verification-v1.0.6.json on 2026-10-20, and release-verification-v1.0.7.json and release-verification-v1.0.8.json on 2026-10-25 with cosign 3.1.4. Each record has "verified": true and an empty problems list. The v1.0.7 and v1.0.8 records were run with --delivery-digest.

Check v1.0.6 v1.0.7 v1.0.8
Commit 402edff3 eb7e0c6a 3831a25d, tag object b4f543b4
Index sha256:ad519537… sha256:9d35b99f… sha256:1d0dd0d4…
Children amd64 sha256:26603481…, arm64 sha256:37bcdd52… amd64 sha256:7a6075d6…, arm64 sha256:31eb3d9e… amd64 sha256:960d3ad1…, arm64 sha256:1b91e636…
Release run 39474117467 39501119789 39523147617
Signature, release.yml at the tag ref Pass Pass Pass
Signature under refs/heads/main Not recorded Fails, as it must Fails, as it must
amd64 child against the delivery-image artifact Not recorded Pass, push CI 39493754304 Pass, push CI 39518736364
SBOM attestation against sbom.cdx.json Pass, sha256:6b139a85… Pass, sha256:5f935388… Pass, sha256:adaaa41d…
Provenance, SLSA v1, naming the commit, the tag ref and both children Pass Pass Pass
Support documents 62 pinned 62 pinned 62 pinned

All three tags are on main, and all three contain #1540, the diskless restore fix.

Blocker list (proposal)

The maintainer agrees this list on #1401. Each item has one class:

  • Release blocker: the candidate cannot be promoted, or described as ecosystem-qualified, until the item is fixed and the fix is verified on a new candidate.
  • Known limitation: the release can ship with it, and the support documents (#1400) must state it.
  • Not relevant: the item does not affect the candidate.

Release blockers

ID Item Source Reason
B1 The provenance attestation has an empty predicate. This record; release.yml The provenance is the link between the signed index, the commit and the tested image. Fix in #1509: attest with --type https://slsa.dev/provenance/v1, and fail the release unless the statement read back names the commit, the ref and both children. Verified when the next release passes aspect verify-release. Condition met. v1.0.6, v1.0.7 and v1.0.8 each pass with no problems: v1.0.6, v1.0.7 and v1.0.8, and Result for v1.0.6, v1.0.7 and v1.0.8. The maintainer closes the item on #1401. A frozen candidate is still verified after its release, with --expected-support against the candidate record.
B2 The four-hour soak fails: log-directory growth is over its allowance on all three brokers. ci.yml soak, 2026-10-06 to 10-18; #1507 A resource that rises without bound under steady load is the failure this lane exists to find. The growth was soak-verify, the offsets topic and the metadata log, which Kafka’s defaults do not reclaim within four hours, and the soak now rolls the last two every minute. Rolling them exposed two leaks: compaction left the producer snapshots of the segments it merged, and the metadata log never deleted its retired segments. Fixed in #1537, merged 2026-10-19. The four-hour soak passed on its head 2cf5551f (run 39288583006, 14,412 s). Since then it passed at the v1.0.8 commit 3831a25d (job 118958685613, 14,411 s), at eb7e0c6a, and in every main nightly from 10-21 to 10-24. It closes when the soak passes on the candidate.
B3 The upgrade lane stops at its timeout after DeleteRecords. ci.yml upgrade, 2026-10-16 to 10-18; #1301, #1389, #1508 The upgrade from the previous release is a lifecycle gate, and the #1301 confirmation that #1389 needs had never passed on main. The hang was in packaging/upgrade-smoke.sh, not in the broker. The script read changelog prose about the on-disk format as a declared format break. It then ran HEAD in the foreground on a directory that HEAD opens normally, so HEAD served until the job limit. Fixed in #1527, merged 2026-10-19: only the **Breaking, on-disk format.** marker counts, and every step after the HEAD image build has a time limit. The build itself is bounded only by the job’s 90 minutes. The lane passed on main in the nightly run 39237543947 at e0588325, and in the dispatch run 39288583006. Both upgraded v1.0.5’s volume, read the record back with HEAD and found the raised log start. Since then it passed at v1.0.7 from v1.0.6 (dispatch 39493788635), at v1.0.8 from v1.0.7 (job 118958685699), and in every nightly from 10-21 to 10-24, which upgrade from v1.0.8. #1508 is closed. It closes when the lane passes on the candidate.
B4 The full container matrix was not green on the latest commits. #1493, run 39114726914 Three groups failed on image pulls, so their Kafka 4.3.1 and librdkafka suites have no result. No container test failed in the eleven nightlies from 2026-10-08 to 10-19, and every red group was infrastructure. The remote executor’s Docker Hub token for ubuntu:22.04 timed out on 10-18 and in dispatch 39177946645, which #1530 fixed: the image now comes from mirror.gcr.io, and an action that cannot start remotely runs locally. mirror.gcr.io was missing a blob of apache/kafka:4.3.1 on 10-10, and #1358 added the Docker Hub fallback for it. GitHub answered HTTP 500 for aspect-launcher on 10-12, and BuildBuddy was unavailable for gssapi on 10-19. All 13 groups passed in the nightlies of 10-13, 10-14, 10-16 and 10-17, and in dispatch 39288583006 at 2cf5551f, which has #1530. #1493 is closed. Every container group passed at v1.0.8, in push CI 39518736364 and in dispatch 39519220181, and in the nightlies of 10-19 and 10-21 to 10-24. It closes when one full-matrix run is green on the candidate.
B5 Ecosystem qualification has not passed on a 1.x commit, and the baseline manifest does not match the pinned siblings. Freeze a candidate The stack cannot be ecosystem-qualified without a run of all eight gates on a manifest that names what the image compiles in. The image compiles in three siblings, not ten: the other seven pins are unused patches, and aspect freeze-candidate now leaves them out of the drift. At the v1.0.8 tag commit 3831a25d, aspect freeze-candidate reports the three as krabka-protocol at 1b282837 (0.7.0), krabka-client-rs at ba3443d0 (0.7.1) and krabka-schema-registry at ac4fe56e (0.4.3), against 05e3e32d, c4c113c7 and 904490ad in the baseline. Each of the three is on its repository’s main, so the workflow’s default-branch check accepts it. None of the three ships an artifact, so the reviewed manifest changes three revisions and rebuilds nothing. The draft is ecosystem-v1.0.8.json, with the proposed freeze record candidate-freeze-3831a25d-proposed.json, which reports no drift against it. Both wait for maintainer review; the draft is not a frozen candidate. The eight-gate preflight 39446231280 on f5c683d9, a release-branch commit three before v1.0.6, passed observability-recovery, registry-migration and snapshot-retention. installation failed on COORDINATOR_NOT_AVAILABLE in its Go client check, cli-admin on a SASL connection reset, and operator-lifecycle on a timeout waiting on kafkanodepools/brokers. disaster-recovery and schema-evolution failed in their harness before their checks ran: krabka-cli v0.4.4 refused the krabka.diskless topic config, and the schema check looked for a test name that its suite does not have. A preflight is not candidate evidence. The run on the reviewed manifest is scheduled with the 1.1 release qualification. It closes when qualification.yml passes all eight gates on the frozen candidate with that manifest.
B6 Fetch in the tiered remote band hangs, and the remote log metadata manager stays NotReady. #1448, reproduced on v1.0.5; #1542 A Fetch with no response is a correctness defect, and the 72-hour tiered path needs its fix. The hang is fixed in #1538. The remote read now serves the archived segment’s own bytes, as Kafka’s RemoteLogManager.read does, so a compressed batch is no longer framed by its uncompressed length. A Fetch while the RLMM catches up now answers the retriable REPLICA_NOT_AVAILABLE. The NotReady stall did not reproduce in 11 Docker soak runs with the real follower pause, three of them on the #1448 executor. #1542 is closed, to be reopened if KrabkaRlmmPartitionNotReady fires. #1555, merged 2026-10-20, fixes the one hazard found: a slow metadata publish held the lock the RLMM consumer needs, so it stalled catch-up on every metadata partition. #1555 also exports per-partition RLMM readiness (krabka_broker_tiered_storage_rlmm_partition_{ready,target_offset,applied_offset}) and adds the KrabkaRlmmPartitionNotReady alert, so a recurrence is visible. Verified when the soak’s tiered path, with its follower pause, reads back every record on the candidate and no RLMM stays NotReady.
B7 The 72-hour soak has never run. #1551, #1393, PR #1473 Sustained correctness under faults is a criterion of M26, which the 1.1 release milestone now carries. The harness is merged (#1473, and the broker fixes its trial runs found: #1485, #1487, #1491). The two fixes #1551 waited on are merged: #1553, a restarted controller keeping its leaderships (#1442), and #1555, the RLMM pump stall. A host that meets the contract’s 16 CPU, 64 GiB and 500 GiB is available, and #1551 holds the run plan. No GitHub-hosted runner can take the run: a hosted job stops at six hours. The netem slow-follower fault had never run, because no kernel the repository tested on had sch_netem; this sandbox’s kernel is built without it (CONFIG_NET_SCH_NETEM is not set). The nightly netem job now runs bench/soak/self-check-netem.yaml, the self-check with that fault, on a GitHub-hosted runner after loading the module. Its first run passed in dispatch 39369741872 at 3a4d6483: the runner loaded sch_netem, and the contract passed in about five minutes. The judge fails a fault that was not injected or not cleared, so the qdisc was added in a follower’s namespace and removed again. The run host must also load sch_netem, or the first slow-follower fault, at 65 minutes, fails the run. It closes when bench/soak/rf3-72h.yaml runs for 72 hours on the candidate and krabka-soak-check judge passes its evidence. The run is scheduled with the 1.1 release qualification.
B8 The regression budgets have not run on a candidate, and the current contract, rf3-regression-v3, has no run yet. #1395, PR #1435 #1401 asks to repeat performance qualification on the candidate. benchmarks.yml runs both modes on a gcp-ubuntu-24-04-32core worker when dispatched with budget (#1649), one after the other, so they no longer wait for the soak host. Two before_after runs of rf3-regression-v1 on main calibrated the budgets. The first exceeded the records/s and p99 variance budgets, so, as the calibration rule requires, rf3-regression-v2 raised them before any result was published. The first rf3-regression-v2 dispatch, run 39865512647 on main at fd007630, ran both modes. pinned_kafka passed, with 90 of 90 trials clean, and is published as a rf3-regression-v2 result. before_after against v1.0.5 failed its comparison step on excess variance only: the candidate’s CPU per record in 1k-random-lz4-p12-saturate spread 11.6%, against a 10% budget, with no correctness failure, no missing trial and no material regression. So rf3-regression-v3 raised the CPU variance budget to 15%, by the same rule. Performance budget results records every run and its evidence. Neither mode has run under rf3-regression-v3, and no run used a candidate. It closes when before_after against the previous release and pinned_kafka both pass under the current contract on the candidate, and their records and verdicts are published there. The before_after rerun under rf3-regression-v3 is scheduled with the 1.1 release qualification.
B9 The OMB max-rate RF3 rerun for COORDINATOR_NOT_AVAILABLE has not run. #1352, #1389, #1394 #1389 lists it as a release blocker. #1352 found an OffsetCommit answered COORDINATOR_NOT_AVAILABLE twice in omb-local-2026-10-11 on v1.0.1, where Kafka 4.3.1 had none: the coordinator’s 5 s __consumer_offsets append timed out with one in-sync follower a record short. Two causes found since are fixed on main: a busy partition starved a follower fetch session (#1410), and retired segment files were unlinked under the log lock, stalling appends and follower reads for up to 0.7 s (#1457). The 5 s timeout matches Kafka’s offsets.commit.timeout.ms, and Kafka also answers a timed-out offsets append with COORDINATOR_NOT_AVAILABLE, so the remaining question is whether a follower still stalls that long; ext4 journal stalls on the run host (#1333) cannot be reproduced elsewhere. The rerun needs that host, an AMD EPYC 4344P with three 4 CPU / 10 GiB brokers, and the local Docker OMB setup the run was published from in krabka-io.github.io#109. bench/omb cannot stand in: it runs krabka at RF=1 only. It closes when the two max-rate-1-topic-1-partition RF3 trials run on the candidate with no failed commits, or with each failure explained by the replicator and fetch debug logs and matched by Kafka in the same run. The rerun is scheduled with the 1.1 release qualification.
B10 Recovery, lifecycle and operator acceptance have no evidence on a release image or a candidate. #1397, #1396, #1398, #1399 Each is a candidate gate. None is planning only any more, and none has passed on the candidate. Loaded lifecycle (#1396): the loaded lifecycle job passed in qualification run 39172925991, v1.0.5 to main at 948e3cc8 and back on four nodes under load, with 0 of 141,918 acknowledged sends lost and an unsupported rollback refused without damage. It does not cover network partitions, tiered or diskless paths under the lifecycle, or the operator-driven path. Secured DR (#1397): v1.0.5 does not pass; its restore refused a growing archive (#1520). On main at f7d824ab, drill 39328090996 judged dr-secured: PASS with RPO 20.0 s and RTO 21.0 s against 60 s and 5 min, but its job stayed red because a restored diskless partition refused writes (#1524). #1540 fixed that and #1525, and no drill has run on an image with it: the drill needs a main delivery image, and none has been published since #1540 merged. Pilot plans (#1398): pilot-acceptance-plans.md defines both plans, and every gap issue they filed is closed. What remains are its human actions M1 to M10: naming the operators, pilot access and consent, and accepting the numbers. Independent operators (#1399): an AI agent’s dry run of the runbooks found six defects (#1512, #1514 to #1517, #1529) and several doc gaps, all fixed on main; it is labeled as not an acceptance run. Acceptance needs operators who did not build krabka. It closes when the loaded lifecycle and the DR drill pass on the candidate, and both pilots, or independent acceptance runs labeled as such, are published. Since 2026-10-20: drill 39375286339 judged dr-secured: PASS on the main image of 7301120, which has #1540, with RPO 19,980 ms and RTO 23,839 ms, and all 51 checks passed, the restored diskless partition’s authenticated round trip among them. The loaded lifecycle passed again at f5c683d9, from v1.0.5 and back, with 0 of 141,412 acknowledged sends lost (job 118708771194). Neither is a release image or a candidate. The DR drill and the loaded lifecycle on release images, and the operator pilots, are in the 1.1 release milestone and are scheduled with its qualification.
B11 The support matrix and the known limitations name no candidate. #1400, support matrix #1401 depends on it, and aspect verify-release pins it. The page is published (#1505), and aspect check-support-matrix fails a row without evidence or a revision. On 2026-10-20 it read: the loaded upgrade and rollback rows are historical at 948e3cc8, secured DR is failing at f7d824ab pending a drill with #1540, the 72-hour and diskless-under-load rows point at #1551, and #1442 and #1448 have left the open defects. Every executed row is historical, because no candidate is frozen. It closes when the candidate revision is set on the page and each row is either candidate, with its check passed on that revision, or an accepted gap. On 2026-10-25 the page cites the v1.0.8 dispatch 39519220181 for every row it ran, secured DR is historical at 7301120, the loaded lifecycle rows are historical at f5c683d9, and the main hermetic suites at 315c2985, krabka-operator on Kind and the 1.x eight gates are failing. Every passing row is still historical.
F1 Flake: txn_secured_marker_fanout creates a topic while its two-voter controller quorum has no leader. main 0d435874, job 116614668765 a_remote_coordinator_verifies_a_produce_for_a_broker_with_only_cluster_action asserted that CreateTopics succeeds at once, and got NOT_CONTROLLER (41) right after the controller logged not leader; current leader: None. The metadata log directory … has failed errors come after that panic: unwinding dropped the brokers’ temporary directories while they still ran, so they are teardown noise, not the cause. The test now retries NOT_CONTROLLER for up to 30 s, as Kafka’s admin client does, in both of the file’s CreateTopics calls. Not reproduced locally: 15 loaded runs of the old test passed. Open parity question: the broker answers NOT_CONTROLLER as soon as the controller has no leader, where Kafka’s broker forwards the request and waits for the next controller.
F2 Flake: diskless_e2e copies a voter log file that a compaction removed. PR #1452 comment diskless_acks_all_append_survives_the_acking_broker panics at voter_dir.rs (copy a voter log file: NotFound). The proposed fix is to tolerate NotFound in copy_dir. It is not on main.
F3 Flake: produce_gate in the coverage job. main 4fdf5f44, job 116715925029 a_batch_larger_than_segment_bytes_is_refused_even_when_the_message_cap_allows_it fails its check of the log end offset (produce_gate.rs:34). No issue or fix exists.
F4 Flake: tiered_storage_multi_broker::multi_earliest::a_follower_left_with_a_raised_floor_takes_its_leaders_back times out at 600 s. main f2305998, run 39053924323 It made a main gate fail.
F5 Intermittent: a refused rollback changes bytes. The soak_contract check an_unsupported_rollback_is_refused_before_it_damages_data fails at soak_contract.rs:715. netem at f5c683d9; the first attempt of nightly 39758128742 at eb3e01f9 While it refused a finalized metadata.version 33, the rollback target changed the metadata log, the high watermark, leader-epoch-checkpoint and quorum-state. The target was v1.0.5 at f5c683d9 and v1.0.6 at eb3e01f9. Root cause: startup checked feature levels in the committed image only, which goes up to the high watermark recovered from its cache file. When that file is below the record that finalized the level, or missing, the check passes. The engine then elects itself and writes, and it refuses the level only when it commits it. Forcing the file below that record reproduces this exactly with v1.0.6 under Docker. v1.0.5 also checked only after the engine had started, which #1573 fixed in v1.0.6. Fix: the startup guard now also reads every feature level the log holds above the high watermark, and unsupported_feature_level.rs proves a refusal over a stale or missing high-watermark file changes no byte. The fix protects a rollback to a release that carries it. The soak_contract check rolls back to the bench/soak/lifecycle.yaml source, which is v1.0.6, so it stays intermittent until that source is a release with the fix. The netem job is not in gate.

F1 to F4 are blockers to resolve, not documented flakes, because each one is in a lane that gate requires. F5 is in a lane that gate does not require, but it checks on-disk safety during a rollback. A fix makes the test deterministic. It does not skip the test or add a retry.

Known limitations

ID Item Source Reason
L1 v1.0.0 to v1.0.5 carry an empty provenance predicate. Result for v1.0.5 A signed attestation cannot be changed after release. The signature, the SBOM and the delivery digest still verify.
L2 SASL edge differences from Kafka 4.3.1: idle expiry does not count handler and mute time, PLAIN compares raw bytes, SCRAM c= must equal the GS2 header, a wrong-length proof gets error 58, and an expired delegation token is refused. #1390 comment Disclosed differences in rare error paths. Each one is stricter or more lenient, and none changes a successful authentication.
L3 Java regex edge differences: the compiled-size limit, the message for a repetition with no argument, and surrogate escapes. #1343, #1389 #1389 classifies them as documented edge differences.
L4 An override of offsets.commit.timeout.ms is not read, and OffsetCommit maps an append error where Kafka answers NOT_COORDINATOR. #1352, #1388 Minor and on error paths. The maintainer should decide whether to fix them before the candidate.
L5 A SIGKILL of the active controller can leave a 15-second outage unrecovered in that window (3 of 10 trials). PR #1452; #1442 #1452 shows that the window, not the broker, decides the case. State the recovery time an operator can expect.
L6 A dead broker that is never unregistered leaves an offline __krabka_audit partition. PR #1485 Expected, and visible in the alerts. Document the unregister step.
L7 Evidence gaps: no live harness reads read_committed across local eviction, or fails over an RF=3 consumer group. #1388 proposed ten follow-up issues, and none is filed. #1388 The behavior is compared with Kafka’s source, not run. The maintainer should file the ten issues and decide which, if any, are blockers.
L8 Performance follow-ups: the gap to the page cache (#1370), O_DIRECT segments (#1333), and the indicative numbers of #1394. #1370, #1333, #1394 Optimizations, not defects. The measured envelope in #1400 states the current limits.
L9 The 200 ms and 400 ms reproduction of #621 still fails. #621 A limit of the harness, as #621 records, not a broker defect.

Not relevant

Item Source Reason
#1344 is still open. #1344 Fixed by #1346 and pinned by #1404. #1389 says it is ready to close.
#1442 is still open. #1442 The same defect as #1450, which #1455 fixed. The maintainer should close it as a duplicate. The open part is L5.
Flakes fixed on main: kraft_model_test timeouts (#1460), cli_smoke port race (#1454), the multi_misaligned copy flake (#1479). The pull requests Fixed. They stay fixed only if F1 to F4 runs stay clean.
The stale branch fix/controller-finalized-features-epoch. #1389 No effect on the candidate.
The soak judge abandons a recovery at twice its deadline. PR #1470 Behavior of the test harness, not of the broker.