feat: jpa, messaging, notification, mongo, graphql 어댑터터 리펙토링

This commit is contained in:
DongHyeonka
2026-08-18 10:59:56 +09:00
parent 2f5d2fc219
commit e98b56eb03
372 changed files with 25131 additions and 20357 deletions
@@ -0,0 +1,237 @@
# Wave 6 — Final Qualification and Documentation Sync Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans (this wave is
> verification-heavy and benefits from one session holding the whole evidence set).
> **Read [`2026-08-15-five-adapter-runtime-remediation-index.md`](2026-08-15-five-adapter-runtime-remediation-index.md)
> first.**
> **Entry criterion:** Wave 5 complete — build logic extracted with a clean baseline diff.
**Goal:** Run every blocking gate, the full activation matrix, and every environment smoke; reconcile
generated metadata and documentation with the code; and produce the evidence set that lets each
Definition-of-Done checkbox in spec §13 be ticked with a command and its output attached.
**Architecture:** Wave 6 adds no capability. It executes, records, and reconciles. Every claim is
backed by a command and its output stored under
`docs/superpowers/plans/evidence/2026-08-15-wave6-final/`. A checkbox without attached evidence stays
unticked, and a gate that could not run is reported as not-run with its reason — never as passing.
**Spec:** [`2026-08-15-five-adapter-runtime-remediation-review-design.md`](../specs/2026-08-15-five-adapter-runtime-remediation-review-design.md)
(§12 in full, §13, §11 Wave 6)
---
## Global Constraints
Inherited from the index. Wave 6 adds:
- **Use `superpowers:verification-before-completion` before any completion claim.** Evidence precedes
assertion, always.
- **A not-run gate is reported as not-run.** Spec HARD-STOP 6 (`AGENTS.md:22`) makes claiming
completion without the verification, or without naming why it could not run, a stop condition.
- **No conclusion broader than its evidence** (HARD-STOP 7). "The matrix passed" requires the matrix,
not a representative lane.
- **The wave ends with the LLM Wiki capture** required by `AGENTS.md:79-90`, or with an explicit
reported reason it was blocked.
---
## Task 1: Build and architecture gates
- [ ] Run and capture each:
```bash
cd src
./gradlew clean compileJava compileTestJava --warning-mode=fail --no-daemon --console=plain
./gradlew test --warning-mode=fail --no-daemon --console=plain
./gradlew check --warning-mode=fail --no-daemon --console=plain
./gradlew verifyCleanArchitectureDependencies verifyEnvKeys \
verifyRuntimeModuleMembership verifyPublicPathSnapshot \
verifyDocumentedLeafCount --console=plain
```
- [ ] Confirm `./gradlew wave0RedReport --console=plain --no-daemon` reports an **empty** red set,
then delete the `wave0Red` lanes and the aggregate — the characterizations they tracked are now
ordinary tests, and a permanent lane for an empty set is a lane that stops being read.
## Task 2: Focused module gates
Gradle paths are read from `src/config/architecture/modules.json`, never from memory.
- [ ] Run and capture:
```bash
cd src
./gradlew :adapter:outbound:persistence-jpa:test --console=plain
./gradlew :adapter:outbound:persistence-mongo:test --console=plain
./gradlew :adapter:outbound:messaging:test --console=plain
./gradlew :adapter:outbound:notification:test --console=plain
./gradlew :adapter:inbound:graphql:test --console=plain
./gradlew :adapter:inbound:graphql:graphqlStableTest \
:app-bootstrap:graphqlRuntimeQualification \
conditionalTransportQualification --console=plain
```
- [ ] Run the messaging platform's Stable facade focused tests and its live-broker lane separately.
Record explicitly that ordinary `test` does **not** substitute for the Docker-backed
qualification.
- [ ] Persistence blocking lanes:
```bash
cd src
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformReleaseGate -Pjpa.matrix.versions=16 --console=plain
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformReleaseGate -Pjpa.matrix.versions=17 --console=plain
./gradlew :adapter:outbound:persistence-jpa:jpaPlatformReleaseGate -Pjpa.matrix.versions=18 --console=plain
./gradlew :adapter:outbound:persistence-mongo:mongoStableContractTest \
:adapter:outbound:persistence-mongo:mongoReplicaSetTest \
:adapter:outbound:persistence-mongo:mongoFailoverTest \
:adapter:outbound:persistence-mongo:mongoMigrationTest \
:adapter:outbound:persistence-mongo:mongoCompatibilityTest \
:adapter:outbound:persistence-mongo:mongoSecurityIntegrationTest \
:adapter:outbound:persistence-mongo:mongoPerformanceTest --console=plain
```
- [ ] Confirm Wave 2 Task B5's decision is reflected: either the three lanes exist and run, or their
Stable blocking claim is gone from `src/config/mongodb/release-contracts.json`,
`docs/mongodb/advanced/sharding.md`, and `scripts/verify-mongodb-advanced.sh`. Attach the
`ReleaseManifestTaskExistenceTest` result.
## Task 3: The activation matrix
Run each row and capture the resolved activation report, health, exit code, WARN/ERROR count, and the
bean/thread inventory.
| # | Matrix | Expected |
| --- | --- | --- |
| 1 | five off | boots with no external infrastructure; health OK; adapter beans, resources, threads, endpoints all zero |
| 2 | JPA only | PostgreSQL + Flyway + Hibernate `validate` succeed; no Mongo, GraphQL, broker, or provider |
| 3 | Mongo only | replica set, client, topology, security succeed; no JPA entity, repository, or pool |
| 4 | Messaging only | broker publish/consume succeeds; with relay off, no database needed |
| 5 | Notification + JPA, `INGEST_ONLY` | durable accept on a frozen non-empty versioned route; provider credentials, calls, and workers all zero; after a `SERVING` restart, exactly one delivery on the same route |
| 6 | Notification + JPA, `SERVING` | reference provider delivery and receipt succeed |
| 7 | GraphQL only | schema endpoint plus security/policy pipeline succeed; no persistence resolver |
| 8 | relay on, dependency missing | startup rejects, **naming the exact missing switch or provider** |
| 9 | JPA + Mongo | distinct ports succeed; two implementations of one port is a startup rejection, not a `@Primary` pick |
| 10 | five on | every dependency and endpoint ready; no silent fallback and no duplicate authority |
- [ ] Row 8 must be checked for the *name*, not merely for a failure. A generic "misconfiguration" is a
fail.
- [ ] Row 9's conflict case must fail; a bean-ordering or `@Primary` resolution is a fail.
## Task 4: Environment and runtime gates
- [ ] Compose artifacts, statically then dynamically:
```bash
./scripts/verify-compose-profile-contracts.sh
./scripts/run-compose-runtime-smoke.sh --matrix src/config/runtime/compose-profile-contracts.json
```
- [ ] Confirm the matrix owned every lane: `off-local`, `off-dev`, `off-prod`, each local adapter lane,
`shared-infra-local`, `shared-infra-dev`, `prod-smoke`, `all-adapters`.
- [ ] Confirm `prod-smoke` actually **started** TLS PostgreSQL, the app, Keycloak, and MinIO and ran
both one-shots — a `config`-only pass is a fail.
- [ ] Confirm each run stored exit code, active profile, resolved activation report, and WARN/ERROR
count as artifacts.
- [ ] Confirm no dev or prod lane was made to pass with a local override. Spec §12.4 does not accept
that as evidence for the profile.
- [ ] Confirm teardown left no stray project or volume: `docker ps -a`, `docker volume ls`.
## Task 5: Documentation and metadata reconciliation
- [ ] Regenerate Spring configuration metadata and diff against the env registry and the YAMLs; any
drift is a fail.
- [ ] Confirm every registry env key appears in `src/.env.example` and that no example carries a real
secret.
- [ ] Confirm `docs/registries/env-keys.yaml` lists the five masters with `false` defaults and the two
subordinate selectors with their `required-when` conditions.
- [ ] Confirm no demoted key (`APP_MESSAGING_BROKER`, `APP_NOTIFICATION_SLACK_PROVIDER`,
`APP_NOTIFICATION_EMAIL_PROVIDER`, `app.jpa-platform.enabled`) is documented anywhere as an
activation switch.
- [ ] Update `README.md` with the five switches, the Compose minimum version, and the two script entry
points.
- [ ] Update `src/app-bootstrap/CLAUDE.md`, replacing its "19-leaf dependency list" phrasing with a
pointer to the registry — a count in prose is the drift `AGENTS.md:52-55` forbids.
- [ ] Confirm the capability docs do not label as Stable anything the index's scope boundaries exclude:
Mongo reactive, change streams, sharding, Atlas, KMS; Notification before NTF-INT-007 is closed.
## Task 6: Definition of Done
Walk spec §13 and tick each box **only** with attached evidence. Reproduced here as the checklist:
- [ ] Five runtime facades on one bootJar runtime classpath.
- [ ] Five canonical master switches in registry, YAML, metadata, and docs, all defaulting `false`.
- [ ] All-off `local`, `dev`, `prod` smoke succeeds with no external resources.
- [ ] Each adapter's off invariant and on fail-closed contract pinned by full-context tests.
- [ ] Mongo, GraphQL, and Messaging Stable facades' resolved runtime membership matches the registry.
- [ ] The JPA switch controls the whole DataSource/Hikari/entity/repository/Hibernate/Flyway/DB
health-and-metrics graph.
- [ ] Messaging has a real broker bridge and a consistent relay dependency.
- [ ] Notification `SERVING` works through production assemblers; `INGEST_ONLY` starts no worker.
- [ ] Notification handoff proves, in one project and DB volume, `INGEST_ONLY` accept → restart →
`SERVING` delivery exactly once on the same frozen route, with zero duplicates.
- [ ] GraphQL policy and JWT context execute on the real `/graphql` request path.
- [ ] GraphQL blocking qualification runs the bootJar JWT composition exactly once and uses neither
class-existence nor test-only Basic Auth as release evidence.
- [ ] `SPRING_PROFILES_ACTIVE` is exactly one of `local|dev|prod`; profileless, multiple, and unknown
deployments are rejected.
- [ ] GraphQL on requires one environment-permitted `APP_GRAPHQL_DEPLOYMENT_MODE`; the legacy
boolean/enum split-brain is rejected.
- [ ] Profiles and env example/secret sources are separated; real secret files are excluded from
tracking, rendering, and evidence.
- [ ] Compose minimum version, per-profile exact service sets, the whole merged model, and mount-target
uniqueness are verified by the canonical script.
- [ ] The runtime-smoke wrapper performs create, `up --wait`, required one-shots, sanitized evidence,
and unique-project teardown with zero skips across `local`, `dev`, and `prod` blocking lanes.
- [ ] PostgreSQL, Mongo, broker, MinIO, and Keycloak/realm smoke evidence exists.
- [ ] The Keycloak realm provides a client-credentials-only service account, audience, and role claims,
and real JWT-protected REST and GraphQL requests succeed against the same issuer URL per lane.
- [ ] Full `test` and `check`, plus architecture, env, public-path, and strict qualification, all pass.
- [ ] Zero Gradle, javac, Checkstyle, SpotBugs, and runtime-startup errors and warnings; zero allowlist
entries; IDE Problems zero confirmed separately on the same toolchain.
- [ ] The real active profile and the structured-log profile field agree; zero late-MeterFilter
warnings.
- [ ] After convention-plugin extraction, task selection, dependency graph, and evidence semantics are
unchanged.
- [ ] Every P0 blocker on a runtime path from the detailed module reviews is either closed or its
capability is explicitly inactive/experimental.
- [ ] Changed files, commands, results, not-run/blocked items, and evidence grades are captured in the
LLM Wiki branch-note.
## Task 7: LLM Wiki capture
Per `AGENTS.md:79-90`:
- [ ] Create or update
`/home/donghyeon/workspace/ai-tool/llm-wiki-private/raw/branch-notes/<branch-name>.md` with the
implementation, changed files, decisions, verification commands, failures/blocks, and evidence
grades.
- [ ] Create derived documents where genuine material exists: `raw/errors/`, `raw/interviews/`,
`raw/blog-topics/`. Each links upward via `## Parent`; the branch-note's `## Cluster / 묶음`
links back.
- [ ] Where no derived document is warranted, record that judgement explicitly ("추출할 별도 글감
없음") rather than omitting the section.
- [ ] Do not create `wiki/blog/`, `wiki/interview/`, `wiki/portfolio/`, `wiki/concepts/`, or
`wiki/projects/` without an explicit canonical extraction request.
## Task 8: Final report
Per `AGENTS.md:239-251`, the closing response states: changed files; the core changes; verification
commands run; verifications that failed or could not run, with reasons; the Wiki capture result; and
remaining risks or follow-ups.
- [ ] Explicitly restate what remains **out of scope and not production-ready**, from the index's scope
boundaries: Mongo reactive, change streams, sharding, Atlas, KMS; Notification's at-rest decision
if the threat-model branch was chosen; object storage's runtime inclusion; Fileserver internals.
---
## Wave 6 Exit Criteria
- [ ] Every §13 checkbox above is ticked **with attached evidence**, or is explicitly reported as
not-met with its reason.
- [ ] `docs/superpowers/plans/evidence/2026-08-15-wave6-final/` holds the output of every command in
Tasks 14.
- [ ] The LLM Wiki branch-note exists and links its derived documents.
- [ ] No completion, "all passing", or "production-ready" claim appears anywhere without the command
output that supports it.