Skip to content

authority(0.7.1g1P2-3c2): T3/T2/T8 mapped, T9 pinned to the chain it actually reaches - #478

Merged
GionaGranchelli merged 3 commits into
epic/0.7.1-control-plane-authorityfrom
task/0.7.1g1P2-3c2-t2-t3-t8-t9
Oct 2, 2026
Merged

GionaGranchelli merged 3 commits into
epic/0.7.1-control-plane-authorityfrom
task/0.7.1g1P2-3c2-t2-t3-t8-t9

Conversation

@GionaGranchelli

@GionaGranchelli GionaGranchelli commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Base

514f61a9a19f5778b58f65ddcf151e4878e5b433 — the post-#476 epic tip (#476 squash-merged; branch cut from this tip, tree clean).

Scope

One test file, one test added. MutationPopulationAdmissionCeremonyTest +47 lines. No production change, no workflow change, no baseline, no suppression.

Audit — what already closed each row, and what did not

Coverage rule applied: a rule-id hit is not coverage. A row closes only when a test reproduces the canonical scenario, with the correct initial authority state, the specific perturbation, the intended rule and the intended outcome.

row §7 canonical scenario §7 expects pre-existing evidence gap
T2 neighbour outcome flips NON_KILLED→KILLED authority digest changes, M34 fail MutationPopulationEvolutionProofTest "T2 an outcome change changes the authority projection" (digest sensitivity) + MutationPopulationAdmissionCeremonyTest "authorized row with a different population digest fails M34" (M34 on a digest mismatch) none — closed by that pair
T3 authorized row's own raw status changes M32 fail MutationPopulationAdmissionCeremonyTest "authorized identity with a different raw status fails M32" — authorized row, own raw status, M32, with the M32 message asserted verbatim none — closed
T8 analyzer/mutator/timeout semantics drift fail, analyzer stays inside the digest "authorized row measured under different analyzer semantics fails M33" (refusal, digest minted against the same candidate so nothing earlier refuses) + "T8 analyzer semantics change the authority projection" (digest sensitivity) none — closed by that pair
T9 family/module re-homing of an authorized identity M32 fail none. The only family/module tests are pending-rewrite M36 cases added below

Two rows I initially tested turned out to be duplicates of pre-existing coverage, so I removed those tests rather than keep T-numbered duplicates, per the task's mapping rule. The net diff is smaller than my first attempt, and the mapping above is the evidence.

Audit method correction worth recording: my first pass searched test bodies for Mxx strings and could not see T2 an outcome change… / T8 analyzer semantics change…, because those tests contain no rule id. Naming-based T-row matching is required alongside rule-id matching.

T9 — the finding (CORRECTED)

My first T9 test was testing the wrong transition, and the reviewer caught it. The fixture called
row("target", family = retryFamily), and MutationRatchetTestSupport.row() derives both
identity = identityOf(marker, family, module) and className = "dev.tramai.$family.Policy" from the
family. So that perturbation changed an identity-bearing field (className) and simply minted a new
mutant — it never re-homed an existing identity, and it could not have reached M32 by construction. The
M06/M37 chain I reported was real but it was evidence about a className change, not about T9.

MutationIdentity.stableKey() hashes module, className, method, descriptor, mutator, description, block
and index. family is not identity-bearing; module is. §7 fuses the two, so the corrected test
separates them:

  • family-only re-homing — row("target").copy(family = retryFamily), with the candidate topology
    kept coherent (baseFamilies + (retryFamily to retryTarget)) and the identity-preservation asserted as
    an explicit precondition. The identity genuinely survives, so the only rule that can refuse the row is
    the persisted-row binding, and it does: M32, with the M32 message, exactly as §7 expects.
  • module move — identity-bearing, a different attack. It cannot reach M32, because the mutant that
    arrives is a new identity: refused as a new survivor (M06) while the authorization for the old
    identity is orphaned (M37). Pinned separately so neither case can be read as evidence for the other.

No production defect, and no production change. M32 is reachable for the canonical attack; my earlier
"M32 cannot fire" claim was a fixture artifact and is withdrawn. The identity-preservation assertion is
deliberate: it makes the fixture self-checking, so this test can never again silently drift into a
className discriminator.

Verification

:build-logic:test --tests MutationPopulationAdmissionCeremonyTest --rerun-tasks
  → T9_RC=0, 25 tests, 0 failures
  → T9 a family-only re-homing keeps identity and fails M32, while a module move changes identity
    (family-only case reaches M32 and the M32 message; module case reaches M06 + M37)
spotlessKotlinCheck / verifyStaticAnalysis / verifyChangePolicy -PchangePolicyBase=514f61a9…
  → B_RC=0, verifyChangePolicy PASSED — 1 changed file(s), change class: build-logic
  → Detekt baseline OK: base 4792 -> current 4792; 0 removed, 0 added
git status → exactly 1 modified file

Authority invariants

67 historical admissions modified: 0 · P1M certificate modified: false · new AdmissionAuthority production sites: 0 · new Valid construction paths: 0 · M36/M37 semantics changed: false · M34 weakened: false · T7 modified: false · T10 started: false · production changes: none.

…actually reaches

Step 3c2 proof closure for T2, T3, T8 and T9. Audit applied the rule that a rule-id hit is not
coverage: a row closes only when a test reproduces the canonical scenario with the correct initial
authority state, the specific perturbation, the intended rule and the intended outcome.

ALREADY CLOSED, mapped rather than duplicated:

- T3: MutationPopulationAdmissionCeremonyTest "authorized identity with a different raw status fails
  M32" is §7's scenario field for field - the authorized row, its own raw status, M32, with the M32
  message asserted verbatim.
- T2: "T2 an outcome change changes the authority projection" (digest sensitivity) plus "authorized
  row with a different population digest fails M34" (M34 on a mismatch) close both halves of the row.
- T8: "authorized row measured under different analyzer semantics fails M33" (refusal, digest minted
  against the same candidate so no earlier predicate refuses) plus "T8 analyzer semantics change the
  authority projection" close both halves.

Two T-numbered tests I wrote first turned out to duplicate that pre-existing coverage, so they were
REMOVED rather than shipped as T-labelled duplicates. Net diff is one file, one test.

Method correction worth recording: my first audit pass searched test bodies for rule ids and could not
see "T2 an outcome change..." or "T8 analyzer semantics change...", because neither contains one.
Naming-based T-row matching is required alongside rule-id matching.

T9 - the finding. Section 7 maps T9 to "M32 fail". M32 cannot fire for this attack and the
implementation is not at fault: MutationPopulationAdmission.admitsRow compares identity, status,
outcome, family and module, but a mutant's identity is a hash over the row's coordinates, so
re-homing an authorized row to another family produces a DIFFERENT identity. The
same-identity/different-row case that is M32's only precondition is unachievable, so the premise
does not hold. Measured with a temporary probe, the transition actually reaches:

  M06: the moved row is a NEW NON_KILLED identity absent from the base authority
  M37: the authorization that admitted the old identity disappears unconsumed

The re-homing is therefore refused - what the row's OUTCOME requires - with a different named rule.
The new test pins that chain and its KDoc states the divergence, so the row is never counted from a
rule-id hit. No production change is proposed: the transition is refused, and the family/module terms
in admitsRow remain correct defence in depth.

Verification: focused suites 180 tests / 0 failures (T9 new; T2/T8 pre-existing); spotless clean;
Detekt baseline 4792 -> 4792 with 0 added; verifyChangePolicy PASSED against base 514f61a, class
build-logic; exactly 1 file modified.

Authority invariants: 67 historical admissions untouched, P1M certificate untouched, no new
AdmissionAuthority production site, no new Valid construction path, M36/M37 unchanged, M34 unchanged,
T7 unchanged, T10 not started, no production change.
Copilot AI balanced review requested due to automatic review settings October 2, 2026 13:20

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

Although the single test is mechanically verified to match production behavior, it deliberately reinterprets the §7 authority matrix (M32 → M06+M37), a governance-semantics judgment that warrants human ratification.

Review effort: Balanced
Findings: None

What changed in this PR

This PR adds a single test to MutationPopulationAdmissionCeremonyTest to close row T9 of the §7 authority matrix (family/module re-homing of an authorized mutation identity). The §7 spec maps T9 to an M32 failure, but the PR argues—and the test pins—that M32 is structurally unreachable: because a mutant's identity is a hash over its coordinates (family is encoded in the class name, plus module), re-homing produces a different identity, so the same-identity precondition admitsRow requires never holds. The test instead asserts the chain the transition actually reaches: the moved row surfaces as a new survivor (M06/MUTATION_RATCHET_NEW_SURVIVOR) while the old authorization is orphaned without consumption (M37/MUTATION_RATCHET_ADMISSION_INVALID). The outcome (refusal) still matches §7; only the named rule differs, and the KDoc documents this divergence so the row is never counted from a rule-id hit. There is no production, workflow, baseline, or suppression change.

Changes:

  • Added test T9 re-homing an authorized identity produces the moved identity and orphaned custody asserting the M06 + M37 refusal chain.
  • Added a KDoc block explaining why the §7 M32 mapping is structurally unachievable for this attack.
File Description
build-logic/​src/​test/​kotlin/​dev/​tramai/​build/​quality/​MutationPopulationAdmissionCeremonyTest.kt Adds the T9 re-homing test (and documenting KDoc) that pins the real M06/M37 refusal chain instead of the spec's nominal M32.

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

Copy link
Copy Markdown
Owner Author

Review hold on #478: T9 is not yet proven, because the new test changes more than the canonical T9 premise.

Production identity schema v2 hashes: module, className, method, methodDescription, mutator, description, block, index. It does not hash family (MutationIdentity.kt).

But MutationRatchetTestSupport.row(marker, family=...) derives className = "dev.tramai.$family.Policy" and then recomputes the identity from that className. So this test:

row("target", family = retryFamily)

changes both the row's family metadata and className, which necessarily produces a different identity. That is why it reaches M06+M37. It does not establish that §7's same-identity family re-homing premise is unachievable.

A family-only re-homing is structurally possible under identity schema v2: keep the original identity/module/className/method/etc. and change only MutationOutcome.family. Because family is outside MutationIdentity.stableKey(), the row can retain the same valid identity, and MutationPopulationAdmission.admitsRow explicitly compares family, which is exactly the path where M32 should be reachable.

Please correct T9 by constructing the candidate row from the authorized row and changing only the canonical T9 field(s) while preserving every identity field. For a family-only case, conceptually:

val moved = target.copy(family = retryFamily)

with coherent candidate family topology/counters. Then assert whether M32 fires as §7 specifies. If the canonical row explicitly requires module re-homing as well, split the two dimensions rather than treating them as equivalent: module is identity-bearing, family is not.

Do not change production code unless the corrected canonical discriminator proves a real defect.

Separately, the 10 ReleaseVerificationPluginTest failures are still unproven pre-existing. Since the required full local suite was red, please also run that class serially at pristine base 514f61a9... before final handoff. That is secondary to the T9 blocker, but it should be settled rather than inferred from ownership.

…ly-only re-homing fails M32

The first T9 test was testing the wrong transition. MutationRatchetTestSupport.row() derives BOTH
identity = identityOf(marker, family, module) and className = "dev.tramai.$family.Policy" from the
family argument, so row("target", family = retryFamily) changed an identity-bearing field and minted
a new mutant. It tested a className change wearing a T9 label, and the M06/M37 chain it reported was
evidence about that transition, not the canonical one. The "M32 cannot fire" conclusion was a fixture
artifact and is withdrawn.

MutationIdentity.stableKey() hashes module, className, method, descriptor, mutator, description,
block and index: family is NOT identity-bearing, module IS. Section 7 fuses the two, so the corrected
test separates them.

family - non-identity-bearing. The re-homing is applied to an existing row via
row("target").copy(family = retryFamily), so className and every other stableKey input survive. The
candidate topology is kept coherent with baseFamilies + (retryFamily to retryTarget); the earlier
"families [policy, retry] disagree with the governing targetFamilies [policy]" diagnostic was exactly
that gap. Identity preservation is asserted as an explicit precondition, so this test can never again
silently drift into a className discriminator. Result: M32, with the M32 message - section 7 as
written. No production defect.

module - identity-bearing, therefore a different attack. It cannot reach M32, because the mutant that
arrives is a new identity: refused as a new survivor (M06) while the authorization for the old
identity is orphaned (M37). Pinned separately so neither case can be read as evidence for the other.

Verification: MutationPopulationAdmissionCeremonyTest 25 tests / 0 failures at this head. Production
untouched.
@GionaGranchelli

Copy link
Copy Markdown
Owner Author

T9 corrected. You were right, and the diagnosis was exact.

MutationRatchetTestSupport.row() (:79, :82) derives both identity = identityOf(marker, family, module) and className = "dev.tramai.$family.Policy" from the family argument. So row("target", family = retryFamily) changed an identity-bearing field and minted a new mutant. It was a className-change test wearing a T9 label, and the M06/M37 chain I reported was evidence about that transition, not the canonical one. My "M32 cannot fire" conclusion is withdrawn — it was a fixture artifact.

What the corrected test does now:

  • family (non-identity-bearing) — row("target").copy(family = retryFamily), so className and every other stableKey input survive, with the candidate topology kept coherent via baseFamilies + (retryFamily to retryTarget) (the earlier families [policy, retry] disagree with the governing targetFamilies [policy] diagnostic was exactly this gap). Identity preservation is asserted as an explicit precondition, so the test cannot silently drift back into a className discriminator. Result: M32, with the M32 message — §7 as written. No production defect.
  • module (identity-bearing) — separated out as a genuinely different attack. It cannot reach M32 because the arriving mutant is a new identity: M06 + M37. Pinned separately so neither case can be read as evidence for the other.

Local result at head: T9_RC=0, 25 tests, 0 failures in MutationPopulationAdmissionCeremonyTest. Production untouched. The PR body's T9 section is rewritten to record the correction rather than leave the withdrawn claim standing.

On the 10 ReleaseVerificationPluginTest failures: running now, serially, in a clean clone checked out at the pristine base 514f61a9a19f5778b58f65ddcf151e4878e5b433 (clone rather than worktree, because build-logic tests that need the repo root as a .git DIR fail in worktrees). The result will be recorded here as same-signature evidence, or as a green base — in which case those 10 are mine to explain and I'll say so.

@GionaGranchelli

Copy link
Copy Markdown
Owner Author

Base reproduction recorded — same signature, so the 10 are pre-existing.

Correction first: my initial attempt proved nothing. I filtered --tests dev.tramai.build.quality.ReleaseVerificationPluginTest, but the class lives at dev.tramai.build.release.ReleaseVerificationPluginTest, so Gradle reported No tests found for given includes and ran zero tests. My tallier then printed "BASE IS GREEN", because it treated tests=0 as zero failures. That branch is now explicit: tests=0 reports INCONCLUSIVE, so a run that executes nothing can never be read as evidence again.

Corrected run — clean clone, pristine base, serial, nothing else running:

base HEAD: 514f61a9a19f5778b58f65ddcf151e4878e5b433
base worktree clean: yes
BASE_RV_GRADLE_RC=1
BASE ReleaseVerificationPluginTest: tests=24 failures=10
   T13 prepareSovereignReleaseArtifacts has real producer edges from a clean workspace
   T15 missing remote-publish properties produce the stable diagnostic
   T1b task types resolve to the typed classes
   T2 exact task names preserved
   T5 unsigned mode does not require asc
   T7 incomplete evidence deletes stale manifest
   T7c sovereign bundle fails closed on corrupt catalog
   T9 evidence index dependency wiring
   W1 wired expectedDescriptions passes when POM matches the catalog description
   W2 wired expectedDescriptions fails when POM diverges from the catalog description

The candidate run at the task base failed the same 10 tests in the same class, and every name captured from that log (T7c, W1, W2, T15, T1b, T2) is in the base set. Structural corroboration: the candidate diff is one test in MutationPopulationAdmissionCeremonyTest, and the authority family reported zero failures throughout.

Caveat I am not hiding: I captured the candidate-side names from the run log, not from its XML, because that run's results were later overwritten by a focused --rerun-tasks of the ceremony class. So the match is count-for-count plus six shared names, not a frozen set diff. If you want the frozen comparison, I can re-run the candidate class and diff the two name sets directly.

This closes the last open evidence question on my side. The other five local failures already had stronger explanations (TramaiDocsGuardsPluginTest is branch-checkout-dependent, and CancellationWiringTest's message shows Gradle daemon contention from my own parallel invocation).

Copy link
Copy Markdown
Owner Author

The corrected T9 family-only discriminator is now structurally sound and resolves the review blocker: it preserves the exact identity-bearing fields, changes only family, and reaches M32 as §7 expects.

One small test-precision follow-up before merge: the module-move half's KDoc/comment says it pins M06 + M37, but the assertion currently checks only DiagnosticCode.MUTATION_RATCHET_ADMISSION_INVALID. That code is shared by multiple admission lifecycle rules. Please either:

  • assert that one failure message contains M37, matching the claim; or
  • soften the claim to generic admission-custody refusal.

No production change needed. The canonical T9 closure itself (family-only re-homing -> same identity -> M32) is now correct.

The 10 ReleaseVerificationPluginTest failures are also now properly base-reproduced at pristine 514f61a9, so that local-suite question is closed.

…move assertion

The module half of the T9 test asserted only MUTATION_RATCHET_ADMISSION_INVALID, which is broader
than the M37 the KDoc claims. Pin the named rule: the orphaned authorization must fail M37, with the
M37 message, so the assertion matches its own documentation and M37 cannot silently degrade into a
sibling rule under the same code.

The canonical family-only half is unchanged: identity preserved, family changed, M32 reached.

MutationPopulationAdmissionCeremonyTest: 25 tests / 0 failures. spotless, static analysis and
verifyChangePolicy (base 514f61a) all pass. Production untouched.
@GionaGranchelli

Copy link
Copy Markdown
Owner Author

M37 pin done at head 1ee3e67a41f3a2cb6121285bb68110bc30311247.

Supplemental module assertion now pins M37 exactly (hasCode(…ADMISSION_INVALID) && message.contains("M37")) instead of the broader code, so it matches its own KDoc. It passes — which is independent confirmation that the module move fails M37 and not a sibling rule under the same code. Canonical family-only half unchanged: identity preserved, family changed, M32 reached.

Local at this head: MutationPopulationAdmissionCeremonyTest 25 tests / 0 failures; spotless, static analysis and verifyChangePolicy (base 514f61a) all pass; 1 changed file; production untouched.

Exact-head CI, confirmed per workflow by headSha rather than by tally:

1ee3e67a41  completed/success  Architecture Authority
1ee3e67a41  completed/success  Sovereign Runtime Release Candidate
1ee3e67a41  completed/success  Mutation Authority Integration
1ee3e67a41  completed/success  CI

PR check summary: 19 pass, no non-pass. Ready for your review and merge — not merged by me.

@GionaGranchelli
GionaGranchelli merged commit a1ce28c into epic/0.7.1-control-plane-authority Oct 2, 2026
19 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