Sub-scope 02 (mongo) negative-space probes: api surface reachability, invariant siblings, duplicate mechanisms, drift revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5 executedAt=2026-08-29T23:31:37+00:00 cwd=/shared/codebase/clean-architecture-backend-template/src observation boundary: tracked sources at this revision. api/** is the intended external surface, so a zero in-repository reference is not evidence of dead code. === 8.1 api types referenced from outside the owning leaf === $ git grep -l 'outbound\.mongo\.api\.' -- . ':!adapter/outbound/persistence-mongo/**' exit=1 === 8.1b framework-free rule: any Spring/driver/BSON/Reactor import under api/** === $ git grep -n 'import org\.springframework\|import com\.mongodb\|import org\.bson\|import reactor\.' -- 'adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api' exit=1 === 8.2 invariant siblings: which api records refuse an incoherent construction === $ grep -n 'requireCommitUnknown\|requireTransient' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTransactionCommitUnknownException.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTransactionTransientException.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTransactionCommitUnknownException.java:32: requireCommitUnknown(failureContext)); adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTransactionCommitUnknownException.java:36: private static MongoFailureContext requireCommitUnknown(MongoFailureContext context) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTransactionTransientException.java:30: requireTransient(failureContext)); adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTransactionTransientException.java:33: private static MongoFailureContext requireTransient(MongoFailureContext context) { exit=0 $ sed -n '30,55p' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoFailureContext.java int attempt, Duration elapsed, String traceId) { public MongoFailureContext { Objects.requireNonNull(scope, "scope"); Objects.requireNonNull(operationType, "operationType"); Objects.requireNonNull(consistencyProfile, "consistencyProfile"); Objects.requireNonNull(category, "category"); Objects.requireNonNull(outcome, "outcome"); Objects.requireNonNull(errorLabels, "errorLabels"); Objects.requireNonNull(serverCode, "serverCode"); Objects.requireNonNull(elapsed, "elapsed"); Objects.requireNonNull(traceId, "traceId"); errorLabels = Set.copyOf(errorLabels); if (attempt < 1) { throw new IllegalArgumentException("attempt must be at least 1"); } if (elapsed.isNegative()) { throw new IllegalArgumentException("elapsed must not be negative"); } } /** * The commit-unknown context of design §14.3. * exit=0 $ grep -n 'isAmbiguous\|forbidsBlindReplay' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoExecutionOutcome.java 45: public boolean isAmbiguous() { 50: public boolean forbidsBlindReplay() { 51: return isAmbiguous() || this == PARTIAL_BULK_WRITE; exit=0 === 8.2b the no-cause rule: the root's claim, the one constructor that breaks it, its call site, and the test === $ grep -n 'no constructor accepts' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoPersistenceException.java 11: * query parameter into an exception message. Second, no constructor accepts a {@link Throwable} exit=0 $ grep -n 'initCause' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTimeoutException.java 27: initCause(cause); exit=0 $ git grep -n 'MongoTimeoutException(' -- 'adapter/outbound/persistence-mongo/src/main' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTimeoutException.java:16: public MongoTimeoutException(MongoFailureContext failureContext) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoTimeoutException.java:25: public MongoTimeoutException(MongoFailureContext failureContext, Throwable cause) { adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/failure/DefaultMongoFailureTranslator.java:104: case TIMEOUT -> new MongoTimeoutException(failureContext); adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/reactive/DefaultReactiveMongoExecutor.java:152: new dev.caskeleton.adapter.outbound.mongo.api.error.MongoTimeoutException( exit=0 $ grep -n 'exceptionsDoNotExposeADriverCause' -A 10 adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoFailureContextTest.java 58: void exceptionsDoNotExposeADriverCause() { 59- MongoPersistenceException exception = 60- new MongoTransactionCommitUnknownException( 61- MongoFailureContext.commitUnknown( 62- new MongoOperationName("order.reserve"), "251", Duration.ZERO), 63- "read the transaction record"); 64- 65- assertThat(exception.getCause()).isNull(); 66- assertThat(exception.category()).isEqualTo(MongoFailureCategory.TRANSACTION_COMMIT_UNKNOWN); 67- } 68-} exit=0 === 8.2c schema-version failure: dedicated category, and what the two construction sites attach === $ grep -n 'SCHEMA_VERSION_UNSUPPORTED' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoFailureCategory.java 68: SCHEMA_VERSION_UNSUPPORTED, exit=0 $ git grep -n 'new MongoDataSchemaUnsupportedException(' -- 'adapter/outbound/persistence-mongo/src/main' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/schema/MongoSchemaVersionPolicy.java:85: return new MongoDataSchemaUnsupportedException( adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/failure/DefaultMongoFailureTranslator.java:111: new MongoDataSchemaUnsupportedException(failureContext, -1, -1, -1); exit=0 $ sed -n '80,90p' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/schema/MongoSchemaVersionPolicy.java } private MongoDataSchemaUnsupportedException unsupported(DocumentSchemaVersion version) { MongoFailureContext context = MongoFailureContext.rejected(new MongoOperationName("schema.version-check")); return new MongoDataSchemaUnsupportedException( context, version.value(), range.minimumSupported().value(), range.current().value()); } } exit=0 $ grep -n 'rejected(' -A 14 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoFailureContext.java | grep -n 'OPERATION_REJECTED\|NOT_SENT' 6:140- MongoFailureCategory.OPERATION_REJECTED, 7:141- MongoExecutionOutcome.NOT_SENT, exit=0 $ sed -n '104,112p' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/failure/DefaultMongoFailureTranslator.java case TIMEOUT -> new MongoTimeoutException(failureContext); case CURSOR -> new MongoCursorException(failureContext); case DOCUMENT_TOO_LARGE -> new MongoDocumentTooLargeException(failureContext, -1L, -1L); case SHARD_ROUTING -> new MongoShardRoutingException(failureContext); case RESUME -> new MongoResumeException(failureContext); case ENCRYPTION -> new MongoEncryptionException(failureContext); case SCHEMA_VERSION_UNSUPPORTED -> new MongoDataSchemaUnsupportedException(failureContext, -1, -1, -1); // Bulk partial failure and local rejection are raised by the layers that own their detail — exit=0 === 8.3 duplicate mechanism: the two profile-name records === $ diff <(sed 's/Database/X/g;s/database/x/g' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/DatabaseProfileName.java) <(sed 's/Collection/X/g;s/collection/x/g' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/CollectionProfileName.java) 6c6 < * Registered logical name of a x profile (design §7.1, §28). --- > * Registered logical name of a x profile (design §7.1, §16.2). 8,12c8,11 < *

A profile name selects a configured connection, credential, consistency default and timeout < * set. It is never the physical x name supplied by a caller: allowing that would turn a < * request value into a routing decision and into a metric tag. The pattern therefore rejects < * slashes, whitespace and generated-identifier shapes, which is what {@code Dynamic collection < * profile} in the design's startup failure list means in practice. --- > *

Every operation resolves its guardrails — field allowlist, operator allowlist, budget, index > * manifest, TTL policy — through this name. Accepting a caller-supplied x string instead > * would defeat all of them at once, so the same dynamic-value rejection as {@link > * DatabaseProfileName} applies here. 18d16 < /** A value that looks like a generated identifier is dynamic input, not a registered profile. */ exit=1 === 8.3b duplicate mechanism: retry semantics carried twice === $ grep -n 'boolean retryable' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoFailureContext.java 26: boolean retryable, exit=0 $ grep -n 'Encoding that as a scope rather than' -B 2 -A 3 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoRetryScope.java 6- *

The design's central retry rule is that {@code TransientTransactionError} and {@code 7- * UnknownTransactionCommitResult} demand opposite responses: the first replays the whole body from 8: * a new session, the second must never replay the body at all. Encoding that as a scope rather than 9- * a {@code retryable} boolean is what stops the two from collapsing into one flag at the call site. 10- */ 11-public enum MongoRetryScope { exit=0 $ grep -n 'retryScope\|bodyReplayAllowed' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/failure/MongoFailureClassification.java 16: MongoFailureCategory category, MongoExecutionOutcome outcome, MongoRetryScope retryScope) { 21: Objects.requireNonNull(retryScope, "retryScope"); 22: if (retryScope == MongoRetryScope.COMMIT_ONLY 75: public boolean bodyReplayAllowed() { 76: return retryScope == MongoRetryScope.WHOLE_OPERATION 77: || retryScope == MongoRetryScope.WHOLE_TRANSACTION; exit=0 $ grep -n 'classification.bodyReplayAllowed()\|classification.ambiguous()' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/failure/DefaultMongoFailureTranslator.java 83: classification.bodyReplayAllowed(), 84: classification.ambiguous(), exit=0 === 8.3c the placeholder profile identity shares the value space of a real one === $ grep -n 'UNSPECIFIED' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/MongoOperationScope.java 18: public static final String UNSPECIFIED = "unspecified"; 37: new DatabaseProfileName(UNSPECIFIED), 38: new CollectionProfileName(UNSPECIFIED)); 43: return !UNSPECIFIED.equals(databaseProfile.value()) 44: && !UNSPECIFIED.equals(collectionProfile.value()); exit=0 $ grep -n 'FORMAT' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/DatabaseProfileName.java 16: private static final Pattern FORMAT = Pattern.compile("[a-z][a-z0-9-]{2,63}"); 23: if (value == null || !FORMAT.matcher(value).matches() || UUID_LIKE.matcher(value).matches()) { exit=0 === 8.4 consistency descriptor: which incoherent pairings the record refuses === $ sed -n '28,42p' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/consistency/MongoConsistencyDescriptor.java Objects.requireNonNull(profile, "profile"); Objects.requireNonNull(readPreference, "readPreference"); Objects.requireNonNull(readConcern, "readConcern"); Objects.requireNonNull(writeConcern, "writeConcern"); Objects.requireNonNull(guarantee, "guarantee"); if (requiresCausalSession && !"majority".equals(readConcern)) { throw new IllegalArgumentException( "a causal session profile requires majority read concern: " + profile); } if (requiresCausalSession && !"majority".equals(writeConcern)) { throw new IllegalArgumentException( "a causal session profile requires majority write concern: " + profile); } } exit=0 $ grep -n 'has not chosen durability' -B 3 -A 1 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/consistency/MongoConsistencyProfile.java 5- * 6- *

These are profiles rather than three independent knobs because read preference, read concern 7- * and write concern only mean something together. A caller that picks {@code majority} write 8: * concern and {@code secondaryPreferred} reads has not chosen durability, it has chosen a bug. 9- * exit=0