diff --git a/docs/roadmap/0.7.0/TASK-0.7.1g1G5c-SURVIVED-12-MANIFEST.json b/docs/roadmap/0.7.0/TASK-0.7.1g1G5c-SURVIVED-12-MANIFEST.json new file mode 100644 index 00000000..c83c47ec --- /dev/null +++ b/docs/roadmap/0.7.0/TASK-0.7.1g1G5c-SURVIVED-12-MANIFEST.json @@ -0,0 +1,426 @@ +{ + "schemaVersion": 1, + "task": "TASK-0.7.1g1G5c", + "parent": "TASK-0.7.1g1G5", + "title": "SURVIVED mapping closure and semantic adjudication (12 identities)", + "base": "42d9d9487ccf59ca3f348d0babfe9bbda232ef0f", + "baseProvenance": "squash merge of #462 on epic/0.7.1-control-plane-authority; frozen parent digest 92a9d06d5a8e43c5fb659f9d65bdd2e73d6ed5f45c1a8536ac70781b03503584 unchanged; admission ledger admissions: []", + "campaigns": { + "control": { + "throwaway": "78458d1eecce60c7f8f307b4564fb47afda0da79", + "measuredCommit": "78458d1eecce60c7f8f307b4564fb47afda0da79", + "configDiff": "family-list narrowing only: 53 deletions in config/quality/test-quality.yml, approval block byte-identical", + "familyCensus": { + "total": 918, + "byModule": { + ":tramai-engine": 492, + ":tramai-security": 426 + }, + "statuses": { + "KILLED": 661, + "SURVIVED": 133, + "NO_COVERAGE": 62, + "TIMED_OUT": 62 + } + }, + "durationSeconds": 1000, + "buildResult": "BUILD SUCCESSFUL" + }, + "candidate": { + "throwaway": "7acbd868adb94658e90ef55d3261d315f6eddda9", + "parent": "78458d1eecce60c7f8f307b4564fb47afda0da79", + "testCommit": "d3837a5880eccf9bf23e2fc5593048e0eb06e395", + "measuredCommit": "7acbd868adb94658e90ef55d3261d315f6eddda9", + "distinctFromControl": "only the test commit", + "familyCensus": { + "total": 918, + "statuses": { + "KILLED": 662, + "SURVIVED": 132, + "NO_COVERAGE": 62, + "TIMED_OUT": 62 + } + }, + "durationSeconds": 923, + "buildResult": "BUILD SUCCESSFUL" + } + }, + "reconciliation": { + "sharedIdentities": 918, + "controlOnly": 0, + "candidateOnly": 0, + "statusMovements": 1, + "movements": [ + { + "identity": "a9760bea3a92a752255a2becee7edb3ffa0957cf0e88cb75a0a11db850dcc747", + "from": "SURVIVED", + "to": "KILLED", + "killingTest": "ApprovalResumeSuspensionContractTest.[method:a cancellation resumed into the structured-parse uncertain path is not replaced by the primary failure()]", + "sourceLine": 102 + } + ], + "killedRegressions": 0, + "newTimeouts": 0, + "identityLoss": 0, + "identityGain": 0, + "recovered": 12, + "missing": 0, + "duplicates": 0, + "movementIdentityNote": "Full 64-hex canonical identities are carried in movement records. An earlier revision of this manifest recorded a malformed prefix (a9760bea3a92f) here; corrected, superseded not erased." + }, + "mappingDiscipline": { + "authority": "MutationIdentity.stableKey(): sha256 over module\u241fclass\u241fmethod\u241fdescriptor\u241fmutator\u241fdescription\u241fblock\u241findex; line deliberately excluded", + "verified": "reproduces all 68 identities of the P1 candidate-only manifest exactly", + "superseded": "G5a marked five identities AMBIGUOUS because opcode-shape enumeration cannot recover PIT's block/index coordinates. They are now EXACT: the canonical identity itself carries block/index, so matching PIT's emitted rows resolves each uniquely. The G5a ambiguity is retained here as superseded, not erased." + }, + "hypotheses": { + "A_observable_missing_discriminator": [ + "a9760bea3a92" + ], + "B_value_discarded": [ + "3a5c5213b226", + "56d3366434a6", + "6a1ce2f46d2a", + "a542bbe6db3f", + "f44636049288", + "eb21aaed3192" + ], + "other_structural": [ + "1ce5967ee60b", + "c7774df8ccdc", + "3658ba45eaed", + "8e18b4b48968", + "d38ac52f2843" + ], + "F_unclear": [], + "E_tooling_limitation": [] + }, + "finalTotals": { + "inputSURVIVED": 12, + "KILLED": 1, + "EQUIVALENT": 11, + "UNREACHABLE": 0, + "TOOLING_LIMITATION": 0, + "UNDETERMINED": 0 + }, + "parentAccounting": { + "parent": 52, + "settledByG5b": 36, + "inputG5c": 12, + "killed": 1, + "equivalent": 11, + "remainingForG5d": 4, + "remainingAre": "exactly the frozen NO_COVERAGE identities" + }, + "identities": [ + { + "identity": "1ce5967ee60b6f4380d69dead598dde89212eeb17c2b9be6348bf9dfc3928981", + "className": "dev.tramai.engine.approval.ApprovalResumeCoordinator", + "method": "resume", + "methodDescription": "(Ldev/tramai/engine/ResumeApprovalCommand;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "VoidMethodCall", + "description": "removed call to dev/tramai/core/coroutines/CancellationKt::rethrowIfCancellation", + "sourceLine": 108, + "pitBlock": 91, + "pitIndex": 641, + "bytecodePc": 1100, + "instruction": "invokestatic dev/tramai/core/coroutines/CancellationKt.rethrowIfCancellation", + "g5aOriginalMapping": "AMBIGUOUS (superseded: count mismatch from opcode-shape enumeration; resolved exactly by canonical identity)", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 20, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "structural equivalence (successor convergence / handler dominance / compiler guard)", + "proofUsed": "catch-handler dominance: resume's exception table registers CancellationException->1088 and Exception->1093; pc1100 executes inside the 1093 (Exception) handler, and JVM dispatch order guarantees the earlier CancellationException handler wins. The helper's only branch (this is CancellationException) cannot fire, so removing the call cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table" + }, + { + "identity": "3658ba45eaedbf6a946036d210a91578d65fda8b0f0f295137393d3454e9a5e0", + "className": "dev.tramai.engine.approval.ApprovalSuspensionCoordinator", + "method": "suspendToolExecution", + "methodDescription": "(Ldev/tramai/engine/approval/SuspendToolExecutionRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "VoidMethodCall", + "description": "removed call to kotlin/jvm/internal/Intrinsics::checkNotNull", + "sourceLine": 160, + "pitBlock": 31, + "pitIndex": 168, + "bytecodePc": 287, + "instruction": "invokestatic kotlin/jvm/internal/Intrinsics.checkNotNull", + "g5aOriginalMapping": "AMBIGUOUS (superseded: count mismatch from opcode-shape enumeration; resolved exactly by canonical identity)", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 95, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "structural equivalence (successor convergence / handler dominance / compiler guard)", + "proofUsed": "compiler-inserted parameter guard: pc285 aload 7; pc287 checkNotNull; pc290 aload 7; pc293 invokespecial CreateApprovalChallenge(...). The checked value is immediately passed on as a non-null Kotlin constructor parameter, whose own parameter check raises the same NPE one instruction later, so removal cannot change exception type or the observable outcome on any valid path.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table" + }, + { + "identity": "3a5c5213b226f1f86b48c12172489ee618b9af890d750ab3ac1d7d2032854e0a", + "className": "dev.tramai.engine.approval.ApprovalSuspensionCoordinator", + "method": "compensateStep", + "methodDescription": "(Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NullReturnVals", + "description": "replaced return value with null for dev/tramai/engine/approval/ApprovalSuspensionCoordinator::compensateStep", + "sourceLine": 307, + "pitBlock": 17, + "pitIndex": 106, + "bytecodePc": 161, + "instruction": "areturn of kotlin/Unit.INSTANCE (preceded by getstatic kotlin/Unit.INSTANCE)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 13, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "B (returned value semantically discarded)", + "proofUsed": "caller-discard: every call site's resumed path begins with an explicit pop (compensateStep pc252, persistSuspendedInvocation pc1231, persistUngoverned pc880, persistGoverned pc1049, requireExistingAttributionMatches pc699), so the returned Unit value is never observed; replacing it with null cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table", + "bytecodePcVerifiedBy": "javap at the census base: compensateStep pc161 is areturn, line-table entry 307, preceded by getstatic kotlin/Unit.INSTANCE", + "callSiteDiscardEvidence": "supplementary: the resumed path of every call site pops the result" + }, + { + "identity": "56d3366434a6608d7b5ccd59b6c1377be7324ab2078ec7a09b872153968e4466", + "className": "dev.tramai.engine.approval.DefaultApprovalGateway", + "method": "requireExistingAttributionMatches", + "methodDescription": "(Ldev/tramai/core/approval/ApprovalRequest;Ldev/tramai/core/identity/GovernedRunIdentity;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NullReturnVals", + "description": "replaced return value with null for dev/tramai/engine/approval/DefaultApprovalGateway::requireExistingAttributionMatches", + "sourceLine": 205, + "pitBlock": 9, + "pitIndex": 50, + "bytecodePc": 100, + "instruction": "areturn of kotlin/Unit.INSTANCE (preceded by getstatic kotlin/Unit.INSTANCE)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 6, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "B (returned value semantically discarded)", + "proofUsed": "caller-discard: every call site's resumed path begins with an explicit pop (compensateStep pc252, persistSuspendedInvocation pc1231, persistUngoverned pc880, persistGoverned pc1049, requireExistingAttributionMatches pc699), so the returned Unit value is never observed; replacing it with null cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table", + "bytecodePcVerifiedBy": "javap at the census base: requireExistingAttributionMatches pc100 is areturn, line-table entry 205, preceded by getstatic kotlin/Unit.INSTANCE", + "callSiteDiscardEvidence": "supplementary: the resumed path of every call site pops the result" + }, + { + "identity": "6a1ce2f46d2ad5cdb7fb857a56fcf4ca0d3d87ffc3825279121386e04c3f7c88", + "className": "dev.tramai.engine.approval.DefaultApprovalGateway", + "method": "requireExistingAttributionMatches", + "methodDescription": "(Ldev/tramai/core/approval/ApprovalRequest;Ldev/tramai/core/identity/GovernedRunIdentity;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NullReturnVals", + "description": "replaced return value with null for dev/tramai/engine/approval/DefaultApprovalGateway::requireExistingAttributionMatches", + "sourceLine": 219, + "pitBlock": 26, + "pitIndex": 141, + "bytecodePc": 244, + "instruction": "areturn of kotlin/Unit.INSTANCE (preceded by getstatic kotlin/Unit.INSTANCE)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 2, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "B (returned value semantically discarded)", + "proofUsed": "caller-discard: every call site's resumed path begins with an explicit pop (compensateStep pc252, persistSuspendedInvocation pc1231, persistUngoverned pc880, persistGoverned pc1049, requireExistingAttributionMatches pc699), so the returned Unit value is never observed; replacing it with null cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table", + "bytecodePcVerifiedBy": "javap at the census base: requireExistingAttributionMatches pc244 is areturn, line-table entry 219, preceded by getstatic kotlin/Unit.INSTANCE", + "callSiteDiscardEvidence": "supplementary: the resumed path of every call site pops the result" + }, + { + "identity": "8e18b4b48968d65f181bd226d6e81d99c6c9abb3427463ae77773eb4c901e658", + "className": "dev.tramai.engine.approval.ApprovalRunAttributionKt", + "method": "decodeApprovalAttribution", + "methodDescription": "(Ljava/lang/String;Ljava/util/Map;)Ldev/tramai/engine/approval/ApprovalRunAttribution;", + "mutator": "NegateConditionals", + "description": "negated conditional", + "sourceLine": 174, + "pitBlock": 22, + "pitIndex": 127, + "bytecodePc": 189, + "instruction": "ifeq following instanceof Collection (inlined any, _Collections.kt 1807)", + "g5aOriginalMapping": "AMBIGUOUS (superseded: count mismatch from opcode-shape enumeration; resolved exactly by canonical identity)", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 5, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "structural equivalence (successor convergence / handler dominance / compiler guard)", + "proofUsed": "successor convergence: negating the inlined fast-path test routes Collection receivers onto the plain-Iterable loop path; empty input returns false on both paths and non-empty input runs the identical loop, so no observable difference exists.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table" + }, + { + "identity": "a542bbe6db3f9f551bbbea6972a28dc670945d1232221bd1d9a3f98814a6defb", + "className": "dev.tramai.engine.approval.DefaultApprovalGateway", + "method": "persistGoverned", + "methodDescription": "(Ldev/tramai/engine/approval/ApprovalGatewayPersistenceRequest;Ldev/tramai/core/identity/GovernedRunIdentity;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NullReturnVals", + "description": "replaced return value with null for dev/tramai/engine/approval/DefaultApprovalGateway::persistGoverned", + "sourceLine": 265, + "pitBlock": 35, + "pitIndex": 204, + "bytecodePc": 341, + "instruction": "areturn of kotlin/Unit.INSTANCE (preceded by getstatic kotlin/Unit.INSTANCE)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 3, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "B (returned value semantically discarded)", + "proofUsed": "caller-discard: every call site's resumed path begins with an explicit pop (compensateStep pc252, persistSuspendedInvocation pc1231, persistUngoverned pc880, persistGoverned pc1049, requireExistingAttributionMatches pc699), so the returned Unit value is never observed; replacing it with null cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table", + "bytecodePcVerifiedBy": "javap at the census base: persistGoverned pc341 is areturn, line-table entry 265, preceded by getstatic kotlin/Unit.INSTANCE", + "callSiteDiscardEvidence": "supplementary: the resumed path of every call site pops the result" + }, + { + "identity": "a9760bea3a92a752255a2becee7edb3ffa0957cf0e88cb75a0a11db850dcc747", + "className": "dev.tramai.engine.approval.ApprovalResumeCoordinator", + "method": "resume", + "methodDescription": "(Ldev/tramai/engine/ResumeApprovalCommand;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NegateConditionals", + "description": "negated conditional", + "sourceLine": 102, + "pitBlock": 76, + "pitIndex": 533, + "bytecodePc": 912, + "instruction": "ifnonnull (Elvis on e::class.simpleName)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 5, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "A (observable, missing discriminator)", + "proofUsed": "SURVIVED -> KILLED by measurement at 7acbd868: the diagnostic 'structured-parse-failed: ${e::class.simpleName ?: \"unknown\"}' is now pinned by isEqualTo in ApprovalResumeSuspensionContractTest, instead of the startsWith prefix that both the original and the mutant satisfied.", + "killingTest": "ApprovalResumeSuspensionContractTest.[engine:junit-jupiter]/[class:...ApprovalResumeSuspensionContractTest]/[method:a cancellation resumed into the structured-parse uncertain path is not replaced by the primary failure()]", + "finalStatus": "KILLED", + "finalDisposition": "KILLED", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table" + }, + { + "identity": "c7774df8ccdcefaaac0c151f68b24313cc511a6a0800951cd375dfe217d265b6", + "className": "dev.tramai.engine.approval.ApprovalSuspensionCoordinator", + "method": "compensateStep", + "methodDescription": "(Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "VoidMethodCall", + "description": "removed call to dev/tramai/core/coroutines/CancellationKt::rethrowIfCancellation", + "sourceLine": 305, + "pitBlock": 16, + "pitIndex": 101, + "bytecodePc": 155, + "instruction": "invokestatic dev/tramai/core/coroutines/CancellationKt.rethrowIfCancellation", + "g5aOriginalMapping": "AMBIGUOUS (superseded: count mismatch from opcode-shape enumeration; resolved exactly by canonical identity)", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 4, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "structural equivalence (successor convergence / handler dominance / compiler guard)", + "proofUsed": "catch-handler dominance: compensateStep registers CancellationException->147 and Exception->150; pc155 executes inside the 150 handler, so the value is never a CancellationException.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table" + }, + { + "identity": "d38ac52f2843448a4df4cba789ae27ddf9df3f73356c9c8ef972f2f03cc180c3", + "className": "dev.tramai.engine.approval.ApprovalRunAttributionKt", + "method": "decodeApprovalAttribution", + "methodDescription": "(Ljava/lang/String;Ljava/util/Map;)Ldev/tramai/engine/approval/ApprovalRunAttribution;", + "mutator": "NegateConditionals", + "description": "negated conditional", + "sourceLine": 174, + "pitBlock": 24, + "pitIndex": 131, + "bytecodePc": 189, + "instruction": "ifeq following instanceof Collection (inlined any, _Collections.kt 1807)", + "g5aOriginalMapping": "AMBIGUOUS (superseded: count mismatch from opcode-shape enumeration; resolved exactly by canonical identity)", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 5, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "structural equivalence (successor convergence / handler dominance / compiler guard)", + "proofUsed": "same fast-path test in the second inlined stratum copy; identical successor convergence.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table" + }, + { + "identity": "eb21aaed31923633c541c603ebf29c170343b1e822874768f732093a099b3a18", + "className": "dev.tramai.engine.approval.ApprovalSuspensionCoordinator", + "method": "persistSuspendedInvocation", + "methodDescription": "(Ldev/tramai/engine/approval/ApprovalSuspensionCoordinator$GovernedSuspension;Ldev/tramai/engine/SuspendedInvocationMetadata;Ldev/tramai/engine/SensitiveReplayEnvelope;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NullReturnVals", + "description": "replaced return value with null for dev/tramai/engine/approval/ApprovalSuspensionCoordinator::persistSuspendedInvocation", + "sourceLine": 284, + "pitBlock": 9, + "pitIndex": 38, + "bytecodePc": 54, + "instruction": "areturn of kotlin/Unit.INSTANCE (preceded by getstatic kotlin/Unit.INSTANCE)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 4, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "B (returned value semantically discarded)", + "proofUsed": "caller-discard: every call site's resumed path begins with an explicit pop (compensateStep pc252, persistSuspendedInvocation pc1231, persistUngoverned pc880, persistGoverned pc1049, requireExistingAttributionMatches pc699), so the returned Unit value is never observed; replacing it with null cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table", + "bytecodePcVerifiedBy": "javap at the census base: persistSuspendedInvocation pc54 is areturn, line-table entry 284, preceded by getstatic kotlin/Unit.INSTANCE", + "callSiteDiscardEvidence": "supplementary: the resumed path of every call site pops the result" + }, + { + "identity": "f4463604928886b61038161dfacada30be5fa9dcd62178ac329b83c4568e3ffd", + "className": "dev.tramai.engine.approval.DefaultApprovalGateway", + "method": "persistUngoverned", + "methodDescription": "(Ldev/tramai/engine/approval/ApprovalGatewayPersistenceRequest;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", + "mutator": "NullReturnVals", + "description": "replaced return value with null for dev/tramai/engine/approval/DefaultApprovalGateway::persistUngoverned", + "sourceLine": 234, + "pitBlock": 29, + "pitIndex": 161, + "bytecodePc": 267, + "instruction": "areturn of kotlin/Unit.INSTANCE (preceded by getstatic kotlin/Unit.INSTANCE)", + "g5aOriginalMapping": "EXACT", + "resolvedExactMapping": "EXACT", + "controlStatus": "SURVIVED", + "controlTestsRun": 7, + "discoveryStatus": "SURVIVED", + "semanticHypothesis": "B (returned value semantically discarded)", + "proofUsed": "caller-discard: every call site's resumed path begins with an explicit pop (compensateStep pc252, persistSuspendedInvocation pc1231, persistUngoverned pc880, persistGoverned pc1049, requireExistingAttributionMatches pc699), so the returned Unit value is never observed; replacing it with null cannot change behaviour.", + "killingTest": null, + "finalStatus": "SURVIVED (structurally equivalent)", + "finalDisposition": "EQUIVALENT", + "evidenceProvenance": "control campaign 78458d1e @ 42d9d948; candidate campaign 7acbd868 (child of 78458d1e + test d3837a58); javap at the census base with the SMAP-resolved line table", + "bytecodePcVerifiedBy": "javap at the census base: persistUngoverned pc267 is areturn, line-table entry 234, preceded by getstatic kotlin/Unit.INSTANCE", + "callSiteDiscardEvidence": "supplementary: the resumed path of every call site pops the result" + } + ], + "corrections": [ + { + "field": "reconciliation.movements[0].identity and the six NullReturnVals bytecodePc values", + "supersededClaim": "movement identity recorded as the malformed prefix a9760bea3a92f; the six NullReturnVals rows carried bytecodePc=null despite claiming resolvedExactMapping=EXACT", + "correctedClaim": "movement records the full 64-hex canonical identity; the six rows carry the mutated areturn PCs 161, 100, 244, 341, 267, 54, each re-verified by javap at the census base against the line table", + "foundDuring": "owner review of #463 at head 5e275ccc", + "effectOnDispositions": "none \u2014 1 KILLED / 11 EQUIVALENT stands" + } + ] +} \ No newline at end of file diff --git a/docs/roadmap/0.7.0/TASK-0.7.1g1G5c-SURVIVED-ADJUDICATION.md b/docs/roadmap/0.7.0/TASK-0.7.1g1G5c-SURVIVED-ADJUDICATION.md new file mode 100644 index 00000000..e28a993c --- /dev/null +++ b/docs/roadmap/0.7.0/TASK-0.7.1g1G5c-SURVIVED-ADJUDICATION.md @@ -0,0 +1,121 @@ +# TASK-0.7.1g1G5c — SURVIVED mapping closure and semantic adjudication + +Parent: **TASK-0.7.1g1G5**. Scope: the **12 frozen SURVIVED identities**, and nothing else. The four NO_COVERAGE identities are untouched. +Base: **`42d9d9487ccf59ca3f348d0babfe9bbda232ef0f`** — the squash merge of #462. Frozen parent digest `92a9d06d5a8e43c5fb659f9d65bdd2e73d6ed5f45c1a8536ac70781b03503584` unchanged; admission ledger still `admissions: []`. + +**Endpoint: G5C SURVIVED COHORT CLOSED — G5 REMAINDER = EXACTLY 4 NO_COVERAGE.** + +## Final accounting + +| | | +| --- | --- | +| input SURVIVED | 12 | +| KILLED | **1** | +| EQUIVALENT | **11** | +| UNREACHABLE | 0 | +| TOOLING_LIMITATION | 0 | +| UNDETERMINED | **0** | +| mappings EXACT | **12 / 12** | +| mappings AMBIGUOUS | 0 | + +## Phase 1 — the five ambiguous mappings, repaired + +G5a marked five identities AMBIGUOUS because an opcode-shape enumeration cannot recover PIT's block/index coordinates. The authority is the repository's own `MutationIdentity.stableKey()`: `sha256(module ␟ class ␟ method ␟ descriptor ␟ mutator ␟ description ␟ block ␟ index)`, with **line deliberately excluded**. It reproduces **all 68** identities of the P1 candidate-only manifest exactly, so the canonical identity itself carries PIT's coordinates — and matching PIT's emitted rows against the frozen identity resolves each ambiguous identity uniquely: + +| identity | resolved line | PIT block/index | instruction | +| --- | --- | --- | --- | +| `1ce5967ee60b` | 108 | 91 / 641 | `pc1100 invokestatic CancellationKt.rethrowIfCancellation` | +| `c7774df8ccdc` | 305 | 16 / 101 | `pc155 invokestatic CancellationKt.rethrowIfCancellation` | +| `3658ba45eaed` | 160 | 31 / 168 | `pc287 invokestatic Intrinsics.checkNotNull` | +| `8e18b4b48968` | 174 | 22 / 127 | `pc189 ifeq` after `instanceof Collection` | +| `d38ac52f2843` | 174 | 24 / 131 | same fast-path test, second inlined stratum copy | + +The G5a AMBIGUOUS determinations are recorded as **superseded, not erased** (see `g5aOriginalMapping` per row). + +**The SMAP matters here.** `ApprovalRunAttributionKt`'s line table has entries at 171–180 while the checked-in `ApprovalRunAttribution.kt` ends at line 169. The `SourceDebugExtension` map explains it: output line 174 comes from stratum **2 = `_Collections.kt` line 1807** (inlined stdlib), and the `KotlinDebug` stratum maps it back to **our line 104** — `expected.any { metadata.getValue(it).isBlank() }`. Treating "174" as a physical line in our file would have been wrong. + +## Phase 2 — control measurement at the new base + +Family-only narrowing (53 deletions, `approval` block byte-identical), throwaway `78458d1eecce60c7f8f307b4564fb47afda0da79`, BUILD SUCCESSFUL in 1000 s, `measuredCommit` equal to the throwaway commit, analyzer semantics unchanged. Census: **918** mutants — engine 492, security 426; KILLED 661, SURVIVED 133, NO_COVERAGE 62, TIMED_OUT 62. + +Recovery: **12 input · 12 recovered · 0 missing · 0 duplicates**, every control status taken from this post-merge tip. All twelve remained SURVIVED (matrix `SURVIVED → SURVIVED` ×12, **status movement 0**), so no identity was incidentally killed by merged tests, and no identity was killed merely to raise a number. + +## Phase 3 — diagnosis + +All twelve had real `numberOfTestsRun` at control (2–95), so E (tooling limitation) was excluded from the outset and no identity was diagnosed from mutator or status similarity. + +| identity | owner | mutator | line | PIT | hypothesis | disposition | +| --- | --- | --- | --- | --- | --- | --- | +| `1ce5967ee60b` | ApprovalResumeCoordinator.resume | VoidMethodCall | 108 | b91/i641 | structural | **EQUIVALENT** | +| `3658ba45eaed` | ApprovalSuspensionCoordinator.suspendToolExecution | VoidMethodCall | 160 | b31/i168 | structural | **EQUIVALENT** | +| `3a5c5213b226` | ApprovalSuspensionCoordinator.compensateStep | NullReturnVals | 307 | b17/i106 | B | **EQUIVALENT** | +| `56d3366434a6` | DefaultApprovalGateway.requireExistingAttributionMatches | NullReturnVals | 205 | b9/i50 | B | **EQUIVALENT** | +| `6a1ce2f46d2a` | DefaultApprovalGateway.requireExistingAttributionMatches | NullReturnVals | 219 | b26/i141 | B | **EQUIVALENT** | +| `8e18b4b48968` | ApprovalRunAttributionKt.decodeApprovalAttribution | NegateConditionals | 174 | b22/i127 | structural | **EQUIVALENT** | +| `a542bbe6db3f` | DefaultApprovalGateway.persistGoverned | NullReturnVals | 265 | b35/i204 | B | **EQUIVALENT** | +| `a9760bea3a92` | ApprovalResumeCoordinator.resume | NegateConditionals | 102 | b76/i533 | A | **KILLED** | +| `c7774df8ccdc` | ApprovalSuspensionCoordinator.compensateStep | VoidMethodCall | 305 | b16/i101 | structural | **EQUIVALENT** | +| `d38ac52f2843` | ApprovalRunAttributionKt.decodeApprovalAttribution | NegateConditionals | 174 | b24/i131 | structural | **EQUIVALENT** | +| `eb21aaed3192` | ApprovalSuspensionCoordinator.persistSuspendedInvocation | NullReturnVals | 284 | b9/i38 | B | **EQUIVALENT** | +| `f44636049288` | DefaultApprovalGateway.persistUngoverned | NullReturnVals | 234 | b29/i161 | B | **EQUIVALENT** | + +## Phase 4 — the one real semantic kill + +`a9760bea3a92` is the `ifnonnull` in `"structured-parse-failed: ${e::class.simpleName ?: "unknown"}"`. The production path already exercised it, but the assertions read `startsWith("structured-parse-failed")` — satisfied both by the authoritative diagnostic and by the mutant's `unknown`. The discriminator was therefore missing, not the coverage. + +The fix is one assertion contract, tightened in place (2 lines, `d3837a58`): `isEqualTo("structured-parse-failed: StructuredOutputException")`. It is about the diagnostic contract, not about PIT: it triggers a real `StructuredOutputException` through the resume path, captures the authoritative uncertain-outcome audit, and pins the class-name component. + +Measurement: candidate `7acbd868adb94658e90ef55d3261d315f6eddda9` — a child of the control throwaway, differing by the test commit alone — BUILD SUCCESSFUL in 923 s. + +| requirement | result | +| --- | --- | +| `a9760bea3a92` | **SURVIVED → KILLED**, killing test named below | +| KILLED regressions | **0** | +| identity loss / gain | 0 / 0 | +| new unexplained timeouts | 0 | +| shared identities | 918 / 918 | + +Killing test: `ApprovalResumeSuspensionContractTest` › *a cancellation resumed into the structured-parse uncertain path is not replaced by the primary failure*, at `resume` line 102. + +## Phase 5 — the eleven equivalence proofs + +**Caller-discard (6 rows, NullReturnVals).** The mutated instruction is the function's `areturn`, immediately preceded by `getstatic kotlin/Unit.INSTANCE` — verified at pc161 (`compensateStep`, line 307), pc100 and pc244 (`requireExistingAttributionMatches`, lines 205 and 219), pc341 (`persistGoverned`, line 265), pc267 (`persistUngoverned`, line 234), pc54 (`persistSuspendedInvocation`, line 284). The mutant returns null where Unit was returned; every call site pops it. The resumed path after each suspend call begins with an explicit `pop`: `compensateStep` pc252, `persistSuspendedInvocation` pc1231, `persistUngoverned` pc880, `persistGoverned` pc1049, `requireExistingAttributionMatches` pc699. The value is never observed, so returning null is unobservable. + +**Catch-handler dominance (2 rows, VoidMethodCall).** `resume` registers `CancellationException → 1088` and `Exception → 1093`, and the mutated `pc1100` executes inside the **1093 (Exception)** handler. `compensateStep` registers `CancellationException → 147` / `Exception → 150`, with `pc155` inside the **150** handler. JVM dispatch order gives the earlier, more specific handler the cancellation, so the helper's only branch (`if (this is CancellationException) throw this`) cannot fire. Removing the call cannot change behaviour. + +**Compiler guard passed on (1 row, VoidMethodCall).** `pc285 aload 7 → pc287 Intrinsics.checkNotNull → pc290 aload 7 → pc293 invokespecial CreateApprovalChallenge(...)`. The guard protects a value that is immediately passed as a non-null Kotlin constructor parameter, whose own parameter check raises the same NPE one instruction later. Same exception type, same observable outcome on any valid path. + +**Successor convergence (2 rows, NegateConditionals).** The mutated conditional is the inlined `any` fast path — `pc186 instanceof Collection; pc189 ifeq` — whose negation routes Collection receivers onto the plain-Iterable loop. Empty input returns `false` on both paths, and non-empty input runs the identical loop, so no observable difference exists. + +Every proof is instruction-level or control-flow-level on this identity. Neighbouring G4 results were used as context only, never as a substitute. + +## Phase 8 — parent accounting + +| | | +| --- | --- | +| G5 parent | 52 | +| G5b TOOLING_LIMITATION | −36 | +| G5c input SURVIVED | 12 | +| G5c KILLED | 1 | +| G5c EQUIVALENT | 11 | +| G5c unresolved | 0 | +| remaining for G5d | **4 — exactly the frozen NO_COVERAGE identities** | + +No widening. P1 mint remains blocked; P2 consumption remains blocked; the final full canonical campaign still runs only after G5d closes. + +## Prohibited changes + +No change to `mutation-baseline.json`, the population-admission ledger, the classification or enrollment ledgers, the mutation-evolution ledger, the mutator set, timeout policy, target-family semantics, mutation ceilings, or verifier/admission semantics. **No production code changed** — no production defect was discovered. The temporary measurement narrowing is evidence machinery only and was never committed to the Epic. + +## Verification + +- frozen parent digest unchanged; admission ledger still empty +- control and candidate campaigns: same base, same narrowing, same analyzer, `measuredCommit` equal to their throwaway commits, 918/918 identities shared +- 12/12 recovered; 12/12 mappings EXACT; 0 UNDETERMINED +- exactly one status movement, 0 KILLED regressions, 0 identity loss/gain, 0 new timeouts +- focused test class green; `spotlessCheck`, `verifyStaticAnalysis`, `verifyChangePolicy -PchangePolicyBase=42d9d9487ccf…` +- exact-head CI is the final authority + +## Limits + +EQUIVALENT here means: on every reachable path, the mutant and the original cannot be distinguished by any observable behaviour — proven at instruction/control-flow level. It does not claim the instruction is unreachable, and it does not claim PIT's TIMED_OUT machinery was involved (no identity in this cohort was a tooling limitation). The four NO_COVERAGE identities are untouched and are G5d's subject. diff --git a/tramai-engine/src/test/kotlin/dev/tramai/engine/approval/ApprovalResumeSuspensionContractTest.kt b/tramai-engine/src/test/kotlin/dev/tramai/engine/approval/ApprovalResumeSuspensionContractTest.kt index 8a185e80..140b4183 100644 --- a/tramai-engine/src/test/kotlin/dev/tramai/engine/approval/ApprovalResumeSuspensionContractTest.kt +++ b/tramai-engine/src/test/kotlin/dev/tramai/engine/approval/ApprovalResumeSuspensionContractTest.kt @@ -526,7 +526,7 @@ class ApprovalResumeSuspensionContractTest { assertReachesCaller(thrown, failure) assertThat(audit.uncertainReasons).hasSize(1) - assertThat(audit.uncertainReasons.single()).startsWith("structured-parse-failed") + assertThat(audit.uncertainReasons.single()).isEqualTo("structured-parse-failed: StructuredOutputException") assertThat(audit.uncertainResumes).isEqualTo(1) } @@ -631,7 +631,7 @@ class ApprovalResumeSuspensionContractTest { assertThat(thrown).isInstanceOf(CancellationException::class.java) assertReachesCaller(thrown, cancellation) assertThat(audit.uncertainResumes).isEqualTo(1) - assertThat(audit.uncertainReasons.single()).startsWith("structured-parse-failed") + assertThat(audit.uncertainReasons.single()).isEqualTo("structured-parse-failed: StructuredOutputException") } @Test