Sub-scope 07 (mongo) schema + migration - manifest and negative-space probes revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5 generatedAt=2026-08-30T00:04:41+00:00 cwd=/shared/codebase/clean-architecture-backend-template/src === OWNED FILES === 0466ca15110d4251cef0759b3c1383b73ee59232 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java 187 f9170f8288edeab3487c7918852a78f2a6137f65 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLock.java 180 a3a6d5ecf2ecbd4d05b17e5bf2b03118d60feb8c main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigration.java 30 769ec5d10816627ba8e887f2f8de8d80826c61c5 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationCheckpoint.java 44 4682c0cd8dda0b15365ce585b41c65f4067d9125 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationChecksum.java 30 80296f59836963b7e108804211efec12a42ef373 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationContext.java 81 47af881f0b4ef203e17c26baff47cd92bd23661f main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationHeartbeat.java 32 a1f751aefed77a211c0990095170257e9fb54f81 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationId.java 33 65a2e6cab8f1582372f07b2b042cd38685dde18c main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLedger.java 54 ca21debac1dd694789d075b0d32508302272145e main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLock.java 49 5db2ea1cddf4d82df9b0d81893a5487d4c1b53ea main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationPostcondition.java 21 83f020725259fc9c634d69e2f6c91ccae34b6551 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationPrecondition.java 20 5718f2152bd32c4aa22fb2edbbbd7941903aa0ac main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationResult.java 58 b79d234a93e4e9f7755bbfa266f93de6da464f72 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationRunner.java 161 8de0ca45f1d4317726a7b9271810da2e969b4ab5 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockChangeUnitView.java 29 113c564352c7f7739c1fdc4d731c8cff51d6dcd6 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLedgerAdapter.java 77 34f128ba5191d0ed026da420e6652cb7699089ee main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLockAdapter.java 72 40127b9342e69c7d7f0f0ad13c690144038bee63 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockMigrationConfiguration.java 40 d9bd3c5db0a9dd35cc4a8412cd7c45135ff9fe96 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockMongoMigrationAdapter.java 33 b61b21deb96608b84e28577476043652325e90a7 main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexApplyPolicy.java 49 3070de52719edfb8b0a22c82feef8557a310de3e main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDescriptorView.java 57 3a2396275b191162a19c8073a9cb77795374d826 main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiff.java 55 dece468ed334df72d050e849a5982443fc0c8efe main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngine.java 79 4d8e976dac5191cec7a3024bb4d384bdbc988b72 main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexRetirementPlan.java 60 be34f2f9c81f314bf7433217da7f4000f0b7238f main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexRetirementState.java 52 f35defc38fa8521c97413c658cffe9d9b6d75e7f main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoCollectionManifest.java 107 6e00339723a4bc7d58a00ff90aa39e844b5af38b main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexDirection.java 42 20ba2c85f7b762c226c1dc067e998694f146e3b6 main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexKey.java 20 3a1533d193354efb63ab7d6e12f57152671dfdde main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java 243 6ffafda63defa8aa39638518e729030dd44680df main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java 87 e5000ec35e83b27805e4d3b51ba6c288f2501109 main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoMetadataOwnership.java 30 6c07172745f15eabc446b1a16f119dfa148ff814 main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoSchemaManifest.java 79 0fde15eab0dc7e0382f9b13afe40296b0c7c9f21 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/EmbeddedCollectionDescriptor.java 60 b4eb96dda1f12b4b90504998cd08202c4101b631 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoBinaryFieldDescriptor.java 32 cc6e4463f03567a521a5c36d8c88e1e5375a87f6 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentModelManifest.java 156 e186785b79e639218b1157b75d48e8de93f98f76 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentModelValidator.java 88 8c91b34f914b3e8c3e2708ba4855d86cf99505f1 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentSizeBudget.java 47 cf1dd7adbdc171ab8b2cadb190ef74739029017e main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoReferenceDescriptor.java 39 6c589c888ef8f9f7427ff00b3dafd643c28afea7 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoReferenceLifecycle.java 20 786b5e19d229fc84d2aba1c02a91b5e2615fa633 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoExpirationAccessPolicy.java 38 2bee1ae545855880eb2165d2551ef57902722b63 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlIndexDescriptor.java 38 d554d62f7ff110406764d95352bc623dba491bf2 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicy.java 55 5bf375305d37971eaf43ff3e13d49efab6613138 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java 94 9bac323540839409b6b14bac4b649afe39968277 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidationAction.java 26 8d305f8383dd15bf5245f27a1215c4f57d0be522 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidationLevel.java 21 7da0133d082e8a1f5d9eca37d0de84fe39fc2a37 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java 102 56cfc5a4826661d1cdcbe669b171cdd99d8f42c9 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorDescriptor.java 36 03b2f002c9c6dccc0fd91910c7a0b79ea1a13b60 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorDiff.java 31 34ac7d7480bdd093f00188d1a63753f79f1839b7 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorDiffEngine.java 50 5dcc5125ed688ba9a84e5950279d766540fe9bae test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationFencingTest.java 238 8de780e56da2c069cf8ee25f34dc4ecac9d5e525 test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java 357 be3f250a150cf77231ef1c63bc8a6ebc0a7b4d5f test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationRunnerTest.java 161 8aab71567e0f5df28a12445d2ef5cc73ff9bdff5 test/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockMongoMigrationAdapterTest.java 118 45b9b78653d63943d63432e55ab835b56135d4b4 test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java 112 e25a040a6f5fa9960e64b8eb484fdf04237cd2a1 test/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistryTest.java 110 095780bb7e5f55eb5a153c69920d6f6d17bf7024 test/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentModelValidatorTest.java 95 ee2cd514b9cb28705cd83a09d1e11810fee6171a test/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidatorTest.java 61 4282df2da997e11d1783655d3915970ffa24bcee test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java 74 production: 49 test: 9 production LOC: 3124 total === 8.1 reachability: what the auto-configuration builds from schema/migration === $ grep -n 'schema\.\|migration\.\|Manifest\|Migration\|IndexDiff\|Validator' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java 4:import dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoTypeRepresentationManifest; 315: public MongoTypeRepresentationManifest mongoPlatformTypeRepresentationManifest() { 316: return MongoTypeRepresentationManifest.standard(); 339: ObjectProvider 354: new MongoStartupValidator( exit=0 $ git grep -ln 'schema.manifest\|schema.index\|schema.model\|schema.ttl\|schema.validation' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/schema/' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/encryption/qe/MongoEncryptionMetadataOwnership.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/search/MongoSearchIndexDescriptor.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/shared/MongoTenantManifestValidator.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoSchemaValidationException.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java exit=0 $ git grep -ln 'mongo.migration' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/migration/' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCheckpointStore.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java exit=0 === 8.1b who consults the apply policies === $ git grep -n 'MongoIndexApplyPolicy\|requireRuntimeApplyAllowed' -- 'adapter/outbound/persistence-mongo/src' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexApplyPolicy.java:14:public enum MongoIndexApplyPolicy { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexApplyPolicy.java:38: public void requireRuntimeApplyAllowed(String collection) { adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:77: assertThat(MongoIndexApplyPolicy.REPORT_ONLY.runtimeMayApply()).isFalse(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:78: assertThatThrownBy(() -> MongoIndexApplyPolicy.REPORT_ONLY.requireRuntimeApplyAllowed("orders")) adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:81: assertThat(MongoIndexApplyPolicy.APPLY.runtimeMayApply()).isTrue(); exit=0 $ git grep -n 'MongoValidatorApplyPolicy' -- 'adapter/outbound/persistence-mongo/src' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:23:public final class MongoValidatorApplyPolicy { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:37: private MongoValidatorApplyPolicy(String serverVersion) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:42: public static MongoValidatorApplyPolicy forServer(String serverVersion) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:43: return new MongoValidatorApplyPolicy(Objects.requireNonNull(serverVersion, "serverVersion")); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:6:import dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidatorApplyPolicy; adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:24: assertThat(MongoValidatorApplyPolicy.forServer("8.0.4").certifiedLane()) adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:27: assertThat(MongoValidatorApplyPolicy.forServer("7.0.28").certifiedLane()).isTrue(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:33: assertThat(MongoValidatorApplyPolicy.forServer("6.0.19").certifiedLane()).isFalse(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:34: assertThat(MongoValidatorApplyPolicy.forServer("8.1.0").certifiedLane()).isFalse(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:40: assertThat(MongoValidatorApplyPolicy.forServer("unknown").certifiedLane()) adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:14:class MongoValidatorApplyPolicyTest { adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:18: MongoValidatorApplyPolicy policy = MongoValidatorApplyPolicy.forServer("8.0"); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:28: MongoValidatorApplyPolicy.forServer("7.0") adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:35: MongoValidatorApplyPolicy policy = MongoValidatorApplyPolicy.forServer("8.0"); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:44: assertThat(MongoValidatorApplyPolicy.forServer("8.0").runtimeMayApply()).isFalse(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:55: MongoValidatorApplyPolicy.forServer("8.0") exit=0 $ git grep -n 'new MongoIndexDiffEngine\|new MongoValidatorDiffEngine' -- 'adapter/outbound/persistence-mongo/src' adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:18: private final MongoIndexDiffEngine engine = new MongoIndexDiffEngine(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:64: new MongoValidatorDiffEngine() exit=0 $ git grep -n 'MongoManifestRegistry' -- 'adapter/outbound/persistence-mongo/src/main' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java:10:import dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoManifestRegistry; adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java:29: private final MongoManifestRegistry manifests; adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java:32: DefaultMongoImperativeExecutor executor, MongoManifestRegistry manifests) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoCollectionManifest.java:93: *

Duplicates are accepted here and rejected by {@link MongoManifestRegistry}: a builder that adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:21:public final class MongoManifestRegistry { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:25: private MongoManifestRegistry(Map byCollection) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:30: public static MongoManifestRegistry of(MongoCollectionManifest... manifests) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:35: public static MongoManifestRegistry of(Collection manifests) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:48: return new MongoManifestRegistry(Map.copyOf(byCollection)); exit=0 === 8.2 recordApplied: the interface contract vs the two implementations === $ grep -n 'only if the fence is still the current one' -A 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLedger.java 19: * Records a completed migration, only if the fence is still the current one. 20- * 21- *

Conditioned on the fence because the runner that wrote the batches may no longer be the 22- * runner that owns the lease. A ledger entry from a superseded runner says a migration completed 23- * when the work it describes was overwritten by the runner that replaced it. 24- * 25- * @param fence the acquisition token from {@link MongoMigrationLock#fence()} 26- * @throws dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException when a 27- * newer acquisition exists -- 40: * Stores a checkpoint after a completed batch, only if the fence is still the current one. 41- * 42- * @param fence the acquisition token from {@link MongoMigrationLock#fence()} 43- * @throws dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException when a 44- * newer acquisition exists 45- */ 46- void saveCheckpoint(MongoMigrationCheckpoint checkpoint, long fence); 47- 48- /** One applied migration. */ exit=0 $ grep -n 'public void recordApplied' -A 22 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java 83: public void recordApplied( 84- MongoMigrationId migrationId, 85- MongoMigrationChecksum checksum, 86- String operator, 87- Instant appliedAt, 88- long fence) { 89- Objects.requireNonNull(migrationId, "migrationId"); 90- Objects.requireNonNull(checksum, "checksum"); 91- Objects.requireNonNull(operator, "operator"); 92- Objects.requireNonNull(appliedAt, "appliedAt"); 93- requireCurrentFence(fence, "ledger entry for " + migrationId.value()); 94- ledger.insertOne( 95- new Document(MIGRATION_ID, migrationId.value()) 96- .append("checksum", checksum.value()) 97- .append("operator", operator) 98- .append("appliedAt", Date.from(appliedAt)) 99- .append("fence", fence)); 100- } 101- 102- @Override 103- public Optional findCheckpoint(MongoMigrationId migrationId) { 104- Objects.requireNonNull(migrationId, "migrationId"); 105- Document found = checkpoints.find(Filters.eq(MIGRATION_ID, migrationId.value())).first(); exit=0 $ grep -n 'public void recordApplied' -A 12 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLedgerAdapter.java 56: public void recordApplied( 57- MongoMigrationId migrationId, 58- MongoMigrationChecksum checksum, 59- String operator, 60- Instant appliedAt, 61- long fence) { 62- recorded.put( 63- Objects.requireNonNull(migrationId, "migrationId"), 64- new AppliedMigration(migrationId, checksum, operator, appliedAt)); 65- } 66- 67- @Override 68- public Optional findCheckpoint(MongoMigrationId migrationId) { exit=0 $ grep -n 'requireCurrentFence' -A 12 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java 93: requireCurrentFence(fence, "ledger entry for " + migrationId.value()); 94- ledger.insertOne( 95- new Document(MIGRATION_ID, migrationId.value()) 96- .append("checksum", checksum.value()) 97- .append("operator", operator) 98- .append("appliedAt", Date.from(appliedAt)) 99- .append("fence", fence)); 100- } 101- 102- @Override 103- public Optional findCheckpoint(MongoMigrationId migrationId) { 104- Objects.requireNonNull(migrationId, "migrationId"); 105- Document found = checkpoints.find(Filters.eq(MIGRATION_ID, migrationId.value())).first(); -- 120: requireCurrentFence(fence, "checkpoint for " + checkpoint.migrationId().value()); 121- // Conditioned on the stored fence as well as matched by id: a superseded runner that wakes up 122- // mid-batch must not move the checkpoint of the runner that replaced it, which would send the 123- // live migration back over rows it has already done or forward past rows it has not. 124- Document document = 125- new Document(MIGRATION_ID, checkpoint.migrationId().value()) 126- .append("resumePosition", checkpoint.resumePosition()) 127- .append("processedCount", checkpoint.processedCount()) 128- .append("updatedAt", Date.from(checkpoint.updatedAt())) 129- .append("fence", fence); 130- 131- // Replace-then-insert rather than one upsert, and the two cases this separates are the whole 132- // behaviour of the fence. -- 177: private static void requireCurrentFence(long fence, String what) { 178- if (fence == MongoMigrationLock.UNFENCED) { 179- throw MongoOperationRejectedException.of( 180- "migration.fence", 181- "refusing to write the " 182- + what 183- + " under an unfenced lease; a lease with no acquisition token cannot show that this" 184- + " runner is still the one migrating"); 185- } 186- } 187-} exit=0 === 8.2b the diff's comparable field set vs what the manifest declares === $ grep -n 'public record MongoIndexManifest' -A 16 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java 21:public record MongoIndexManifest( 22- String name, 23- List keys, 24- boolean unique, 25- boolean sparse, 26- boolean hidden, 27- boolean deprecated, 28- String partialFilterExpression, 29- String collationProfile, 30- Duration expireAfter, 31- String wildcardProjection, 32- boolean shardKeySupport, 33- Set expectedUsage, 34- String owner, 35- MongoMetadataOwnership metadataOwnership) { 36- 37- public MongoIndexManifest { exit=0 $ grep -n 'public record MongoIndexDescriptorView' -A 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDescriptorView.java 14:public record MongoIndexDescriptorView( 15- String collection, 16- String name, 17- String keySignature, 18- boolean unique, 19- boolean hidden, 20- MongoMetadataOwnership metadataOwnership) { 21- 22- /** MongoDB's own index on {@code _id}. */ exit=0 $ grep -n 'keySignature().equals\|unique() !=\|expireAfter\|sparse\|partialFilter\|collation\|wildcard' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngine.java 49: if (!actual.keySignature().equals(declared.keySignature()) 50: || actual.unique() != declared.unique()) { exit=0 === 8.2c the diff has no case for an index hidden on the server but declared visible === $ grep -n 'hidden' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngine.java 55: if (declared.hidden() && !actual.hidden()) { exit=0 === 8.3 TTL declared two ways: manifest expireAfter vs MongoTtlPolicy === $ grep -n 'expireAfter' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java 30: Duration expireAfter, 54: if (expireAfter != null && expireAfter.isNegative()) { 73: return Optional.ofNullable(expireAfter); 78: return expireAfter != null; 114: private Duration expireAfter; 189: public Builder expireAfter(Duration retention) { 190: this.expireAfter = Objects.requireNonNull(retention, "retention"); 235: expireAfter, exit=0 $ git grep -n 'MongoTtlPolicyValidator\|MongoTtlIndexDescriptor\|MongoTtlPolicy\b' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/schema/ttl/' exit=1 $ git grep -n 'isTtlIndex\|\.ttl()' -- 'adapter/outbound/persistence-mongo/src' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java:77: public boolean isTtlIndex() { exit=0 $ git grep -n 'MINIMUM_SAFE_RETENTION' -- 'adapter/outbound/persistence-mongo/src' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java:21: public static final Duration MINIMUM_SAFE_RETENTION = Duration.ofMinutes(1); adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java:49: if (!policy.retention().isZero() && policy.retention().compareTo(MINIMUM_SAFE_RETENTION) < 0) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java:55: + MINIMUM_SAFE_RETENTION exit=0 === 8.3b two ledgers, two locks, and what the unfenced adapter can actually run === $ git grep -n 'implements MongoMigrationLedger\|implements MongoMigrationLock' -- 'adapter/outbound/persistence-mongo/src/main' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java:29:public final class MongoCollectionMigrationLedger implements MongoMigrationLedger { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLock.java:28:public final class MongoCollectionMigrationLock implements MongoMigrationLock { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLedgerAdapter.java:24:public final class FlamingockLedgerAdapter implements MongoMigrationLedger { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLockAdapter.java:17:public final class FlamingockLockAdapter implements MongoMigrationLock { exit=0 $ grep -n 'refuses resumable migrations under an unfenced lease' -B 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLockAdapter.java 40- return held.get(); 41- } 42- 43- /** 44- * Unfenced: the engine's lock exposes no monotonic token. 45- * 46- *

Reported honestly rather than synthesised from a local counter, which would look like 47- * fencing and protect nothing — a per-process counter says nothing about what another process 48: * acquired. The runner refuses resumable migrations under an unfenced lease for exactly this exit=0 $ grep -n 'UNFENCED' -A 10 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationRunner.java 82: if (lock.fence() == MongoMigrationLock.UNFENCED) { 83- // An unfenced lease cannot tell a live runner from a stalled one that is about to wake up. 84- // Refused rather than downgraded: the alternative is a certification that says "one runner at 85- // a time" and a mechanism that cannot show it. 86- throw MongoOperationRejectedException.of( 87- "migration.fence", 88- "this migration lease exposes no fencing token, so a stalled runner cannot be excluded;" 89- + " use a fenced lease or run the migration inside a maintenance window with the" 90- + " application stopped"); 91- } 92- exit=0 === 8.3c ensureIndexes: who calls it === $ git grep -n 'ensureIndexes' -- 'adapter/outbound/persistence-mongo/src' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java:62: public void ensureIndexes() { adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:78: ledger.ensureIndexes(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:129: ledger.ensureIndexes(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:152: writer.ensureIndexes(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:178: ledger.ensureIndexes(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:206: ledger.ensureIndexes(); adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:225: ledger.ensureIndexes(); exit=0 === 8.4 documentation and lane drift === $ grep -rn 'mongoMigrationTest\|mongodb-migration' adapter/outbound/persistence-mongo/build.gradle 97: 'mongodb-migration', 119: lane('mongoMigrationTest') { 120: tag = 'mongodb-migration' exit=0 $ grep -rn 'mongodb-migration\|mongoMigrationTest' ../docs ../README.md 2>/dev/null | head -30 ../docs/reviews/2026-08-14-mongodb-module-code-review.md:1127:./gradlew :adapter:outbound:persistence-mongo:mongoMigrationTest --no-daemon --console=plain ../docs/superpowers/plans/2026-08-15-wave6-final-qualification.md:88: :adapter:outbound:persistence-mongo:mongoMigrationTest \ ../docs/superpowers/plans/evidence/2026-08-15-wave0-baseline.md:163:registered: [mongoReplicaSetTest, mongoFailoverTest, mongoMigrationTest, mongoCompatibilityTest, ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-b5-ghost-release-lanes.md:57:`mongoStableContractTest`, `mongoReplicaSetTest`, `mongoFailoverTest`, `mongoMigrationTest`, ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes.log:22:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:23:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:30:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest FAILED ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:35:Execution failed for task ':adapter:outbound:persistence-mongo:mongoMigrationTest'. ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:36:> There were failing tests. See the report at: file:///home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template/src/adapter/outbound/persistence-mongo/build/reports/tests/mongoMigrationTest/index.html ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-migration.log:1:$ ./gradlew :adapter:outbound:persistence-mongo:mongoMigrationTest --console=plain ../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-migration.log:22:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest ../docs/superpowers/plans/2026-08-15-wave2-module-on-path.md:594: :adapter:outbound:persistence-mongo:mongoMigrationTest \ ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:58:│ ├── mongodb-migration-core/ ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:59:│ ├── mongodb-migration-flamingock/ ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:109:mongodb-migration-core ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:113:mongodb-migration-flamingock ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:114: → mongodb-migration-core ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:139:- Create: `modules/mongodb/mongodb-migration-core/build.gradle.kts` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:140:- Create: `modules/mongodb/mongodb-migration-flamingock/build.gradle.kts` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:195: "mongodb-geospatial", "mongodb-migration-core", ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:196: "mongodb-migration-flamingock", "mongodb-observability", ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:221:git add 'build-logic/src/main/kotlin/mongodb-library-conventions.gradle.kts' 'modules/mongodb/mongodb-core-api/build.gradle.kts' 'modules/mongodb/mongodb-spring-data/build.gradle.kts' 'modules/mongodb/mongodb-imperative/build.gradle.kts' 'modules/mongodb/mongodb-reactive/build.gradle.kts' 'modules/mongodb/mongodb-query/build.gradle.kts' 'modules/mongodb/mongodb-aggregation/build.gradle.kts' 'modules/mongodb/mongodb-transaction/build.gradle.kts' 'modules/mongodb/mongodb-index-schema/build.gradle.kts' 'modules/mongodb/mongodb-change-stream/build.gradle.kts' 'modules/mongodb/mongodb-geospatial/build.gradle.kts' 'modules/mongodb/mongodb-migration-core/build.gradle.kts' 'modules/mongodb/mongodb-migration-flamingock/build.gradle.kts' 'modules/mongodb/mongodb-observability/build.gradle.kts' 'modules/mongodb/mongodb-security/build.gradle.kts' 'modules/mongodb/mongodb-spring-boot-starter/build.gradle.kts' 'modules/mongodb/mongodb-testkit-core/build.gradle.kts' 'modules/mongodb/mongodb-testkit-replicaset/build.gradle.kts' 'modules/mongodb/mongodb-testkit-failover/build.gradle.kts' 'modules/mongodb/mongodb-testkit-migration/build.gradle.kts' 'settings.gradle.kts' 'build-logic/src/test/java/MongoDbModuleBoundaryTest.java' ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1755:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigration.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1756:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationId.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1757:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationChecksum.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1758:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationLedger.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1759:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationCheckpoint.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1760:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunner.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1761:- Test: `modules/mongodb/mongodb-migration-core/src/test/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunnerTest.java` ../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1794:./gradlew :modules:mongodb:mongodb-migration-core:test --tests 'io.backend.skeleton.mongodb.migration.MongoMigrationRunnerTest' exit=0 $ git grep -n 'schema.manifest\|MongoManifestRegistry\|MongoMigrationRunner' -- 'adapter/outbound/persistence-mongo/README.md' 2>/dev/null exit=1 $ ls adapter/outbound/persistence-mongo/src/main/resources 2>/dev/null; find adapter/outbound/persistence-mongo/src -name '*.json' -path '*schema*' | head META-INF exit=0