Repository navigation
feat(0.7.1g1P2-2): digest-migration certificate as a first-class authority object (M45/M46/M47) - #471
Conversation
…ority object Step 2 of TASK-0.7.1g1P2-AUTHORITY-MODEL-RECONCILIATION.md 6.2: the mechanism only. No certificate-aware consumption, no M40-M44, no P2, and the 67 admissions untouched. The certificate carries a *semantic* upgrade of the whole-population authority digest without rewriting historical authority. The 67 P1 authorizations stay bound to their raw-v1 digests and byte-identical; the certificate stands beside them in its own ledger and states, in base authority, that one exact fromDigest is equivalent to one exact toDigest for one exact bounded set of authorizations. M36's payload comparison never sees it, so M36 and M37 stay literally unchanged - no exception branch, no permitted-field list. Three files, mirroring the admissions trio: - MutationAuthorityDigestCertificate.kt - the type, the ledger, and the bounded admission-set digest. Fields exactly as designed: fromAlgorithm, fromDigest, toAlgorithm, toDigest, admissionSetDigest, fromBaseSha, reason. enforcedPayload() is that field set with authorizedBy/authorizedAt excluded as audit-only, so M46 judges byte-identity over bound fields while touching who/when can never manufacture authority. The admission-set recipe (sorted identities joined with a newline plus a trailing newline, SHA-256) is documented as contract, not implementation detail. - MutationAuthorityDigestCertificateLoader.kt - parses config/quality/mutation-authority-digest-certificates.yml. An absent file means no certificates, which is fail-closed: no migration is possible and pre-authority-v2 authorizations stay unconsumable. A present but malformed file is a hard failure. Validates shape only - required fields, 64-hex digests, 40-hex fromBaseSha, known digest semantics, unique source digest, and no certificate that migrates a digest to itself. - MutationAuthorityDigestCertificateCeremony.kt - the lifecycle: M45 mint-time base binding, M46 retained immutability, M47 removal custody. Standalone over the two ledgers plus the authority baseSha, because a lifecycle rule is a transition property that cannot be expressed by parsing one file. M47 takes its strictly fail-closed half. With no certificate-aware consumption path there is no way to *prove* a valid consumption, so any base certificate absent from the candidate fails. That is deliberate and documented in the rule and the test, not an unfinished branch: a disappearance must never become its own evidence. When M40-M44 land it becomes "unless validly consumed by the same transition", judged against an independently established consumption. This is the T19 boundary flagged during Step 1. No ledger instance is created: minting the P1M certificate is a later base transition, so this change touches no config/quality file and no authority record. Tests (22 new): loader shape contract including every fail-closed case; the admission-set recipe pinned against the real committed 67-admission set; M45 in both directions plus the discriminator that binding is enforced at mint only, so a retained certificate survives intermediate merges; M46 driven as a loop over each bound field, which fails if a field is ever added to the type without being classified into the enforced payload; audit-only metadata changing without failing; and M47's removal custody in both directions. Verification: - :build-logic:test --tests 'dev.tramai.build.quality.MutationAuthorityDigestCertificate*' --rerun-tasks - BUILD SUCCESSFUL, 8 tasks executed - spotlessKotlinCheck, verifyStaticAnalysis, verifyChangePolicy - see the pull request
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
This is high-stakes build-authority governance machinery with subtle fail-closed semantics that warrants final human review despite only minor convention findings.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
This PR implements Step 2 of the authority-model reconciliation task, introducing the digest-migration certificate as a first-class, base-side authority object within the build-logic mutation-ratchet governance machinery. The certificate lets the whole-population authority digest be semantically upgraded (raw-v1 → authority-v2) over a bounded, named set of authorizations without rewriting the 67 historical P1 authorizations or touching the M36/M37 admission-payload comparison. The change is mechanism-only: it adds the type, its ledger, a fail-closed loader, and the mint/retention/removal lifecycle (M45/M46/M47), plus tests. It deliberately omits certificate consumption (M40–M44), task wiring, and any config/quality/** ledger instance, all deferred to Step 3.
Changes:
- Adds
MutationAuthorityDigestCertificate(immutable type with an audit-excludedenforcedPayload()) and itsMutationAuthorityDigestCertificatesledger plus the bounded admission-set digest recipe. - Adds a shape-only, fail-closed YAML loader and a transition-level ceremony enforcing mint-time base binding (M45), retained immutability (M46), and strictly fail-closed removal custody (M47).
- Adds 22 tests covering loader shape/fail-closed cases, the pinned admission-set digest recipe, and bidirectional lifecycle discriminators.
| File | Description |
|---|---|
MutationAuthorityDigestCertificate.kt |
Certificate type + ledger + admissionSetDigest recipe; documents field semantics and rule ownership. |
MutationAuthorityDigestCertificateLoader.kt |
Fail-closed, shape-only parser for the certificate YAML; validates every enforcement-critical field. |
MutationAuthorityDigestCertificateCeremony.kt |
Transition-level M45/M46/M47 lifecycle checks producing VerificationDiagnostics. |
MutationAuthorityDigestCertificateLoaderTest.kt |
Loader shape contract, fail-closed cases, and the admission-set recipe pinned to the committed 67-admission set. |
MutationAuthorityDigestCertificateCeremonyTest.kt |
Bidirectional discriminators for M45/M46/M47, including the fail-closed removal half. |
The implementation closely and faithfully mirrors the existing sibling MutationPopulationAdmissionLoader/ceremony patterns, is well-documented, and is backed by strong, behavior-level assertions. My two findings are minor convention items (parameter shadowing in the loader; non-robust repo-root resolution in the test) and are documented inline.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
1. Medium, valid: MutationAuthorityDigestCertificateLoaderTest.repositoryRoot() assumed the test starts exactly one level below the repository by using a single parent hop. This test is deliberately pinned against the real committed 67-admission ledger, so a brittle path guess would make it fail for a reason unrelated to the recipe. It now walks upward to the `gradlew` marker, the same idiom CanonicalProbeFunctionalTest uses, and fails with an explicit message naming the starting directory if no root is found. 2. Low, readability: parseCertificates declared `val raw = raw["certificates"]`, shadowing its own parameter. The parameter is now `ledger` and the binding `certificatesRaw`, so the two are distinguishable at the point of use. Both fixes are narrow: no rule, field, threshold or test expectation is weakened, and no suppression or baseline entry was added. Verification: - :build-logic:test --tests 'dev.tramai.build.quality.MutationAuthorityDigestCertificate*' --rerun-tasks - BUILD SUCCESSFUL - spotlessKotlinCheck -PtramaiFormattingBaseRef=e6e0d69... - BUILD SUCCESSFUL - verifyStaticAnalysis - Detekt baseline unchanged (4792 -> 4792, 0 added, 0 removed), no new findings - verifyChangePolicy -PchangePolicyBase=e6e0d69... - PASSED, 5 changed files, class build-logic
64d0545
into
epic/0.7.1-control-plane-authority
…igration certificate (#472) P1M: the single isolated transition between Step 2 and Step 3. It establishes certificate authority; it does not consume it. One certificate, derived from repository truth rather than chosen: - fromAlgorithm raw-v1 / fromDigest 9aebd3202288c82ff006f2db33c95cac0772746fa3c3061569167cd3f45df9b0 This is the digest every one of the 67 committed P1 admissions is bound to. The ledger holds exactly one distinct populationDigest value across 67 admissions, so the historical authority source is coherent; nothing was changed to make it match. - toAlgorithm authority-v2 / toDigest e6ad01dc1d2966894a6555304bc8ca9a04c8174e3c83ae88760fcfebf1464dad Recomputed independently over the frozen unrestricted measurement (2544 identities, the population the raw-v1 digest was taken over) from the canonical authority projection semantics: identity, canonical outcome, family, module, topology, analyzer. The same value was byte-identical across all four preserved campaigns. It is not copied from any candidate. - admissionSetDigest 98a9587a1068ec0cd7158a05ba6909c61c522308c272e7fa9cb27d5edd93a79c Reproduced from the exact 67 committed identities using the Step-2 contract (sorted, joined with a newline plus the trailing newline, SHA-256). It equals the value pinned by the Step-2 test. - fromBaseSha 64d0545 - the exact post-#471 epic tip, so M45's mint-time binding is against the real authority base and not a provisional preview SHA. - reason records the migration without implying any historical admission was upgraded or rewritten. The certificate ledger is the only changed path. The 67 admissions are byte-identical: no admission was modified, no digest rewritten, no metadata touched, and no mutation authority was re-measured. Ordering this preserves: a candidate may only consume migration authority that already existed in its base. A later transition (Step 3) consumes this certificate; P1M minting and consuming its own authority in one transition is exactly what the M43 analogue forbids. Mechanical gate P1-P8 (changed-path isolation, admission byte identity, semantic population identity, certificate cardinality, M45 binding, bounded admission set, source binding, independent target recomputation) is recorded in the pull request. No permanent rule was added for this migration, and no gate, threshold, suppression or baseline was weakened.
…igration certificate (#472) P1M: the single isolated transition between Step 2 and Step 3. It establishes certificate authority; it does not consume it. One certificate, derived from repository truth rather than chosen: - fromAlgorithm raw-v1 / fromDigest 9aebd3202288c82ff006f2db33c95cac0772746fa3c3061569167cd3f45df9b0 This is the digest every one of the 67 committed P1 admissions is bound to. The ledger holds exactly one distinct populationDigest value across 67 admissions, so the historical authority source is coherent; nothing was changed to make it match. - toAlgorithm authority-v2 / toDigest e6ad01dc1d2966894a6555304bc8ca9a04c8174e3c83ae88760fcfebf1464dad Recomputed independently over the frozen unrestricted measurement (2544 identities, the population the raw-v1 digest was taken over) from the canonical authority projection semantics: identity, canonical outcome, family, module, topology, analyzer. The same value was byte-identical across all four preserved campaigns. It is not copied from any candidate. - admissionSetDigest 98a9587a1068ec0cd7158a05ba6909c61c522308c272e7fa9cb27d5edd93a79c Reproduced from the exact 67 committed identities using the Step-2 contract (sorted, joined with a newline plus the trailing newline, SHA-256). It equals the value pinned by the Step-2 test. - fromBaseSha 64d0545 - the exact post-#471 epic tip, so M45's mint-time binding is against the real authority base and not a provisional preview SHA. - reason records the migration without implying any historical admission was upgraded or rewritten. The certificate ledger is the only changed path. The 67 admissions are byte-identical: no admission was modified, no digest rewritten, no metadata touched, and no mutation authority was re-measured. Ordering this preserves: a candidate may only consume migration authority that already existed in its base. A later transition (Step 3) consumes this certificate; P1M minting and consuming its own authority in one transition is exactly what the M43 analogue forbids. Mechanical gate P1-P8 (changed-path isolation, admission byte identity, semantic population identity, certificate cardinality, M45 binding, bounded admission set, source binding, independent target recomputation) is recorded in the pull request. No permanent rule was added for this migration, and no gate, threshold, suppression or baseline was weakened.
…igration certificate (#472) P1M: the single isolated transition between Step 2 and Step 3. It establishes certificate authority; it does not consume it. One certificate, derived from repository truth rather than chosen: - fromAlgorithm raw-v1 / fromDigest 9aebd3202288c82ff006f2db33c95cac0772746fa3c3061569167cd3f45df9b0 This is the digest every one of the 67 committed P1 admissions is bound to. The ledger holds exactly one distinct populationDigest value across 67 admissions, so the historical authority source is coherent; nothing was changed to make it match. - toAlgorithm authority-v2 / toDigest e6ad01dc1d2966894a6555304bc8ca9a04c8174e3c83ae88760fcfebf1464dad Recomputed independently over the frozen unrestricted measurement (2544 identities, the population the raw-v1 digest was taken over) from the canonical authority projection semantics: identity, canonical outcome, family, module, topology, analyzer. The same value was byte-identical across all four preserved campaigns. It is not copied from any candidate. - admissionSetDigest 98a9587a1068ec0cd7158a05ba6909c61c522308c272e7fa9cb27d5edd93a79c Reproduced from the exact 67 committed identities using the Step-2 contract (sorted, joined with a newline plus the trailing newline, SHA-256). It equals the value pinned by the Step-2 test. - fromBaseSha 64d0545 - the exact post-#471 epic tip, so M45's mint-time binding is against the real authority base and not a provisional preview SHA. - reason records the migration without implying any historical admission was upgraded or rewritten. The certificate ledger is the only changed path. The 67 admissions are byte-identical: no admission was modified, no digest rewritten, no metadata touched, and no mutation authority was re-measured. Ordering this preserves: a candidate may only consume migration authority that already existed in its base. A later transition (Step 3) consumes this certificate; P1M minting and consuming its own authority in one transition is exactly what the M43 analogue forbids. Mechanical gate P1-P8 (changed-path isolation, admission byte identity, semantic population identity, certificate cardinality, M45 binding, bounded admission set, source binding, independent target recomputation) is recorded in the pull request. No permanent rule was added for this migration, and no gate, threshold, suppression or baseline was weakened.


Base
e6e0d69ed2adfa47c6cdfa4485ef30bc184bb881— the post-merge Epic tip from #470.Step 2 of
TASK-0.7.1g1P2-AUTHORITY-MODEL-RECONCILIATION.md§6.2. The mechanism only, as scoped: type + ledger + loader + the mint/retention/removal lifecycle (M45/M46/M47) with its own fail-closed tests. No certificate-aware consumption, no M40-M44, no P2, no mutation of the 67 admissions.Scope
Five new files, 843 insertions, nothing modified:
MutationAuthorityDigestCertificate.kt— the certificate type, its ledger, and the bounded admission-set digest.MutationAuthorityDigestCertificateLoader.kt— parsesconfig/quality/mutation-authority-digest-certificates.yml.MutationAuthorityDigestCertificateCeremony.kt— the lifecycle: M45, M46, M47.No
config/quality/**file is touched: no ledger instance is created, because minting the P1M certificate is a later base transition. This change introduces only the machinery that transition will use.Design
The certificate carries a semantic upgrade of the whole-population authority digest without rewriting historical authority. The 67 P1 authorizations stay bound to their raw-v1 digests and byte-identical; the certificate stands beside them in its own ledger and states, in base authority, that one exact
fromDigestis equivalent to one exacttoDigestfor one exact bounded set of authorizations. M36's payload comparison never sees it, so M36 and M37 stay literally unchanged — no exception branch, no permitted-field list.Fields exactly as designed:
fromAlgorithm, fromDigest, toAlgorithm, toDigest, admissionSetDigest, fromBaseSha, reason.enforcedPayload()is that set, withauthorizedBy/authorizedAtexcluded as audit-only — so M46 judges byte-identity over bound fields while touching who/when can never manufacture authority.fromBaseSha≠ the authority base fails. Anti-replay at introduction only, exactly asMutationPopulationAdmission.fromBaseSha.The admission-set recipe (sorted identities joined with a newline plus a trailing newline, SHA-256) is documented as contract, not implementation detail, because whoever mints the P1M certificate must reproduce it exactly and M42 will compare against it.
Deliberate scope boundary — the T19 half
M47 takes its strictly fail-closed half. With no certificate-aware consumption path there is no way to prove a valid consumption, so any disappearance fails. That is documented in the rule, in the type, and in the test — not left as an unfinished branch. A disappearance must never become its own evidence. When M40-M44 land it becomes "unless validly consumed by the same transition", judged against an independently established consumption. This is the T19 boundary flagged during Step 1.
Tests (22)
fromBaseSha, unknown digest semantics, missingreason, absentschemaVersion, duplicate source digest (two certificates claiming one source contradict each other), and a certificate that migrates a digest to itself.98a9587a…), so the value P1M must reproduce is testable now.Verification
./gradlew :build-logic:test --tests 'dev.tramai.build.quality.MutationAuthorityDigestCertificate*' --rerun-tasks— BUILD SUCCESSFUL, 8 tasks executed./gradlew spotlessKotlinCheck(ratchet-scoped to the base) — BUILD SUCCESSFUL./gradlew verifyStaticAnalysis— no new findings; Detekt baseline unchanged (4792 → 4792, 0 added, 0 removed). Four findings in the first draft were fixed properly, by constants and by restructuring the test builders — not baselined and not suppressed../gradlew verifyChangePolicy -PchangePolicyBase=e6e0d69…— PASSED, 5 changed files, change classbuild-logic, no policy violationsNot run
Consumption (M40-M44), certificate-aware M34, the full T1-T19 matrix, and the task-level authority transport for certificates — all Step 3. No P2, no campaign rerun, no baseline regeneration, no classification or evolution change.
Remaining risks