# Task 3 — the ten-row activation matrix Every row below names where its evidence is, and nothing is ticked from reasoning about the code. Rows 1–7 and 10 are runtime lanes from one Compose matrix run, `20260819T025416Z-200172`; rows 8 and 9 needed work this wave and are described in full. The per-lane `activation.json` is the application's own answer about which switches are on, not the flags the harness passed in — which is the point of reading it rather than the lane definition. | # | Matrix | Evidence | Resolved switches (from `activation.json`) | | --- | --- | --- | --- | | 1 | five off | `off-local`, `off-dev`, `off-prod` lanes + `FiveAdapterOffInventoryTest` | `local` / `dev` / `prod`, **none on** | | 2 | JPA only | `local-jpa` lane | `local`, `persistence-jpa` | | 3 | Mongo only | `local-mongo` lane | `local`, `persistence-mongo` | | 4 | Messaging only | `local-messaging` lane | `local`, `messaging` | | 5 | Notification + JPA, `INGEST_ONLY` | `local-notification-ingest` + `local-notification-handoff` | `local`, `persistence-jpa`+`notification.platform`, `APP_NOTIFICATION_PLATFORM_MODE: INGEST_ONLY` | | 6 | Notification + JPA, `SERVING` | `local-notification-serving` | same switches, `MODE: SERVING` | | 7 | GraphQL only | `local-graphql` lane | `local`, `graphql` | | 8 | relay on, dependency missing | **this wave** — see below | n/a: the deployment is refused | | 9 | JPA + Mongo | `all-adapters` lane + `PortResolutionContractTest` | `local`, both persistence switches on | | 10 | five on | `all-adapters` lane | `local`, `graphql`,`messaging`,`persistence-jpa`,`persistence-mongo`,`notification.platform` | Row 5's exactly-once claim is not inferred from the lane passing; the smoke client says it: ``` notification-smoke: b0e083cb-06c5-4dde-b454-4c4e26f65edc delivered exactly once and stayed that way ``` ## Row 8 — a defect, found by running the row The plan is explicit that Row 8 must be checked for the *name*, not merely for a failure. Checking it that way found that the name was not what an operator got. `ca-skeleton.outbox.enabled=true` with the JPA switch off is a dependency error this repository names, and `CapabilityDependencyValidator` produces the sentence that names it. But the outbox is also a registered relational consumer, so `DataSourceRequirement` reports that a pool is required, `JpaOffAutoConfigurationImportFilter` therefore keeps Boot's relational auto-configurations in the candidate set, and Hibernate and Flyway were instantiated during refresh — ahead of the `InitializingBean` that carried the check. What actually came out was: ``` Unable to obtain connection from database: Connection to localhost:5433 refused. ``` Both components were right on their own. A pool does have consumers besides JPA, and the outbox does need the JPA switch. The disagreement was only ever visible in **which one spoke first**, and no test could see it: the existing `CapabilityDependencyValidatorTest` calls the validator's static method against a `MockEnvironment`, which proves the rule computes the right sentence and nothing about whether anything runs it in time. **Fix.** The check moved to the environment stage (`CapabilityDependencyEnvironmentValidator`, an `EnvironmentPostProcessor` at `LOWEST_PRECEDENCE` alongside the master-switch and profile validators), where every property source is resolved and nothing has been instantiated. The `InitializingBean` stays: a context built without `spring.factories` — an `ApplicationContextRunner`, a slice test — never reaches the post-processor, and the rule should not be optional there. **Evidence, from the built jar rather than from a test harness** — see [task3-row8-dependency-error.log](task3-row8-dependency-error.log): ``` ### outbox on, JPA off exit code: 1 This deployment enables capabilities whose dependencies are off: - ca-skeleton.outbox.enabled=true needs relational persistence to store rows; set ca-skeleton.persistence-jpa.enabled=true or turn the outbox off. - ca-skeleton.outbox.enabled=true needs somewhere to publish; set app.messaging.enabled=true or turn the outbox off. ### relay on, outbox off exit code: 1 This deployment enables capabilities whose dependencies are off: - ca-skeleton.outbox.relay-enabled=true only starts the scheduler for a capability that is off; set ca-skeleton.outbox.enabled=true or turn the relay off. ``` `DependencyErrorStartupContractTest` pins this at the composition-root level, including a case that boots all-off successfully — without it, every other assertion in that class would also be satisfied by a validator that refuses everything. ## Row 9 — the positive half runs; the conflict half is pinned as a rule `all-adapters` starts PostgreSQL and MongoDB and the application together, and the application reports both persistence switches on. The two adapters implement disjoint ports, so there is no ambiguity to resolve and no `@Primary` involved. That absence is what needs pinning, because manufacturing a conflict would test Spring's `NoUniqueBeanDefinitionException` rather than this repository. What can silently change is the property the row depends on: that no port is resolved by preferring one bean over another. A `@Primary` added later to settle an ambiguity would convert a startup rejection into a silent pick, and every existing test would stay green, because the composition would still boot. `PortResolutionContractTest` states the rule directly — a `@Primary` on a port implementation is permitted only when a condition decides which one is active, which makes it a selector rather than a tiebreak. The whole repository has three `@Primary` beans: | bean | type | guard | | --- | --- | --- | | `inProcessDistributedLock` | `DistributedLockPort` | `multi-instance-enabled` false or absent | | `distributedLockProvider` | `DistributedLockPort` | `multi-instance-enabled` true | | `applicationTaskExecutor` | `TaskExecutor` | none — Boot's own contract wants a primary executor, and it is not a port | The two lock beans cannot coexist, so neither is being preferred; one of them simply is not there. The rule was falsified before being trusted: removing the `@ConditionalOnProperty` from `distributedLockProvider` fails the test, and restoring it passes.