Skip to content

0.7.1i: carry an already-certified evidence population across the promotion boundary - #483

Merged
GionaGranchelli merged 1 commit into
epic/0.7.1-control-plane-authorityfrom
task/0.7.1i-population-promotion
Oct 3, 2026
Merged

GionaGranchelli merged 1 commit into
epic/0.7.1-control-plane-authorityfrom
task/0.7.1i-population-promotion

Conversation

@GionaGranchelli

Copy link
Copy Markdown
Owner

Problem

The 0.7.1 → release/0.7.0 promotion (#440) fails on exactly one check, tests, in the step "Verify mutation ratchet against PR base":

M35 × 134 lines  =  67 admissions × 2
M45 ×   2 lines  =   1 certificate  × 2
authority base   =  9ccbd0d3c6d2b0c0f30277724ef9a33ef240a22a

M35 and M45 bind a newly introduced admission / certificate to the authority base it was minted against. A promotion has no legitimate way to satisfy that: re-minting admissions, rewriting fromBaseSha, manufacturing replacement certificates and a per-entry migration chain are all forbidden. Every row the promoted line added looks newly introduced relative to release/0.7.0, so the whole population is refused.

Minting and promotion are different operations. This adds the one concept that distinguishes them.

Change: MutationPopulationPromotion

It grants no authority. It declares that an already-certified evidence population is carried across a promotion boundary, and therefore was not minted by the transition that carries it.

Four fields, all enforced against the ledgers themselves:

promotionBase: "9ccbd0d3c6d2b0c0f30277724ef9a33ef240a22a"
populationDigest: "9aebd3202288c82ff006f2db33c95cac0772746fa3c3061569167cd3f45df9b0"
admissions: 67
certificates: 1
  • New MutationPopulationPromotion.kt — the declaration, its fail-closed loader, and one carried(...) rule.
  • New config/quality/mutation-population-promotions.yml — the declaration above. promotionBase was verified equal to the live release/0.7.0 tip.
  • MutationRatchetCandidate gains the declaration (absent by default = fail-closed).
  • MutationRatchetVerifier computes it once from the same ledgers both ceremonies judge, so the admission and certificate rules cannot drift apart.
  • M35 and M45 each get one guard: if (fromBaseSha != baseSha && !carriedForward). Everything else fails exactly as before.

Why candidate-side

The declaration must be SHA-exact ("this promotion, against this base"), and the base branch cannot name the commit that will contain the declaration — a file cannot contain its own commit SHA. So it rides in the promotion's own candidate. This is a deliberate, bounded departure from "authority must pre-exist in the base": the declaration admits nothing, it only certifies an unchanged, digest-bound set, and a genuinely newly minted row carries a different digest and is still refused by M35/M45.

Negative proofs (12 tests, all green)

Each changes exactly one field of an otherwise exact declaration, or removes it:

  • declaration absent → M35 fires (normal non-promotion minting still rejected)
  • wrong promotionBase → nothing carried, M35 fires
  • wrong populationDigest → nothing carried, M35 fires
  • 68 admissions instead of 67 (and 0) → nothing carried
  • 0 or 2 certificates instead of 1 → nothing carried
  • a newly minted admission with another digest → M35 fires
  • a newly introduced certificate with another digest → M45 fires
  • malformed ledger → hard failure (never a silent "no promotion"); absent file → no promotion
  • exact declaration → M35 and M45 both cleared

Verification

  • ./gradlew verifyMutationRatchet -PtramaiMutationBaseSha=origin/release/0.7.0 → Mutation ratchet (base 9ccbd0d3…) baseline verification PASSED. Task executed, not UP-TO-DATE. This is the exact check Epic/0.7.1 control plane authority #440 fails.
  • :build-logic:test --tests MutationPopulationPromotionTest → 12 tests, 0 failures.
  • spotlessApply/spotlessCheck -PtramaiFormattingBaseRef=80cecf9f… → clean; the formatter touched only this slice's files.
  • verifyStaticAnalysis -PtramaiStaticAnalysisBaseRef=origin/master → clean. The two findings raised by this code (ReturnCount on the first form of carried, ThrowsCount in parseDeclarations) were fixed in source — no @Suppress, no baseline entry.
  • verifyChangePolicy -PchangePolicyBase=80cecf9f… → PASSED, 8 files, no violations.

Not touched

No fromBaseSha rewritten, no admission re-minted, no replacement certificate, no per-entry migration chain, no baseline or deviation change, no gate weakened. M35/M45 continue to bind everything that was actually minted by the transition.

Disclosed

The policy-maintainability lane pins expected: 364 and its filter set includes --tests 'dev.tramai.build.quality.Mutation*Test', which now matches this PR's new test class. The lane measures 448 on this tree (0 failures; the epic's own drift is +72, this class is +12). That pin lives in .github/workflows/maintainability-baseline.yml, which does not run for epic-targeted PRs, so it is not changed here — workflow changes ship alone. It is queued as its own workflow-only change carrying the measured 448.

…motion boundary

The 0.7.1 -> release/0.7.0 promotion (#440) fails the mutation ratchet on
M35 x 67 admissions and M45 x 1 certificate: both rules bind a NEWLY
INTRODUCED row to the authority base it was minted against, and a promotion
cannot satisfy that by any legitimate means - re-minting admissions,
rewriting fromBaseSha, manufacturing replacement certificates and a
per-entry migration chain are all forbidden.

Minting and promotion are different operations. This adds the one concept
that distinguishes them: MutationPopulationPromotion, a declaration that an
already-certified evidence population is carried across a promotion boundary
and therefore was not minted by the transition that carries it.

It grants no authority. Four fields - promotionBase, populationDigest,
admissions, certificates - are each enforced against the ledgers themselves,
so a declaration cannot describe a population other than the one it names.
SHA-exact and candidate-side, because the base branch cannot name the commit
that will contain the declaration.

M35 and M45 each gain one narrow guard: a row is not bound to this base when
the exact declaration carried it. Everything else fails exactly as before -
a newly minted row carries a different digest and is still refused. The
declaration is computed once in MutationRatchetVerifier, so the admission and
certificate rules cannot drift apart.

Verified: the real verifyMutationRatchet against release/0.7.0 PASSES (the
exact check #440 fails); 12 discriminators green, covering every field of the
declaration plus its absence; spotlessCheck and verifyStaticAnalysis clean,
with the two findings this code raised (ReturnCount, ThrowsCount) fixed in
source - no suppression, no baseline entry. No fromBaseSha rewritten, no
admission re-minted, no gate weakened.
Copilot AI balanced review requested due to automatic review settings October 3, 2026 11:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

It deliberately relaxes an authority/mint-binding invariant (M35/M45) via a self-asserted candidate-side declaration, which is exactly the kind of governance-boundary change the repository guidance reserves for human review.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
What changed in this PR

This PR adds a new concept — MutationPopulationPromotion — to the mutation-ratchet governance machinery in build-logic. The mutation ratchet's mint-binding rules (M35 for admissions, M45 for digest-migration certificates) require every newly introduced admission/certificate to record a fromBaseSha equal to the transition's authority base. During a release promotion (0.7.1 → release/0.7.0), the entire promoted evidence line looks newly introduced relative to the new base, so M35/M45 reject the whole population. This change introduces a narrow, SHA-exact, candidate-side declaration that states an already-certified population is carried across the promotion boundary (not minted there), relaxing M35/M45 only for the exact declared digest and counts while leaving every other protection in force.

Changes:

  • New MutationPopulationPromotion data class + fail-closed YAML loader + a single carried(...) rule that validates the declaration against the actual ledgers (base SHA match, exact admission/certificate counts, and every appearing row's digest).
  • Threaded the computed-once promotion through MutationRatchetVerifier into both the admission ceremony (M35) and certificate ceremony (M45), adding one guard each (fromBaseSha != baseSha && !carriedForward).
  • New declaration file and 12 negative-proof tests asserting that any single field deviation (or absence) carries nothing and the original rules fire.
File Description
build-logic/​.../​MutationPopulationPromotion.kt New declaration type, carried(...) rule, and fail-closed loader.
config/​quality/​mutation-population-promotions.yml The concrete 0.7.1 → release/0.7.0 promotion declaration (67 admissions, 1 certificate).
build-logic/​.../​MutationRatchetVerifier.kt Computes the promotion once and passes it to both ceremonies.
build-logic/​.../​MutationRatchetAuthority.kt Adds optional candidate-side promotion field (defaults to absent/fail-closed).
build-logic/​.../​MutationPopulationAdmissionCeremony.kt M35 gains a carriedForward exception bound to the declared digest.
build-logic/​.../​MutationAuthorityDigestCertificateCeremony.kt M45 gains the same carriedForward exception for certificates.
build-logic/​.../​MaintainabilityBaselinePlugin.kt Loads the promotion from the candidate tree and wires it into the candidate.
build-logic/​.../​MutationPopulationPromotionTest.kt 12 negative-proof tests for the carry rule and loader.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +99 to +100
* Notes carry declaration history, so a later promotion appends rather than replaces: an older
* declaration simply never matches the transition being verified, since the base SHA is exact.
@GionaGranchelli
GionaGranchelli merged commit 0f3e9d2 into epic/0.7.1-control-plane-authority Oct 3, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants