feat: 설계 문서 추가

This commit is contained in:
DongHyeonka
2026-08-13 21:09:52 +09:00
parent 0a6dd0e419
commit 3b5aee50e3
33 changed files with 45861 additions and 0 deletions
@@ -0,0 +1,6 @@
44ba9931722364a53fcb3b5f31a1d539eabcaf42db775f5a33fb558f558c7504 README.md
d064f0ac6c3be0e5c76ef22454db2a97e1d78ed287bd22f4c125f19aba3ad8e3 VALIDATION.md
1ef15812f33dc998a6332b87523ed5942ba46d79d984a0ca776b05bb9247a06a docs/superpowers/plans/2026-08-12-graphql-advanced-capabilities-expansion-plan.md
5ae70b53e22cdb852b2bb0df171dec868bfe99b15bb8e71fb2b0b3431cd7e2cd docs/superpowers/plans/2026-08-12-graphql-api-execution-platform-implementation-plan.md
8d0203203f6bfe4b2e18625eff23bb308ba6454703a4ca4cd3236dab31ecafc3 docs/superpowers/specs/2026-08-12-graphql-api-execution-platform-design.md
8048fe6a536de67d2cf5b0df05d35128f2c68ba8f0dd615831b40430fc76277b validate_graphql_docs.py
+43
View File
@@ -0,0 +1,43 @@
# GraphQL Superpowers 설계 패키지
이 패키지는 `GraphQL API 실행 플랫폼 심층 리서치`를 구현 기준선으로 변환한 설계서와 실행 계획서다.
## 문서
- `docs/superpowers/specs/2026-08-12-graphql-api-execution-platform-design.md`
- Stable·Advanced 전체 아키텍처, 공개 계약, 경계, 실패 의미론, 테스트와 지원 등급
- 입력 심층 리서치 원문을 추적 부록으로 포함
- `docs/superpowers/plans/2026-08-12-graphql-api-execution-platform-implementation-plan.md`
- Stable 구현 Task 148
- `docs/superpowers/plans/2026-08-12-graphql-advanced-capabilities-expansion-plan.md`
- Stable Release Gate 이후 실행하는 Advanced·Experimental Task 119
- `VALIDATION.md`
- 정적 검증 결과와 검증 범위
- `validate_graphql_docs.py`
- 패키지 내부 문서 재검증 스크립트
- `MANIFEST.sha256`
- 패키지 파일 무결성 목록
## 구현 순서
```text
Stable Task 148
→ Stable Release Gate
→ Advanced Task 119
→ Capability별 Promotion Gate
```
## 명시적 전제
```text
Java 21
Gradle Kotlin DSL
Spring Boot 4.1 BOM
Spring for GraphQL 2.0
Boot-managed GraphQL Java v25 계열
Stable module root: modules/graphql
Advanced module root: modules/graphql-advanced
Root package: io.backend.skeleton.graphql
```
실제 저장소에 적용할 때 기존 package·version catalog·module naming에 맞춰 경로만 조정하고, 문서의 공개 계약·불변 조건·테스트 의미는 유지한다.
+98
View File
@@ -0,0 +1,98 @@
# GraphQL Superpowers 문서 정적 검증 결과
- **검증 시각 기준:** 2026-08-12
- **검증 대상:** 설계서 1개, Stable 구현 계획서 1개, Advanced·Experimental 확장 계획서 1개
- **검증 명령:** `python3 validate_graphql_docs.py`
- **결과:** **PASS**
- **실행 검사:** 1,475
- **통과:** 1,475
- **실패:** 0
## 문서 규모
| 문서 | 행 수 | 크기 |
|---|---:|---:|
| GraphQL API 실행 플랫폼 설계서 | 2,553 | 93,359 bytes |
| Stable 구현 계획서 | 4,560 | 209,041 bytes |
| Advanced 확장 계획서 | 1,976 | 105,717 bytes |
## 계획 구조
| 항목 | Stable | Advanced |
|---|---:|---:|
| Task 수 | 48 | 19 |
| Create 경로 수 | 227 | 113 |
| Task 번호 연속성 | PASS | PASS |
| 모든 Task의 `Files`·`Interfaces` | PASS | PASS |
| 모든 Task의 Implementation Requirements | PASS | PASS |
| 모든 Task의 Step 15 | PASS | PASS |
| 실패·통과 예상 결과 | PASS | PASS |
| Task별 Git commit 명령 | PASS | PASS |
| Create 경로 중복 | 없음 | 없음 |
| Stable·Advanced 경로 충돌 | 없음 | 없음 |
## 핵심 계약 검증
```text
SDL-first external contract
Single Executable Schema Stable default
HTTP POST Stable profile
application/graphql-response+json preferred
Validation 이후 Field Error·Partial Data는 HTTP 200
Draft 294는 Stable에서 제외
JPA Entity·MongoDB Document 직접 노출 금지
GraphQL Multipart Upload 미지원·Fileserver 사용
request-wide database transaction 금지
DataLoader request scope
Finite Fetch Profile
HMAC-signed cursor
Mutation idempotency·expected version 분리
Parser·shape·complexity·runtime response budget
Actor·Field·Object·Tenant authorization
Low-cardinality observability
Stable/Advanced dependency isolation
Persisted Operation·WebSocket·SSE·Federation 분리
RSocket·HTTP GET·Incremental Delivery Experimental
```
위 계약은 설계서와 계획서의 필수 문자열·모듈 경로·Task별 파일·테스트를 대조해 검증했습니다.
## 입력 리서치 추적성
- 첨부된 `GraphQL API 실행 플랫폼 심층 리서치` 원문 전체가 설계서의 `부록 B`에 포함되어 있습니다.
- 설계 본문은 원문의 용어와 결론을 유지하면서 구현 판단을 Stable·Advanced·Experimental로 고정합니다.
- 설계서와 입력 원문의 exact text 포함 검사를 별도로 통과했습니다.
## 패키지 검증 항목
```text
문서 파일 존재
Markdown code fence 균형
Task 148 / 119 연속성
Task별 테스트·명령·commit
정확한 Create 경로
Placeholder 금지
Stable module에 WebSocket·Federation·Persisted Operation 경로 부재
Advanced module에 feature flag와 capability 경로 존재
금지 API pattern 부재
문서 SHA-256 계산
```
## 검증 범위의 한계
현재 PASS는 **문서의 정적 구조, 요구사항 추적성, 내부 계약과 실행 계획의 완결성**을 의미합니다. 실제 Backend Skeleton 저장소가 입력으로 제공되지 않았으므로 다음은 실행하지 않았습니다.
```text
Gradle configuration·compile
Spring Boot ApplicationContext 기동
SchemaMappingInspector 실제 결과
GraphQlTester HTTP·WebFlux contract
JPA·MongoDB statement/query-count integration
query bomb·complexity load test
Virtual Thread·event-loop blocking test
WebSocket·SSE soak test
Federation composition·router integration
actual Git commit
```
실제 구현에서는 Stable Task 148을 먼저 수행해 Stable Release Gate를 통과한 뒤 Advanced Task 119를 시작해야 합니다.
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/env python3
from __future__ import annotations
from pathlib import Path
import re
import sys
import hashlib
ROOT = Path(__file__).resolve().parent
DESIGN = ROOT / "docs/superpowers/specs/2026-08-12-graphql-api-execution-platform-design.md"
STABLE = ROOT / "docs/superpowers/plans/2026-08-12-graphql-api-execution-platform-implementation-plan.md"
ADVANCED = ROOT / "docs/superpowers/plans/2026-08-12-graphql-advanced-capabilities-expansion-plan.md"
checks: list[tuple[str, bool, str]] = []
def check(name: str, condition: bool, detail: str = "") -> None:
checks.append((name, bool(condition), detail))
def read(path: Path) -> str:
check(f"file exists: {path.name}", path.exists(), str(path))
return path.read_text(encoding="utf-8") if path.exists() else ""
design = read(DESIGN)
stable = read(STABLE)
advanced = read(ADVANCED)
# Basic document integrity
check("design line floor", len(design.splitlines()) >= 2000, str(len(design.splitlines())))
check("stable plan line floor", len(stable.splitlines()) >= 4000, str(len(stable.splitlines())))
check("advanced plan line floor", len(advanced.splitlines()) >= 1500, str(len(advanced.splitlines())))
for label, text in [("design", design), ("stable", stable), ("advanced", advanced)]:
check(f"{label} code fences balanced", text.count("```") % 2 == 0, str(text.count("```")))
for marker in ["TODO", "TBD", "FIXME", "implement later", "fill in details"]:
check(f"{label} no placeholder {marker}", marker.lower() not in text.lower())
# Design required sections and source traceability
required_design_terms = [
"# GraphQL API 실행 플랫폼 설계서",
"GraphQL Platform owns",
"Domain/Application owns",
"G1 Standard GraphQL API",
"G2 Advanced Execution",
"G3 GraphQL Extension",
"G4 Admin Plane",
"SDL",
"September 2025",
"application/graphql-response+json",
"HTTP `200`",
"GraphQlRequestContext",
"DataLoader",
"GraphQlFetchProfile",
"HMAC",
"Idempotency",
"Partial Data",
"Persisted Operation",
"Subscription",
"Federation",
"GraphQL Multipart Upload",
"Fileserver",
"부록 B. 입력 심층 리서치 원문",
"# GraphQL API 실행 플랫폼 심층 리서치",
]
for term in required_design_terms:
check(f"design contains {term}", term in design)
# Critical design invariants
critical_pairs = [
("field error uses HTTP 200", "field error" in design.lower() and "HTTP `200`" in design),
("no draft 294 stable", "294" in design and "Stable" in design),
("dataloader request scope", "request" in design.lower() and "DataLoader" in design),
("cursor HMAC", "Cursor" in design and "HMAC" in design),
("no multipart upload", "Multipart Upload" in design and "Fileserver" in design),
("single schema default", "Single Executable Schema" in design),
("request-wide transaction prohibited", "request-wide" in design.lower() and "transaction" in design.lower()),
("entity/document boundary", "JPA Entity" in design and "MongoDB Document" in design),
]
for name, condition in critical_pairs:
check(name, condition)
# Plan headers and global constraints
stable_header_terms = [
"# GraphQL API 실행 플랫폼 Implementation Plan",
"REQUIRED SUB-SKILL",
"**Goal:**",
"**Architecture:**",
"**Tech Stack:**",
"## Global Constraints",
"Stable Task",
]
advanced_header_terms = [
"# GraphQL Advanced Capability Expansion Implementation Plan",
"REQUIRED SUB-SKILL",
"backend.graphql.advanced.*",
"Stable 구현 계획 Task `148`",
]
for term in stable_header_terms:
check(f"stable header contains {term}", term in stable)
for term in advanced_header_terms:
check(f"advanced header contains {term}", term in advanced)
# Task sequence and per-task structure
def task_sections(text: str) -> list[tuple[int, str]]:
matches = list(re.finditer(r"^### Task (\d+): .+$", text, re.MULTILINE))
result = []
for i, match in enumerate(matches):
start = match.start()
end = matches[i+1].start() if i+1 < len(matches) else len(text)
result.append((int(match.group(1)), text[start:end]))
return result
stable_tasks = task_sections(stable)
advanced_tasks = task_sections(advanced)
check("stable task count", len(stable_tasks) == 48, str(len(stable_tasks)))
check("advanced task count", len(advanced_tasks) == 19, str(len(advanced_tasks)))
check("stable task sequence", [n for n, _ in stable_tasks] == list(range(1, 49)))
check("advanced task sequence", [n for n, _ in advanced_tasks] == list(range(1, 20)))
def validate_tasks(label: str, tasks: list[tuple[int, str]]) -> None:
required = [
"**Files:**",
"**Interfaces:**",
"**Implementation requirements:**",
"**Step 1: Write the failing test**",
"**Step 2: Run the focused test and verify the failure**",
"**Step 3: Implement the smallest complete production contract**",
"**Step 4: Run the focused test and the owning suite**",
"**Step 5: Commit the independently reviewable change**",
"Expected: FAIL",
"Expected: PASS",
"git commit -m",
]
for number, section in tasks:
for token in required:
check(f"{label} task {number} contains {token}", token in section)
check(f"{label} task {number} has test path", "- Test: `" in section)
check(f"{label} task {number} has production file", "- Create: `" in section)
check(f"{label} task {number} fences balanced", section.count("```") % 2 == 0)
check(f"{label} task {number} has gradle test", "./gradlew" in section and ":test" in section)
validate_tasks("stable", stable_tasks)
validate_tasks("advanced", advanced_tasks)
# Create paths
def create_paths(text: str) -> list[str]:
return re.findall(r"^- Create: `([^`]+)`$", text, re.MULTILINE)
stable_paths = create_paths(stable)
advanced_paths = create_paths(advanced)
check("stable create paths exist", len(stable_paths) >= 150, str(len(stable_paths)))
check("advanced create paths exist", len(advanced_paths) >= 80, str(len(advanced_paths)))
check("stable create paths unique", len(stable_paths) == len(set(stable_paths)))
check("advanced create paths unique", len(advanced_paths) == len(set(advanced_paths)))
check("stable and advanced paths disjoint", set(stable_paths).isdisjoint(advanced_paths))
for index, path in enumerate(stable_paths, 1):
check(f"stable create path {index} exact", "*" not in path and "..." not in path and (path.startswith("modules/graphql/") or path.startswith("build-logic/")))
for index, path in enumerate(advanced_paths, 1):
check(f"advanced create path {index} exact", "*" not in path and "..." not in path and path.startswith("modules/graphql-advanced/"))
# Stable/Advanced separation
for forbidden in [
"modules/graphql/graphql-websocket/",
"modules/graphql/graphql-federation/",
"modules/graphql/graphql-persisted-operation/",
"modules/graphql/graphql-rsocket/",
]:
check(f"stable excludes {forbidden}", forbidden not in stable)
for required in [
"modules/graphql-advanced/graphql-persisted-operation/",
"modules/graphql-advanced/graphql-websocket/",
"modules/graphql-advanced/graphql-subscription/",
"modules/graphql-advanced/graphql-federation/",
"modules/graphql-advanced/graphql-rsocket/",
]:
check(f"advanced includes {required}", required in advanced)
# Stable coverage
stable_required_terms = [
"GraphQlRequestContext",
"GraphQlClientPolicy",
"GraphQlSchemaContract",
"SchemaMappingInspector",
"@oneOf",
"GraphQlHttpProfile",
"application/graphql-response+json",
"GraphQlExecutionProfile",
"GraphQlWireError",
"GraphQlTenantIsolationPolicy",
"GraphQlParserLimits",
"GraphQlComplexityCalculator",
"GraphQlRuntimeBudget",
"GraphQlPreparsedCacheKey",
"GraphQlBatchPolicy",
"GraphQlFetchProfile",
"HmacGraphQlCursorCodec",
"GraphQlConnection",
"GraphQlMutationIdempotencyContext",
"GraphQlMetricCardinalityPolicy",
"GraphQlPlatformStartupValidator",
"GraphQlReleaseGate",
]
for term in stable_required_terms:
check(f"stable coverage {term}", term in stable)
advanced_required_terms = [
"GraphQlPersistedOperation",
"GraphQlWebSocketProtocol",
"GraphQlSubscriptionBufferPolicy",
"GraphQlSubscriptionOrderingProfile",
"GraphQlSseConnectionPolicy",
"GraphQlReplayPosition",
"GraphQlDataLoaderDependencyGraph",
"GraphQlFederationEntityKey",
"GraphQlFederationCompositionGate",
"GraphQlGeneratedSourceBoundary",
"GraphQlRepositoryAllowlist",
"GraphQlRSocketRoutePolicy",
"GraphQlHttpGetOperationPolicy",
"GraphQlIncrementalCompatibilityGate",
"GraphQlAdvancedReleaseGate",
]
for term in advanced_required_terms:
check(f"advanced coverage {term}", term in advanced)
# Prohibited API patterns
prohibited_patterns = [
(r"interface\s+GenericGraphQlRepository", "no generic graphql repository"),
(r"public\s+.*\bEntityManager\b", "no public entity manager"),
(r"public\s+.*\bMongoTemplate\b", "no public mongo template"),
(r"scalar\s+Upload\b", "no upload scalar declaration"),
(r"@Transactional\s+.*GraphQL request", "no request-wide transaction implementation"),
]
for pattern, name in prohibited_patterns:
check(name, re.search(pattern, stable, re.IGNORECASE | re.MULTILINE) is None)
# File hashes can be printed for package evidence
for path in [DESIGN, STABLE, ADVANCED]:
if path.exists():
digest = hashlib.sha256(path.read_bytes()).hexdigest()
check(f"sha256 computed: {path.name}", len(digest) == 64, digest)
failed = [(n, d) for n, ok, d in checks if not ok]
print(f"CHECKS={len(checks)}")
print(f"PASSED={len(checks)-len(failed)}")
print(f"FAILED={len(failed)}")
for name, detail in failed:
print(f"FAIL: {name}" + (f" :: {detail}" if detail else ""))
sys.exit(1 if failed else 0)
+6
View File
@@ -0,0 +1,6 @@
72113b04239cd397787fec5cdc9ac5aa309e85c53767042fc7019338afb884d0 ./README.md
2ae49f02d38b32dbd660ae3957f97912f469c0ff404e0e665d4986fe21e33963 ./VALIDATION.md
1ac376309b161cf95b6b3def89284e509ce0191131a268833ca13bd50814ff09 ./docs/superpowers/plans/2026-08-11-jpa-persistence-experimental-expansion-plan.md
bb5d0d876e9a3232a3661cd50b3a4c0da0ea4c68f820526d8b34cb3bdcb43d86 ./docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md
a28046eb1451d87e3ac9c3b1922c134b99fa9155d32c09c0f5bddf80f588aaec ./docs/superpowers/specs/2026-08-11-jpa-persistence-platform-design.md
4e53da920430d5a82a744475a30b8036d2412627c2488d182daf6d3c1b013eb6 ./validate_jpa_docs.py
+68
View File
@@ -0,0 +1,68 @@
# JPA 관계형 영속성 플랫폼 Superpowers 패키지
이 패키지는 Java/Spring Backend Skeleton의 JPA 관계형 영속성 플랫폼을 구현하기 위한 설계서, Stable 구현 계획서, Experimental 확장 계획서와 정적 검증 도구를 포함한다.
## 기준
- 기준일: 2026-08-11
- Java 21
- Gradle Kotlin DSL
- Spring Boot 4.1 dependency management
- Spring Data JPA 4.1
- Jakarta Persistence 3.2
- Hibernate ORM 7.4
- PostgreSQL 16·17·18 Stable Matrix
- H2는 Local Convenience이며 PostgreSQL 호환성 증거로 사용하지 않는다.
## 문서
```text
docs/superpowers/specs/2026-08-11-jpa-persistence-platform-design.md
docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md
docs/superpowers/plans/2026-08-11-jpa-persistence-experimental-expansion-plan.md
```
## 구현 경계
Stable 계획은 다음 원칙을 고정한다.
```text
도메인이 Entity·Embeddable·Repository·Query를 소유한다.
GenericRepository 또는 Spring Data CRUD 재포장을 만들지 않는다.
Application Service가 Transaction 경계를 소유한다.
Retry는 새 Persistence Context에서 전체 Use Case를 재실행한다.
Commit 결과 불명은 자동 Retry하지 않고 reconciliation으로 처리한다.
Flyway가 운영 Schema 변경의 Source of Truth다.
PostgreSQL 16·17·18 실제 계약 테스트를 Release Gate로 사용한다.
```
Experimental 계획은 다음 범위를 Stable과 분리한다.
```text
Shared-schema Multi-tenancy
PostgreSQL RLS
Schema-per-tenant
Database-per-tenant
Consistency-aware Read Replica
Jakarta Persistence 4.0
Hibernate ORM 8
PostgreSQL 19
```
## 실행 순서
1. Stable 구현 계획 Task 1~53을 순서대로 수행한다.
2. 각 Task에서 실패 테스트 → 실패 확인 → 최소 구현 → 통과 확인 → 커밋 순서를 따른다.
3. Stable Release Gate가 통과한 뒤 Experimental Task 1~9를 시작한다.
4. Experimental 기능은 별도 feature flag와 승격 ADR 없이 Stable Starter에 포함하지 않는다.
## 검증
압축을 해제한 패키지 루트에서 실행한다.
```bash
python validate_jpa_docs.py
sha256sum -c MANIFEST.sha256
```
현재 `VALIDATION.md`는 문서 구조·Task 완결성·타입 일관성·경로 충돌·핵심 계약을 대상으로 한 정적 검증 결과다. 실제 Backend Skeleton 저장소의 Gradle compile, PostgreSQL integration, Flyway upgrade, failure injection, 성능 및 보안 테스트 결과는 아니다.
+829
View File
@@ -0,0 +1,829 @@
# JPA Superpowers 문서 정적 검증
- 결과: **PASS**
- 실행 검사: **808개**
- 통과: **808개**
- 실패: **0개**
- 설계서: **3,276행**
- Stable 구현 계획서: **4,716행**
- Experimental 확장 계획서: **771행**
- Stable Task: **53개**
- Experimental Task: **9개**
- Stable Create 경로: **235개**
- Experimental Create 경로: **37개**
- 설계 SHA-256: `a28046eb1451d87e3ac9c3b1922c134b99fa9155d32c09c0f5bddf80f588aaec`
- Stable 계획 SHA-256: `bb5d0d876e9a3232a3661cd50b3a4c0da0ea4c68f820526d8b34cb3bdcb43d86`
- Experimental 계획 SHA-256: `1ac376309b161cf95b6b3def89284e509ce0191131a268833ca13bd50814ff09`
## 검사 결과
| 검사 | 결과 | 상세 |
|---|---:|---|
| 설계서 존재 | PASS | /mnt/data/jpa-superpowers-package/docs/superpowers/specs/2026-08-11-jpa-persistence-platform-design.md |
| Stable 계획서 존재 | PASS | /mnt/data/jpa-superpowers-package/docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md |
| Experimental 계획서 존재 | PASS | /mnt/data/jpa-superpowers-package/docs/superpowers/plans/2026-08-11-jpa-persistence-experimental-expansion-plan.md |
| 설계서 최소 상세도 | PASS | 3276 lines |
| Stable 계획서 최소 상세도 | PASS | 4716 lines |
| Experimental 계획서 최소 상세도 | PASS | 771 lines |
| 설계서 코드 fence 균형 | PASS | 262 |
| 설계서 placeholder 부재 | PASS | |
| Stable 계획서 코드 fence 균형 | PASS | 540 |
| Stable 계획서 placeholder 부재 | PASS | |
| Experimental 계획서 코드 fence 균형 | PASS | 94 |
| Experimental 계획서 placeholder 부재 | PASS | |
| 설계 핵심 계약: GenericRepository | PASS | |
| 설계 핵심 계약: TransactionCompletionUnknownException | PASS | |
| 설계 핵심 계약: EvidenceAwareJpaTransactionManager | PASS | |
| 설계 핵심 계약: Application Service | PASS | |
| 설계 핵심 계약: OSIV | PASS | |
| 설계 핵심 계약: PostgreSQL 16·17·18 | PASS | |
| 설계 핵심 계약: Hibernate 7.4 Collection Fetch Pagination | PASS | |
| 설계 핵심 계약: FOR UPDATE SKIP LOCKED | PASS | |
| 설계 핵심 계약: CREATE INDEX CONCURRENTLY | PASS | |
| 설계 핵심 계약: Flyway | PASS | |
| 설계 핵심 계약: Runtime·Migration·Admin | PASS | |
| 설계 핵심 계약: H2는 Local Convenience | PASS | |
| 설계 핵심 계약: 전체 Transaction Retry | PASS | |
| 설계 핵심 계약: J1 Standard Persistence | PASS | |
| 설계 핵심 계약: J4 Admin / Operations | PASS | |
| 설계 핵심 계약: 완료 정의 | PASS | |
| GenericRepository 실제 선언 부재 | PASS | |
| 운영 ddl auto update 금지 | PASS | |
| Completion Unknown 자동 Retry 금지 | PASS | |
| OSIV false 강제 | PASS | |
| PG16·17·18 Matrix | PASS | |
| Hibernate 7.4 fetch pagination gate | PASS | |
| Flyway snapshot upgrade gate | PASS | |
| Runtime role no DDL gate | PASS | |
| Stable 계획에 Experimental create 경로 부재 | PASS | |
| Stable Task 1~53 연속성 | PASS | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53] |
| Experimental Task 1~9 연속성 | PASS | [1, 2, 3, 4, 5, 6, 7, 8, 9] |
| Stable Task chunk 수 | PASS | 53 |
| Experimental Task chunk 수 | PASS | 9 |
| Stable Task 1 필수 항목: **Files:** | PASS | |
| Stable Task 1 필수 항목: **Interfaces:** | PASS | |
| Stable Task 1 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 1 필수 항목: **Step 1: | PASS | |
| Stable Task 1 필수 항목: **Step 2: | PASS | |
| Stable Task 1 필수 항목: **Step 3: | PASS | |
| Stable Task 1 필수 항목: **Step 4: | PASS | |
| Stable Task 1 필수 항목: **Step 5: | PASS | |
| Stable Task 1 필수 항목: Expected: | PASS | |
| Stable Task 1 필수 항목: git commit -m | PASS | |
| Stable Task 1 Gradle focused command | PASS | |
| Stable Task 1 exact path | PASS | |
| Stable Task 2 필수 항목: **Files:** | PASS | |
| Stable Task 2 필수 항목: **Interfaces:** | PASS | |
| Stable Task 2 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 2 필수 항목: **Step 1: | PASS | |
| Stable Task 2 필수 항목: **Step 2: | PASS | |
| Stable Task 2 필수 항목: **Step 3: | PASS | |
| Stable Task 2 필수 항목: **Step 4: | PASS | |
| Stable Task 2 필수 항목: **Step 5: | PASS | |
| Stable Task 2 필수 항목: Expected: | PASS | |
| Stable Task 2 필수 항목: git commit -m | PASS | |
| Stable Task 2 Gradle focused command | PASS | |
| Stable Task 2 exact path | PASS | |
| Stable Task 3 필수 항목: **Files:** | PASS | |
| Stable Task 3 필수 항목: **Interfaces:** | PASS | |
| Stable Task 3 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 3 필수 항목: **Step 1: | PASS | |
| Stable Task 3 필수 항목: **Step 2: | PASS | |
| Stable Task 3 필수 항목: **Step 3: | PASS | |
| Stable Task 3 필수 항목: **Step 4: | PASS | |
| Stable Task 3 필수 항목: **Step 5: | PASS | |
| Stable Task 3 필수 항목: Expected: | PASS | |
| Stable Task 3 필수 항목: git commit -m | PASS | |
| Stable Task 3 Gradle focused command | PASS | |
| Stable Task 3 exact path | PASS | |
| Stable Task 4 필수 항목: **Files:** | PASS | |
| Stable Task 4 필수 항목: **Interfaces:** | PASS | |
| Stable Task 4 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 4 필수 항목: **Step 1: | PASS | |
| Stable Task 4 필수 항목: **Step 2: | PASS | |
| Stable Task 4 필수 항목: **Step 3: | PASS | |
| Stable Task 4 필수 항목: **Step 4: | PASS | |
| Stable Task 4 필수 항목: **Step 5: | PASS | |
| Stable Task 4 필수 항목: Expected: | PASS | |
| Stable Task 4 필수 항목: git commit -m | PASS | |
| Stable Task 4 Gradle focused command | PASS | |
| Stable Task 4 exact path | PASS | |
| Stable Task 5 필수 항목: **Files:** | PASS | |
| Stable Task 5 필수 항목: **Interfaces:** | PASS | |
| Stable Task 5 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 5 필수 항목: **Step 1: | PASS | |
| Stable Task 5 필수 항목: **Step 2: | PASS | |
| Stable Task 5 필수 항목: **Step 3: | PASS | |
| Stable Task 5 필수 항목: **Step 4: | PASS | |
| Stable Task 5 필수 항목: **Step 5: | PASS | |
| Stable Task 5 필수 항목: Expected: | PASS | |
| Stable Task 5 필수 항목: git commit -m | PASS | |
| Stable Task 5 Gradle focused command | PASS | |
| Stable Task 5 exact path | PASS | |
| Stable Task 6 필수 항목: **Files:** | PASS | |
| Stable Task 6 필수 항목: **Interfaces:** | PASS | |
| Stable Task 6 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 6 필수 항목: **Step 1: | PASS | |
| Stable Task 6 필수 항목: **Step 2: | PASS | |
| Stable Task 6 필수 항목: **Step 3: | PASS | |
| Stable Task 6 필수 항목: **Step 4: | PASS | |
| Stable Task 6 필수 항목: **Step 5: | PASS | |
| Stable Task 6 필수 항목: Expected: | PASS | |
| Stable Task 6 필수 항목: git commit -m | PASS | |
| Stable Task 6 Gradle focused command | PASS | |
| Stable Task 6 exact path | PASS | |
| Stable Task 7 필수 항목: **Files:** | PASS | |
| Stable Task 7 필수 항목: **Interfaces:** | PASS | |
| Stable Task 7 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 7 필수 항목: **Step 1: | PASS | |
| Stable Task 7 필수 항목: **Step 2: | PASS | |
| Stable Task 7 필수 항목: **Step 3: | PASS | |
| Stable Task 7 필수 항목: **Step 4: | PASS | |
| Stable Task 7 필수 항목: **Step 5: | PASS | |
| Stable Task 7 필수 항목: Expected: | PASS | |
| Stable Task 7 필수 항목: git commit -m | PASS | |
| Stable Task 7 Gradle focused command | PASS | |
| Stable Task 7 exact path | PASS | |
| Stable Task 8 필수 항목: **Files:** | PASS | |
| Stable Task 8 필수 항목: **Interfaces:** | PASS | |
| Stable Task 8 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 8 필수 항목: **Step 1: | PASS | |
| Stable Task 8 필수 항목: **Step 2: | PASS | |
| Stable Task 8 필수 항목: **Step 3: | PASS | |
| Stable Task 8 필수 항목: **Step 4: | PASS | |
| Stable Task 8 필수 항목: **Step 5: | PASS | |
| Stable Task 8 필수 항목: Expected: | PASS | |
| Stable Task 8 필수 항목: git commit -m | PASS | |
| Stable Task 8 Gradle focused command | PASS | |
| Stable Task 8 exact path | PASS | |
| Stable Task 9 필수 항목: **Files:** | PASS | |
| Stable Task 9 필수 항목: **Interfaces:** | PASS | |
| Stable Task 9 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 9 필수 항목: **Step 1: | PASS | |
| Stable Task 9 필수 항목: **Step 2: | PASS | |
| Stable Task 9 필수 항목: **Step 3: | PASS | |
| Stable Task 9 필수 항목: **Step 4: | PASS | |
| Stable Task 9 필수 항목: **Step 5: | PASS | |
| Stable Task 9 필수 항목: Expected: | PASS | |
| Stable Task 9 필수 항목: git commit -m | PASS | |
| Stable Task 9 Gradle focused command | PASS | |
| Stable Task 9 exact path | PASS | |
| Stable Task 10 필수 항목: **Files:** | PASS | |
| Stable Task 10 필수 항목: **Interfaces:** | PASS | |
| Stable Task 10 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 10 필수 항목: **Step 1: | PASS | |
| Stable Task 10 필수 항목: **Step 2: | PASS | |
| Stable Task 10 필수 항목: **Step 3: | PASS | |
| Stable Task 10 필수 항목: **Step 4: | PASS | |
| Stable Task 10 필수 항목: **Step 5: | PASS | |
| Stable Task 10 필수 항목: Expected: | PASS | |
| Stable Task 10 필수 항목: git commit -m | PASS | |
| Stable Task 10 Gradle focused command | PASS | |
| Stable Task 10 exact path | PASS | |
| Stable Task 11 필수 항목: **Files:** | PASS | |
| Stable Task 11 필수 항목: **Interfaces:** | PASS | |
| Stable Task 11 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 11 필수 항목: **Step 1: | PASS | |
| Stable Task 11 필수 항목: **Step 2: | PASS | |
| Stable Task 11 필수 항목: **Step 3: | PASS | |
| Stable Task 11 필수 항목: **Step 4: | PASS | |
| Stable Task 11 필수 항목: **Step 5: | PASS | |
| Stable Task 11 필수 항목: Expected: | PASS | |
| Stable Task 11 필수 항목: git commit -m | PASS | |
| Stable Task 11 Gradle focused command | PASS | |
| Stable Task 11 exact path | PASS | |
| Stable Task 12 필수 항목: **Files:** | PASS | |
| Stable Task 12 필수 항목: **Interfaces:** | PASS | |
| Stable Task 12 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 12 필수 항목: **Step 1: | PASS | |
| Stable Task 12 필수 항목: **Step 2: | PASS | |
| Stable Task 12 필수 항목: **Step 3: | PASS | |
| Stable Task 12 필수 항목: **Step 4: | PASS | |
| Stable Task 12 필수 항목: **Step 5: | PASS | |
| Stable Task 12 필수 항목: Expected: | PASS | |
| Stable Task 12 필수 항목: git commit -m | PASS | |
| Stable Task 12 Gradle focused command | PASS | |
| Stable Task 12 exact path | PASS | |
| Stable Task 13 필수 항목: **Files:** | PASS | |
| Stable Task 13 필수 항목: **Interfaces:** | PASS | |
| Stable Task 13 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 13 필수 항목: **Step 1: | PASS | |
| Stable Task 13 필수 항목: **Step 2: | PASS | |
| Stable Task 13 필수 항목: **Step 3: | PASS | |
| Stable Task 13 필수 항목: **Step 4: | PASS | |
| Stable Task 13 필수 항목: **Step 5: | PASS | |
| Stable Task 13 필수 항목: Expected: | PASS | |
| Stable Task 13 필수 항목: git commit -m | PASS | |
| Stable Task 13 Gradle focused command | PASS | |
| Stable Task 13 exact path | PASS | |
| Stable Task 14 필수 항목: **Files:** | PASS | |
| Stable Task 14 필수 항목: **Interfaces:** | PASS | |
| Stable Task 14 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 14 필수 항목: **Step 1: | PASS | |
| Stable Task 14 필수 항목: **Step 2: | PASS | |
| Stable Task 14 필수 항목: **Step 3: | PASS | |
| Stable Task 14 필수 항목: **Step 4: | PASS | |
| Stable Task 14 필수 항목: **Step 5: | PASS | |
| Stable Task 14 필수 항목: Expected: | PASS | |
| Stable Task 14 필수 항목: git commit -m | PASS | |
| Stable Task 14 Gradle focused command | PASS | |
| Stable Task 14 exact path | PASS | |
| Stable Task 15 필수 항목: **Files:** | PASS | |
| Stable Task 15 필수 항목: **Interfaces:** | PASS | |
| Stable Task 15 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 15 필수 항목: **Step 1: | PASS | |
| Stable Task 15 필수 항목: **Step 2: | PASS | |
| Stable Task 15 필수 항목: **Step 3: | PASS | |
| Stable Task 15 필수 항목: **Step 4: | PASS | |
| Stable Task 15 필수 항목: **Step 5: | PASS | |
| Stable Task 15 필수 항목: Expected: | PASS | |
| Stable Task 15 필수 항목: git commit -m | PASS | |
| Stable Task 15 Gradle focused command | PASS | |
| Stable Task 15 exact path | PASS | |
| Stable Task 16 필수 항목: **Files:** | PASS | |
| Stable Task 16 필수 항목: **Interfaces:** | PASS | |
| Stable Task 16 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 16 필수 항목: **Step 1: | PASS | |
| Stable Task 16 필수 항목: **Step 2: | PASS | |
| Stable Task 16 필수 항목: **Step 3: | PASS | |
| Stable Task 16 필수 항목: **Step 4: | PASS | |
| Stable Task 16 필수 항목: **Step 5: | PASS | |
| Stable Task 16 필수 항목: Expected: | PASS | |
| Stable Task 16 필수 항목: git commit -m | PASS | |
| Stable Task 16 Gradle focused command | PASS | |
| Stable Task 16 exact path | PASS | |
| Stable Task 17 필수 항목: **Files:** | PASS | |
| Stable Task 17 필수 항목: **Interfaces:** | PASS | |
| Stable Task 17 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 17 필수 항목: **Step 1: | PASS | |
| Stable Task 17 필수 항목: **Step 2: | PASS | |
| Stable Task 17 필수 항목: **Step 3: | PASS | |
| Stable Task 17 필수 항목: **Step 4: | PASS | |
| Stable Task 17 필수 항목: **Step 5: | PASS | |
| Stable Task 17 필수 항목: Expected: | PASS | |
| Stable Task 17 필수 항목: git commit -m | PASS | |
| Stable Task 17 Gradle focused command | PASS | |
| Stable Task 17 exact path | PASS | |
| Stable Task 18 필수 항목: **Files:** | PASS | |
| Stable Task 18 필수 항목: **Interfaces:** | PASS | |
| Stable Task 18 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 18 필수 항목: **Step 1: | PASS | |
| Stable Task 18 필수 항목: **Step 2: | PASS | |
| Stable Task 18 필수 항목: **Step 3: | PASS | |
| Stable Task 18 필수 항목: **Step 4: | PASS | |
| Stable Task 18 필수 항목: **Step 5: | PASS | |
| Stable Task 18 필수 항목: Expected: | PASS | |
| Stable Task 18 필수 항목: git commit -m | PASS | |
| Stable Task 18 Gradle focused command | PASS | |
| Stable Task 18 exact path | PASS | |
| Stable Task 19 필수 항목: **Files:** | PASS | |
| Stable Task 19 필수 항목: **Interfaces:** | PASS | |
| Stable Task 19 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 19 필수 항목: **Step 1: | PASS | |
| Stable Task 19 필수 항목: **Step 2: | PASS | |
| Stable Task 19 필수 항목: **Step 3: | PASS | |
| Stable Task 19 필수 항목: **Step 4: | PASS | |
| Stable Task 19 필수 항목: **Step 5: | PASS | |
| Stable Task 19 필수 항목: Expected: | PASS | |
| Stable Task 19 필수 항목: git commit -m | PASS | |
| Stable Task 19 Gradle focused command | PASS | |
| Stable Task 19 exact path | PASS | |
| Stable Task 20 필수 항목: **Files:** | PASS | |
| Stable Task 20 필수 항목: **Interfaces:** | PASS | |
| Stable Task 20 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 20 필수 항목: **Step 1: | PASS | |
| Stable Task 20 필수 항목: **Step 2: | PASS | |
| Stable Task 20 필수 항목: **Step 3: | PASS | |
| Stable Task 20 필수 항목: **Step 4: | PASS | |
| Stable Task 20 필수 항목: **Step 5: | PASS | |
| Stable Task 20 필수 항목: Expected: | PASS | |
| Stable Task 20 필수 항목: git commit -m | PASS | |
| Stable Task 20 Gradle focused command | PASS | |
| Stable Task 20 exact path | PASS | |
| Stable Task 21 필수 항목: **Files:** | PASS | |
| Stable Task 21 필수 항목: **Interfaces:** | PASS | |
| Stable Task 21 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 21 필수 항목: **Step 1: | PASS | |
| Stable Task 21 필수 항목: **Step 2: | PASS | |
| Stable Task 21 필수 항목: **Step 3: | PASS | |
| Stable Task 21 필수 항목: **Step 4: | PASS | |
| Stable Task 21 필수 항목: **Step 5: | PASS | |
| Stable Task 21 필수 항목: Expected: | PASS | |
| Stable Task 21 필수 항목: git commit -m | PASS | |
| Stable Task 21 Gradle focused command | PASS | |
| Stable Task 21 exact path | PASS | |
| Stable Task 22 필수 항목: **Files:** | PASS | |
| Stable Task 22 필수 항목: **Interfaces:** | PASS | |
| Stable Task 22 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 22 필수 항목: **Step 1: | PASS | |
| Stable Task 22 필수 항목: **Step 2: | PASS | |
| Stable Task 22 필수 항목: **Step 3: | PASS | |
| Stable Task 22 필수 항목: **Step 4: | PASS | |
| Stable Task 22 필수 항목: **Step 5: | PASS | |
| Stable Task 22 필수 항목: Expected: | PASS | |
| Stable Task 22 필수 항목: git commit -m | PASS | |
| Stable Task 22 Gradle focused command | PASS | |
| Stable Task 22 exact path | PASS | |
| Stable Task 23 필수 항목: **Files:** | PASS | |
| Stable Task 23 필수 항목: **Interfaces:** | PASS | |
| Stable Task 23 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 23 필수 항목: **Step 1: | PASS | |
| Stable Task 23 필수 항목: **Step 2: | PASS | |
| Stable Task 23 필수 항목: **Step 3: | PASS | |
| Stable Task 23 필수 항목: **Step 4: | PASS | |
| Stable Task 23 필수 항목: **Step 5: | PASS | |
| Stable Task 23 필수 항목: Expected: | PASS | |
| Stable Task 23 필수 항목: git commit -m | PASS | |
| Stable Task 23 Gradle focused command | PASS | |
| Stable Task 23 exact path | PASS | |
| Stable Task 24 필수 항목: **Files:** | PASS | |
| Stable Task 24 필수 항목: **Interfaces:** | PASS | |
| Stable Task 24 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 24 필수 항목: **Step 1: | PASS | |
| Stable Task 24 필수 항목: **Step 2: | PASS | |
| Stable Task 24 필수 항목: **Step 3: | PASS | |
| Stable Task 24 필수 항목: **Step 4: | PASS | |
| Stable Task 24 필수 항목: **Step 5: | PASS | |
| Stable Task 24 필수 항목: Expected: | PASS | |
| Stable Task 24 필수 항목: git commit -m | PASS | |
| Stable Task 24 Gradle focused command | PASS | |
| Stable Task 24 exact path | PASS | |
| Stable Task 25 필수 항목: **Files:** | PASS | |
| Stable Task 25 필수 항목: **Interfaces:** | PASS | |
| Stable Task 25 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 25 필수 항목: **Step 1: | PASS | |
| Stable Task 25 필수 항목: **Step 2: | PASS | |
| Stable Task 25 필수 항목: **Step 3: | PASS | |
| Stable Task 25 필수 항목: **Step 4: | PASS | |
| Stable Task 25 필수 항목: **Step 5: | PASS | |
| Stable Task 25 필수 항목: Expected: | PASS | |
| Stable Task 25 필수 항목: git commit -m | PASS | |
| Stable Task 25 Gradle focused command | PASS | |
| Stable Task 25 exact path | PASS | |
| Stable Task 26 필수 항목: **Files:** | PASS | |
| Stable Task 26 필수 항목: **Interfaces:** | PASS | |
| Stable Task 26 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 26 필수 항목: **Step 1: | PASS | |
| Stable Task 26 필수 항목: **Step 2: | PASS | |
| Stable Task 26 필수 항목: **Step 3: | PASS | |
| Stable Task 26 필수 항목: **Step 4: | PASS | |
| Stable Task 26 필수 항목: **Step 5: | PASS | |
| Stable Task 26 필수 항목: Expected: | PASS | |
| Stable Task 26 필수 항목: git commit -m | PASS | |
| Stable Task 26 Gradle focused command | PASS | |
| Stable Task 26 exact path | PASS | |
| Stable Task 27 필수 항목: **Files:** | PASS | |
| Stable Task 27 필수 항목: **Interfaces:** | PASS | |
| Stable Task 27 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 27 필수 항목: **Step 1: | PASS | |
| Stable Task 27 필수 항목: **Step 2: | PASS | |
| Stable Task 27 필수 항목: **Step 3: | PASS | |
| Stable Task 27 필수 항목: **Step 4: | PASS | |
| Stable Task 27 필수 항목: **Step 5: | PASS | |
| Stable Task 27 필수 항목: Expected: | PASS | |
| Stable Task 27 필수 항목: git commit -m | PASS | |
| Stable Task 27 Gradle focused command | PASS | |
| Stable Task 27 exact path | PASS | |
| Stable Task 28 필수 항목: **Files:** | PASS | |
| Stable Task 28 필수 항목: **Interfaces:** | PASS | |
| Stable Task 28 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 28 필수 항목: **Step 1: | PASS | |
| Stable Task 28 필수 항목: **Step 2: | PASS | |
| Stable Task 28 필수 항목: **Step 3: | PASS | |
| Stable Task 28 필수 항목: **Step 4: | PASS | |
| Stable Task 28 필수 항목: **Step 5: | PASS | |
| Stable Task 28 필수 항목: Expected: | PASS | |
| Stable Task 28 필수 항목: git commit -m | PASS | |
| Stable Task 28 Gradle focused command | PASS | |
| Stable Task 28 exact path | PASS | |
| Stable Task 29 필수 항목: **Files:** | PASS | |
| Stable Task 29 필수 항목: **Interfaces:** | PASS | |
| Stable Task 29 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 29 필수 항목: **Step 1: | PASS | |
| Stable Task 29 필수 항목: **Step 2: | PASS | |
| Stable Task 29 필수 항목: **Step 3: | PASS | |
| Stable Task 29 필수 항목: **Step 4: | PASS | |
| Stable Task 29 필수 항목: **Step 5: | PASS | |
| Stable Task 29 필수 항목: Expected: | PASS | |
| Stable Task 29 필수 항목: git commit -m | PASS | |
| Stable Task 29 Gradle focused command | PASS | |
| Stable Task 29 exact path | PASS | |
| Stable Task 30 필수 항목: **Files:** | PASS | |
| Stable Task 30 필수 항목: **Interfaces:** | PASS | |
| Stable Task 30 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 30 필수 항목: **Step 1: | PASS | |
| Stable Task 30 필수 항목: **Step 2: | PASS | |
| Stable Task 30 필수 항목: **Step 3: | PASS | |
| Stable Task 30 필수 항목: **Step 4: | PASS | |
| Stable Task 30 필수 항목: **Step 5: | PASS | |
| Stable Task 30 필수 항목: Expected: | PASS | |
| Stable Task 30 필수 항목: git commit -m | PASS | |
| Stable Task 30 Gradle focused command | PASS | |
| Stable Task 30 exact path | PASS | |
| Stable Task 31 필수 항목: **Files:** | PASS | |
| Stable Task 31 필수 항목: **Interfaces:** | PASS | |
| Stable Task 31 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 31 필수 항목: **Step 1: | PASS | |
| Stable Task 31 필수 항목: **Step 2: | PASS | |
| Stable Task 31 필수 항목: **Step 3: | PASS | |
| Stable Task 31 필수 항목: **Step 4: | PASS | |
| Stable Task 31 필수 항목: **Step 5: | PASS | |
| Stable Task 31 필수 항목: Expected: | PASS | |
| Stable Task 31 필수 항목: git commit -m | PASS | |
| Stable Task 31 Gradle focused command | PASS | |
| Stable Task 31 exact path | PASS | |
| Stable Task 32 필수 항목: **Files:** | PASS | |
| Stable Task 32 필수 항목: **Interfaces:** | PASS | |
| Stable Task 32 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 32 필수 항목: **Step 1: | PASS | |
| Stable Task 32 필수 항목: **Step 2: | PASS | |
| Stable Task 32 필수 항목: **Step 3: | PASS | |
| Stable Task 32 필수 항목: **Step 4: | PASS | |
| Stable Task 32 필수 항목: **Step 5: | PASS | |
| Stable Task 32 필수 항목: Expected: | PASS | |
| Stable Task 32 필수 항목: git commit -m | PASS | |
| Stable Task 32 Gradle focused command | PASS | |
| Stable Task 32 exact path | PASS | |
| Stable Task 33 필수 항목: **Files:** | PASS | |
| Stable Task 33 필수 항목: **Interfaces:** | PASS | |
| Stable Task 33 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 33 필수 항목: **Step 1: | PASS | |
| Stable Task 33 필수 항목: **Step 2: | PASS | |
| Stable Task 33 필수 항목: **Step 3: | PASS | |
| Stable Task 33 필수 항목: **Step 4: | PASS | |
| Stable Task 33 필수 항목: **Step 5: | PASS | |
| Stable Task 33 필수 항목: Expected: | PASS | |
| Stable Task 33 필수 항목: git commit -m | PASS | |
| Stable Task 33 Gradle focused command | PASS | |
| Stable Task 33 exact path | PASS | |
| Stable Task 34 필수 항목: **Files:** | PASS | |
| Stable Task 34 필수 항목: **Interfaces:** | PASS | |
| Stable Task 34 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 34 필수 항목: **Step 1: | PASS | |
| Stable Task 34 필수 항목: **Step 2: | PASS | |
| Stable Task 34 필수 항목: **Step 3: | PASS | |
| Stable Task 34 필수 항목: **Step 4: | PASS | |
| Stable Task 34 필수 항목: **Step 5: | PASS | |
| Stable Task 34 필수 항목: Expected: | PASS | |
| Stable Task 34 필수 항목: git commit -m | PASS | |
| Stable Task 34 Gradle focused command | PASS | |
| Stable Task 34 exact path | PASS | |
| Stable Task 35 필수 항목: **Files:** | PASS | |
| Stable Task 35 필수 항목: **Interfaces:** | PASS | |
| Stable Task 35 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 35 필수 항목: **Step 1: | PASS | |
| Stable Task 35 필수 항목: **Step 2: | PASS | |
| Stable Task 35 필수 항목: **Step 3: | PASS | |
| Stable Task 35 필수 항목: **Step 4: | PASS | |
| Stable Task 35 필수 항목: **Step 5: | PASS | |
| Stable Task 35 필수 항목: Expected: | PASS | |
| Stable Task 35 필수 항목: git commit -m | PASS | |
| Stable Task 35 Gradle focused command | PASS | |
| Stable Task 35 exact path | PASS | |
| Stable Task 36 필수 항목: **Files:** | PASS | |
| Stable Task 36 필수 항목: **Interfaces:** | PASS | |
| Stable Task 36 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 36 필수 항목: **Step 1: | PASS | |
| Stable Task 36 필수 항목: **Step 2: | PASS | |
| Stable Task 36 필수 항목: **Step 3: | PASS | |
| Stable Task 36 필수 항목: **Step 4: | PASS | |
| Stable Task 36 필수 항목: **Step 5: | PASS | |
| Stable Task 36 필수 항목: Expected: | PASS | |
| Stable Task 36 필수 항목: git commit -m | PASS | |
| Stable Task 36 Gradle focused command | PASS | |
| Stable Task 36 exact path | PASS | |
| Stable Task 37 필수 항목: **Files:** | PASS | |
| Stable Task 37 필수 항목: **Interfaces:** | PASS | |
| Stable Task 37 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 37 필수 항목: **Step 1: | PASS | |
| Stable Task 37 필수 항목: **Step 2: | PASS | |
| Stable Task 37 필수 항목: **Step 3: | PASS | |
| Stable Task 37 필수 항목: **Step 4: | PASS | |
| Stable Task 37 필수 항목: **Step 5: | PASS | |
| Stable Task 37 필수 항목: Expected: | PASS | |
| Stable Task 37 필수 항목: git commit -m | PASS | |
| Stable Task 37 Gradle focused command | PASS | |
| Stable Task 37 exact path | PASS | |
| Stable Task 38 필수 항목: **Files:** | PASS | |
| Stable Task 38 필수 항목: **Interfaces:** | PASS | |
| Stable Task 38 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 38 필수 항목: **Step 1: | PASS | |
| Stable Task 38 필수 항목: **Step 2: | PASS | |
| Stable Task 38 필수 항목: **Step 3: | PASS | |
| Stable Task 38 필수 항목: **Step 4: | PASS | |
| Stable Task 38 필수 항목: **Step 5: | PASS | |
| Stable Task 38 필수 항목: Expected: | PASS | |
| Stable Task 38 필수 항목: git commit -m | PASS | |
| Stable Task 38 Gradle focused command | PASS | |
| Stable Task 38 exact path | PASS | |
| Stable Task 39 필수 항목: **Files:** | PASS | |
| Stable Task 39 필수 항목: **Interfaces:** | PASS | |
| Stable Task 39 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 39 필수 항목: **Step 1: | PASS | |
| Stable Task 39 필수 항목: **Step 2: | PASS | |
| Stable Task 39 필수 항목: **Step 3: | PASS | |
| Stable Task 39 필수 항목: **Step 4: | PASS | |
| Stable Task 39 필수 항목: **Step 5: | PASS | |
| Stable Task 39 필수 항목: Expected: | PASS | |
| Stable Task 39 필수 항목: git commit -m | PASS | |
| Stable Task 39 Gradle focused command | PASS | |
| Stable Task 39 exact path | PASS | |
| Stable Task 40 필수 항목: **Files:** | PASS | |
| Stable Task 40 필수 항목: **Interfaces:** | PASS | |
| Stable Task 40 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 40 필수 항목: **Step 1: | PASS | |
| Stable Task 40 필수 항목: **Step 2: | PASS | |
| Stable Task 40 필수 항목: **Step 3: | PASS | |
| Stable Task 40 필수 항목: **Step 4: | PASS | |
| Stable Task 40 필수 항목: **Step 5: | PASS | |
| Stable Task 40 필수 항목: Expected: | PASS | |
| Stable Task 40 필수 항목: git commit -m | PASS | |
| Stable Task 40 Gradle focused command | PASS | |
| Stable Task 40 exact path | PASS | |
| Stable Task 41 필수 항목: **Files:** | PASS | |
| Stable Task 41 필수 항목: **Interfaces:** | PASS | |
| Stable Task 41 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 41 필수 항목: **Step 1: | PASS | |
| Stable Task 41 필수 항목: **Step 2: | PASS | |
| Stable Task 41 필수 항목: **Step 3: | PASS | |
| Stable Task 41 필수 항목: **Step 4: | PASS | |
| Stable Task 41 필수 항목: **Step 5: | PASS | |
| Stable Task 41 필수 항목: Expected: | PASS | |
| Stable Task 41 필수 항목: git commit -m | PASS | |
| Stable Task 41 Gradle focused command | PASS | |
| Stable Task 41 exact path | PASS | |
| Stable Task 42 필수 항목: **Files:** | PASS | |
| Stable Task 42 필수 항목: **Interfaces:** | PASS | |
| Stable Task 42 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 42 필수 항목: **Step 1: | PASS | |
| Stable Task 42 필수 항목: **Step 2: | PASS | |
| Stable Task 42 필수 항목: **Step 3: | PASS | |
| Stable Task 42 필수 항목: **Step 4: | PASS | |
| Stable Task 42 필수 항목: **Step 5: | PASS | |
| Stable Task 42 필수 항목: Expected: | PASS | |
| Stable Task 42 필수 항목: git commit -m | PASS | |
| Stable Task 42 Gradle focused command | PASS | |
| Stable Task 42 exact path | PASS | |
| Stable Task 43 필수 항목: **Files:** | PASS | |
| Stable Task 43 필수 항목: **Interfaces:** | PASS | |
| Stable Task 43 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 43 필수 항목: **Step 1: | PASS | |
| Stable Task 43 필수 항목: **Step 2: | PASS | |
| Stable Task 43 필수 항목: **Step 3: | PASS | |
| Stable Task 43 필수 항목: **Step 4: | PASS | |
| Stable Task 43 필수 항목: **Step 5: | PASS | |
| Stable Task 43 필수 항목: Expected: | PASS | |
| Stable Task 43 필수 항목: git commit -m | PASS | |
| Stable Task 43 Gradle focused command | PASS | |
| Stable Task 43 exact path | PASS | |
| Stable Task 44 필수 항목: **Files:** | PASS | |
| Stable Task 44 필수 항목: **Interfaces:** | PASS | |
| Stable Task 44 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 44 필수 항목: **Step 1: | PASS | |
| Stable Task 44 필수 항목: **Step 2: | PASS | |
| Stable Task 44 필수 항목: **Step 3: | PASS | |
| Stable Task 44 필수 항목: **Step 4: | PASS | |
| Stable Task 44 필수 항목: **Step 5: | PASS | |
| Stable Task 44 필수 항목: Expected: | PASS | |
| Stable Task 44 필수 항목: git commit -m | PASS | |
| Stable Task 44 Gradle focused command | PASS | |
| Stable Task 44 exact path | PASS | |
| Stable Task 45 필수 항목: **Files:** | PASS | |
| Stable Task 45 필수 항목: **Interfaces:** | PASS | |
| Stable Task 45 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 45 필수 항목: **Step 1: | PASS | |
| Stable Task 45 필수 항목: **Step 2: | PASS | |
| Stable Task 45 필수 항목: **Step 3: | PASS | |
| Stable Task 45 필수 항목: **Step 4: | PASS | |
| Stable Task 45 필수 항목: **Step 5: | PASS | |
| Stable Task 45 필수 항목: Expected: | PASS | |
| Stable Task 45 필수 항목: git commit -m | PASS | |
| Stable Task 45 Gradle focused command | PASS | |
| Stable Task 45 exact path | PASS | |
| Stable Task 46 필수 항목: **Files:** | PASS | |
| Stable Task 46 필수 항목: **Interfaces:** | PASS | |
| Stable Task 46 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 46 필수 항목: **Step 1: | PASS | |
| Stable Task 46 필수 항목: **Step 2: | PASS | |
| Stable Task 46 필수 항목: **Step 3: | PASS | |
| Stable Task 46 필수 항목: **Step 4: | PASS | |
| Stable Task 46 필수 항목: **Step 5: | PASS | |
| Stable Task 46 필수 항목: Expected: | PASS | |
| Stable Task 46 필수 항목: git commit -m | PASS | |
| Stable Task 46 Gradle focused command | PASS | |
| Stable Task 46 exact path | PASS | |
| Stable Task 47 필수 항목: **Files:** | PASS | |
| Stable Task 47 필수 항목: **Interfaces:** | PASS | |
| Stable Task 47 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 47 필수 항목: **Step 1: | PASS | |
| Stable Task 47 필수 항목: **Step 2: | PASS | |
| Stable Task 47 필수 항목: **Step 3: | PASS | |
| Stable Task 47 필수 항목: **Step 4: | PASS | |
| Stable Task 47 필수 항목: **Step 5: | PASS | |
| Stable Task 47 필수 항목: Expected: | PASS | |
| Stable Task 47 필수 항목: git commit -m | PASS | |
| Stable Task 47 Gradle focused command | PASS | |
| Stable Task 47 exact path | PASS | |
| Stable Task 48 필수 항목: **Files:** | PASS | |
| Stable Task 48 필수 항목: **Interfaces:** | PASS | |
| Stable Task 48 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 48 필수 항목: **Step 1: | PASS | |
| Stable Task 48 필수 항목: **Step 2: | PASS | |
| Stable Task 48 필수 항목: **Step 3: | PASS | |
| Stable Task 48 필수 항목: **Step 4: | PASS | |
| Stable Task 48 필수 항목: **Step 5: | PASS | |
| Stable Task 48 필수 항목: Expected: | PASS | |
| Stable Task 48 필수 항목: git commit -m | PASS | |
| Stable Task 48 Gradle focused command | PASS | |
| Stable Task 48 exact path | PASS | |
| Stable Task 49 필수 항목: **Files:** | PASS | |
| Stable Task 49 필수 항목: **Interfaces:** | PASS | |
| Stable Task 49 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 49 필수 항목: **Step 1: | PASS | |
| Stable Task 49 필수 항목: **Step 2: | PASS | |
| Stable Task 49 필수 항목: **Step 3: | PASS | |
| Stable Task 49 필수 항목: **Step 4: | PASS | |
| Stable Task 49 필수 항목: **Step 5: | PASS | |
| Stable Task 49 필수 항목: Expected: | PASS | |
| Stable Task 49 필수 항목: git commit -m | PASS | |
| Stable Task 49 Gradle focused command | PASS | |
| Stable Task 49 exact path | PASS | |
| Stable Task 50 필수 항목: **Files:** | PASS | |
| Stable Task 50 필수 항목: **Interfaces:** | PASS | |
| Stable Task 50 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 50 필수 항목: **Step 1: | PASS | |
| Stable Task 50 필수 항목: **Step 2: | PASS | |
| Stable Task 50 필수 항목: **Step 3: | PASS | |
| Stable Task 50 필수 항목: **Step 4: | PASS | |
| Stable Task 50 필수 항목: **Step 5: | PASS | |
| Stable Task 50 필수 항목: Expected: | PASS | |
| Stable Task 50 필수 항목: git commit -m | PASS | |
| Stable Task 50 Gradle focused command | PASS | |
| Stable Task 50 exact path | PASS | |
| Stable Task 51 필수 항목: **Files:** | PASS | |
| Stable Task 51 필수 항목: **Interfaces:** | PASS | |
| Stable Task 51 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 51 필수 항목: **Step 1: | PASS | |
| Stable Task 51 필수 항목: **Step 2: | PASS | |
| Stable Task 51 필수 항목: **Step 3: | PASS | |
| Stable Task 51 필수 항목: **Step 4: | PASS | |
| Stable Task 51 필수 항목: **Step 5: | PASS | |
| Stable Task 51 필수 항목: Expected: | PASS | |
| Stable Task 51 필수 항목: git commit -m | PASS | |
| Stable Task 51 Gradle focused command | PASS | |
| Stable Task 51 exact path | PASS | |
| Stable Task 52 필수 항목: **Files:** | PASS | |
| Stable Task 52 필수 항목: **Interfaces:** | PASS | |
| Stable Task 52 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 52 필수 항목: **Step 1: | PASS | |
| Stable Task 52 필수 항목: **Step 2: | PASS | |
| Stable Task 52 필수 항목: **Step 3: | PASS | |
| Stable Task 52 필수 항목: **Step 4: | PASS | |
| Stable Task 52 필수 항목: **Step 5: | PASS | |
| Stable Task 52 필수 항목: Expected: | PASS | |
| Stable Task 52 필수 항목: git commit -m | PASS | |
| Stable Task 52 Gradle focused command | PASS | |
| Stable Task 52 exact path | PASS | |
| Stable Task 53 필수 항목: **Files:** | PASS | |
| Stable Task 53 필수 항목: **Interfaces:** | PASS | |
| Stable Task 53 필수 항목: **Implementation requirements:** | PASS | |
| Stable Task 53 필수 항목: **Step 1: | PASS | |
| Stable Task 53 필수 항목: **Step 2: | PASS | |
| Stable Task 53 필수 항목: **Step 3: | PASS | |
| Stable Task 53 필수 항목: **Step 4: | PASS | |
| Stable Task 53 필수 항목: **Step 5: | PASS | |
| Stable Task 53 필수 항목: Expected: | PASS | |
| Stable Task 53 필수 항목: git commit -m | PASS | |
| Stable Task 53 Gradle focused command | PASS | |
| Stable Task 53 exact path | PASS | |
| Experimental Task 1 필수 항목: **Files:** | PASS | |
| Experimental Task 1 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 1 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 1 필수 항목: **Step 1: | PASS | |
| Experimental Task 1 필수 항목: **Step 2: | PASS | |
| Experimental Task 1 필수 항목: **Step 3: | PASS | |
| Experimental Task 1 필수 항목: **Step 4: | PASS | |
| Experimental Task 1 필수 항목: **Step 5: | PASS | |
| Experimental Task 1 필수 항목: Expected: | PASS | |
| Experimental Task 1 필수 항목: git commit -m | PASS | |
| Experimental Task 1 Gradle focused command | PASS | |
| Experimental Task 1 exact path | PASS | |
| Experimental Task 2 필수 항목: **Files:** | PASS | |
| Experimental Task 2 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 2 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 2 필수 항목: **Step 1: | PASS | |
| Experimental Task 2 필수 항목: **Step 2: | PASS | |
| Experimental Task 2 필수 항목: **Step 3: | PASS | |
| Experimental Task 2 필수 항목: **Step 4: | PASS | |
| Experimental Task 2 필수 항목: **Step 5: | PASS | |
| Experimental Task 2 필수 항목: Expected: | PASS | |
| Experimental Task 2 필수 항목: git commit -m | PASS | |
| Experimental Task 2 Gradle focused command | PASS | |
| Experimental Task 2 exact path | PASS | |
| Experimental Task 3 필수 항목: **Files:** | PASS | |
| Experimental Task 3 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 3 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 3 필수 항목: **Step 1: | PASS | |
| Experimental Task 3 필수 항목: **Step 2: | PASS | |
| Experimental Task 3 필수 항목: **Step 3: | PASS | |
| Experimental Task 3 필수 항목: **Step 4: | PASS | |
| Experimental Task 3 필수 항목: **Step 5: | PASS | |
| Experimental Task 3 필수 항목: Expected: | PASS | |
| Experimental Task 3 필수 항목: git commit -m | PASS | |
| Experimental Task 3 Gradle focused command | PASS | |
| Experimental Task 3 exact path | PASS | |
| Experimental Task 4 필수 항목: **Files:** | PASS | |
| Experimental Task 4 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 4 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 4 필수 항목: **Step 1: | PASS | |
| Experimental Task 4 필수 항목: **Step 2: | PASS | |
| Experimental Task 4 필수 항목: **Step 3: | PASS | |
| Experimental Task 4 필수 항목: **Step 4: | PASS | |
| Experimental Task 4 필수 항목: **Step 5: | PASS | |
| Experimental Task 4 필수 항목: Expected: | PASS | |
| Experimental Task 4 필수 항목: git commit -m | PASS | |
| Experimental Task 4 Gradle focused command | PASS | |
| Experimental Task 4 exact path | PASS | |
| Experimental Task 5 필수 항목: **Files:** | PASS | |
| Experimental Task 5 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 5 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 5 필수 항목: **Step 1: | PASS | |
| Experimental Task 5 필수 항목: **Step 2: | PASS | |
| Experimental Task 5 필수 항목: **Step 3: | PASS | |
| Experimental Task 5 필수 항목: **Step 4: | PASS | |
| Experimental Task 5 필수 항목: **Step 5: | PASS | |
| Experimental Task 5 필수 항목: Expected: | PASS | |
| Experimental Task 5 필수 항목: git commit -m | PASS | |
| Experimental Task 5 Gradle focused command | PASS | |
| Experimental Task 5 exact path | PASS | |
| Experimental Task 6 필수 항목: **Files:** | PASS | |
| Experimental Task 6 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 6 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 6 필수 항목: **Step 1: | PASS | |
| Experimental Task 6 필수 항목: **Step 2: | PASS | |
| Experimental Task 6 필수 항목: **Step 3: | PASS | |
| Experimental Task 6 필수 항목: **Step 4: | PASS | |
| Experimental Task 6 필수 항목: **Step 5: | PASS | |
| Experimental Task 6 필수 항목: Expected: | PASS | |
| Experimental Task 6 필수 항목: git commit -m | PASS | |
| Experimental Task 6 Gradle focused command | PASS | |
| Experimental Task 6 exact path | PASS | |
| Experimental Task 7 필수 항목: **Files:** | PASS | |
| Experimental Task 7 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 7 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 7 필수 항목: **Step 1: | PASS | |
| Experimental Task 7 필수 항목: **Step 2: | PASS | |
| Experimental Task 7 필수 항목: **Step 3: | PASS | |
| Experimental Task 7 필수 항목: **Step 4: | PASS | |
| Experimental Task 7 필수 항목: **Step 5: | PASS | |
| Experimental Task 7 필수 항목: Expected: | PASS | |
| Experimental Task 7 필수 항목: git commit -m | PASS | |
| Experimental Task 7 Gradle focused command | PASS | |
| Experimental Task 7 exact path | PASS | |
| Experimental Task 8 필수 항목: **Files:** | PASS | |
| Experimental Task 8 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 8 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 8 필수 항목: **Step 1: | PASS | |
| Experimental Task 8 필수 항목: **Step 2: | PASS | |
| Experimental Task 8 필수 항목: **Step 3: | PASS | |
| Experimental Task 8 필수 항목: **Step 4: | PASS | |
| Experimental Task 8 필수 항목: **Step 5: | PASS | |
| Experimental Task 8 필수 항목: Expected: | PASS | |
| Experimental Task 8 필수 항목: git commit -m | PASS | |
| Experimental Task 8 Gradle focused command | PASS | |
| Experimental Task 8 exact path | PASS | |
| Experimental Task 9 필수 항목: **Files:** | PASS | |
| Experimental Task 9 필수 항목: **Interfaces:** | PASS | |
| Experimental Task 9 필수 항목: **Implementation requirements:** | PASS | |
| Experimental Task 9 필수 항목: **Step 1: | PASS | |
| Experimental Task 9 필수 항목: **Step 2: | PASS | |
| Experimental Task 9 필수 항목: **Step 3: | PASS | |
| Experimental Task 9 필수 항목: **Step 4: | PASS | |
| Experimental Task 9 필수 항목: **Step 5: | PASS | |
| Experimental Task 9 필수 항목: Expected: | PASS | |
| Experimental Task 9 필수 항목: git commit -m | PASS | |
| Experimental Task 9 Gradle focused command | PASS | |
| Experimental Task 9 exact path | PASS | |
| Stable Create 경로 중복 부재 | PASS | 235 |
| Experimental Create 경로 중복 부재 | PASS | 37 |
| Stable·Experimental Create 경로 충돌 부재 | PASS | set() |
| Experimental 계획은 Stable Task 1~53 이후 시작 | PASS | |
| 공통 타입 일관성: PersistenceOperationName | PASS | 21 |
| 공통 타입 일관성: TransactionProfile | PASS | 14 |
| 공통 타입 일관성: RetryProfile | PASS | 5 |
| 공통 타입 일관성: JpaTransactionExecutor | PASS | 14 |
| 공통 타입 일관성: JpaRetryPolicy | PASS | 7 |
| 공통 타입 일관성: RetryDecision | PASS | 8 |
| 공통 타입 일관성: JpaFailureContext | PASS | 11 |
| 공통 타입 일관성: QueryName | PASS | 22 |
| 공통 타입 일관성: KeysetPageRequest | PASS | 4 |
| 공통 타입 일관성: KeysetSlice | PASS | 7 |
| 공통 타입 일관성: TransactionCompletionUnknownException | PASS | 7 |
| 공통 타입 일관성: PostgreSqlWorkClaimExecutor | PASS | 2 |
| 공통 타입 일관성: FlywayValidationGate | PASS | 8 |
| 공통 타입 일관성: JpaPlatformEndpoint | PASS | 3 |
| Experimental Gradle 경로 정확성 | PASS | |
| Replica annotation-only routing 금지 | PASS | |
| RLS connection reuse 검증 | PASS | |
| Stable 승격 ADR gate | PASS | |
| 심층 리서치 원문 부록 포함 | PASS | |
@@ -0,0 +1,771 @@
# JPA Experimental Expansion Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Stable JPA 플랫폼을 변경하지 않고 Multi-tenancy, PostgreSQL RLS, schema/database tenant 분리, consistency-aware Read Replica, Jakarta Persistence 4.0, Hibernate ORM 8, PostgreSQL 19 호환성을 독립 Experimental 모듈과 승격 Gate로 검증한다.
**Architecture:** Experimental module은 Stable `jpa-core-api` 계약만 소비하며 Stable starter에 자동 포함되지 않는다. 각 기능은 명시적 feature flag와 별도 compatibility/failure suite를 요구한다. 실험 결과가 Stable 의미론과 충돌하면 Core를 왜곡하지 않고 capability 또는 별도 profile로 유지한다.
**Tech Stack:** Stable 계획의 Java 21·Spring Boot 4.1·PostgreSQL Testcontainers 기반, PostgreSQL RLS, AbstractRoutingDataSource, tenant-specific DataSource registry, Jakarta Persistence 4.0 preview/final compatibility lane, Hibernate ORM 8 compatibility lane, PostgreSQL 19 compatibility lane.
## Global Constraints
- Stable 계획 Task 1~53이 완료되고 Release Gate가 통과한 뒤 시작한다.
- 모듈 루트는 `modules/jpa-experimental`이다.
- Experimental module은 `jpa-spring-boot-starter`의 기본 dependency가 아니다.
- 모든 기능은 `backend.jpa.experimental.*` feature flag를 요구한다.
- Tenant ID와 consistency token은 metric label에 기록하지 않는다.
- Tenant context 누락은 fail-closed다.
- `readOnly=true`만으로 replica routing하지 않는다.
- Lock query, write transaction, read-after-write pin은 primary를 사용한다.
- JPA4/Hibernate8/PG19 결과로 Stable 3.2/7.4/PG16~18 contract를 수정하지 않는다.
- 승격 전 별도 security, failure, migration and compatibility evidence가 필요하다.
---
## 1. Experimental 파일 구조
```text
modules/jpa-experimental/
├── jpa-experimental-core/
├── jpa-multitenancy-column/
├── jpa-multitenancy-rls/
├── jpa-multitenancy-schema/
├── jpa-multitenancy-database/
├── jpa-read-replica/
└── jpa-next-compatibility/
```
---
### Task 1: Experimental Module·Feature Gate·Dependency Isolation 구성
**Files:**
- Create: `modules/jpa-experimental/jpa-experimental-core/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-multitenancy-column/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-multitenancy-rls/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-multitenancy-schema/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-multitenancy-database/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-read-replica/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts`
- Create: `modules/jpa-experimental/jpa-experimental-core/src/main/java/io/backend/skeleton/jpa/experimental/ExperimentalFeature.java`
- Create: `modules/jpa-experimental/jpa-experimental-core/src/main/java/io/backend/skeleton/jpa/experimental/ExperimentalFeatureGate.java`
- Modify: `settings.gradle.kts`
- Test: `modules/jpa-experimental/jpa-experimental-core/src/test/java/io/backend/skeleton/jpa/experimental/ExperimentalFeatureGateTest.java`
**Interfaces:**
- Consumes: Stable `jpa-core-api` and explicit environment feature flags.
- Produces: Isolated experimental projects that cannot enter the Stable starter transitively.
**Implementation requirements:**
- Every module depends only on Stable public contracts, never on Stable internal packages.
- Feature gate fails startup when module is present but flag is absent.
- Add a dependency graph test proving the Stable starter has no experimental dependency.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental;
class ExperimentalFeatureGateTest {
@Test
void featureIsDisabledUnlessExplicitlyEnabled() {
assertThatThrownBy(() -> gate.requireEnabled(MULTITENANCY_COLUMN, Map.of()))
.hasMessageContaining("backend.jpa.experimental.multitenancy-column=true");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-experimental-core:test --tests 'io.backend.skeleton.jpa.experimental.ExperimentalFeatureGateTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental;
public final class ExperimentalFeatureGate {
public void requireEnabled(
ExperimentalFeature feature,
Map<String, Boolean> flags) {
if (!Boolean.TRUE.equals(flags.get(feature.property()))) {
throw new IllegalStateException(feature.property() + "=true is required");
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-experimental-core:test --tests 'io.backend.skeleton.jpa.experimental.ExperimentalFeatureGateTest'
./gradlew :modules:jpa-experimental:jpa-experimental-core:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-experimental-core/build.gradle.kts' 'modules/jpa-experimental/jpa-multitenancy-column/build.gradle.kts' 'modules/jpa-experimental/jpa-multitenancy-rls/build.gradle.kts' 'modules/jpa-experimental/jpa-multitenancy-schema/build.gradle.kts' 'modules/jpa-experimental/jpa-multitenancy-database/build.gradle.kts' 'modules/jpa-experimental/jpa-read-replica/build.gradle.kts' 'modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts' 'modules/jpa-experimental/jpa-experimental-core/src/main/java/io/backend/skeleton/jpa/experimental/ExperimentalFeature.java' 'modules/jpa-experimental/jpa-experimental-core/src/main/java/io/backend/skeleton/jpa/experimental/ExperimentalFeatureGate.java' 'settings.gradle.kts' 'modules/jpa-experimental/jpa-experimental-core/src/test/java/io/backend/skeleton/jpa/experimental/ExperimentalFeatureGateTest.java'
git commit -m "build: isolate jpa experimental modules"
```
### Task 2: Shared-schema Tenant Context와 Column Guard 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantId.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantContext.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantAwareRepositoryGuard.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantEntityListenerGuard.java`
- Test: `modules/jpa-experimental/jpa-multitenancy-column/src/integrationTest/java/io/backend/skeleton/jpa/experimental/tenant/TenantColumnIsolationTest.java`
**Interfaces:**
- Consumes: Explicit request/job tenant context and domain Entity tenant-column contracts.
- Produces: Fail-closed tenant context propagation and query/write isolation evidence.
**Implementation requirements:**
- Reject Repository access when tenant context is absent outside an audited admin scope.
- Require tenant column in unique/index requirements where isolation depends on it.
- Test async job context propagation and cleanup.
- Do not rely on Hibernate filter alone as the final security boundary.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental.tenant;
class TenantColumnIsolationTest {
@Test
void tenantARepositoryCannotReadTenantBRows() {
insertFor(TENANT_A, "a");
insertFor(TENANT_B, "b");
assertThat(withTenant(TENANT_A, repository::findAll))
.extracting(Item::value)
.containsExactly("a");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-column:integrationTest --tests 'io.backend.skeleton.jpa.experimental.tenant.TenantColumnIsolationTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental.tenant;
public final class TenantContext {
private static final ThreadLocal<TenantId> CURRENT = new ThreadLocal<>();
public static TenantId require() {
TenantId tenant = CURRENT.get();
if (tenant == null) throw new IllegalStateException("tenant context is required");
return tenant;
}
public static void clear() { CURRENT.remove(); }
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-column:integrationTest --tests 'io.backend.skeleton.jpa.experimental.tenant.TenantColumnIsolationTest'
./gradlew :modules:jpa-experimental:jpa-multitenancy-column:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantId.java' 'modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantContext.java' 'modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantAwareRepositoryGuard.java' 'modules/jpa-experimental/jpa-multitenancy-column/src/main/java/io/backend/skeleton/jpa/experimental/tenant/TenantEntityListenerGuard.java' 'modules/jpa-experimental/jpa-multitenancy-column/src/integrationTest/java/io/backend/skeleton/jpa/experimental/tenant/TenantColumnIsolationTest.java'
git commit -m "feat: add experimental tenant column isolation"
```
### Task 3: PostgreSQL RLS Tenant Policy와 Connection Reuse Guard 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-multitenancy-rls/src/main/java/io/backend/skeleton/jpa/experimental/rls/RlsTenantSessionBinder.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-rls/src/main/java/io/backend/skeleton/jpa/experimental/rls/RlsPolicyVerifier.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-rls/src/main/java/io/backend/skeleton/jpa/experimental/rls/RlsAdminBypassToken.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-rls/src/main/resources/db/experimental-rls/V1__tenant_rls.sql`
- Test: `modules/jpa-experimental/jpa-multitenancy-rls/src/failureTest/java/io/backend/skeleton/jpa/experimental/rls/RlsIsolationFailureTest.java`
**Interfaces:**
- Consumes: TenantContext, PostgreSQL transaction-local settings and restricted runtime role.
- Produces: Database-enforced tenant isolation that resets safely across pooled connections.
**Implementation requirements:**
- Set tenant context with transaction-local `set_config` before tenant queries.
- Prove a pooled connection cannot leak the prior tenant into the next transaction.
- Runtime role must not own tables or bypass RLS.
- Admin bypass requires a separate DataSource and audit token.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental.rls;
class RlsIsolationFailureTest {
@Test
void pooledConnectionDoesNotLeakPriorTenantSetting() {
withTenant(TENANT_A, () -> assertThat(repository.count()).isEqualTo(1));
withTenant(TENANT_B, () -> assertThat(repository.count()).isEqualTo(1));
withoutTenant(() -> assertThatThrownBy(repository::count).isInstanceOf(DataAccessException.class));
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-rls:failureTest --tests 'io.backend.skeleton.jpa.experimental.rls.RlsIsolationFailureTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental.rls;
public final class RlsTenantSessionBinder {
public void bind(EntityManager entityManager, TenantId tenant) {
entityManager.createNativeQuery(
"select set_config('app.tenant_id', :tenant, true)")
.setParameter("tenant", tenant.value())
.getSingleResult();
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-rls:failureTest --tests 'io.backend.skeleton.jpa.experimental.rls.RlsIsolationFailureTest'
./gradlew :modules:jpa-experimental:jpa-multitenancy-rls:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-multitenancy-rls/src/main/java/io/backend/skeleton/jpa/experimental/rls/RlsTenantSessionBinder.java' 'modules/jpa-experimental/jpa-multitenancy-rls/src/main/java/io/backend/skeleton/jpa/experimental/rls/RlsPolicyVerifier.java' 'modules/jpa-experimental/jpa-multitenancy-rls/src/main/java/io/backend/skeleton/jpa/experimental/rls/RlsAdminBypassToken.java' 'modules/jpa-experimental/jpa-multitenancy-rls/src/main/resources/db/experimental-rls/V1__tenant_rls.sql' 'modules/jpa-experimental/jpa-multitenancy-rls/src/failureTest/java/io/backend/skeleton/jpa/experimental/rls/RlsIsolationFailureTest.java'
git commit -m "feat: add experimental postgresql rls isolation"
```
### Task 4: Schema-per-tenant Connection Provider와 Migration Orchestrator 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-multitenancy-schema/src/main/java/io/backend/skeleton/jpa/experimental/schema/SchemaTenantRegistry.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-schema/src/main/java/io/backend/skeleton/jpa/experimental/schema/SchemaMultiTenantConnectionProvider.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-schema/src/main/java/io/backend/skeleton/jpa/experimental/schema/SchemaTenantMigrationOrchestrator.java`
- Test: `modules/jpa-experimental/jpa-multitenancy-schema/src/migrationTest/java/io/backend/skeleton/jpa/experimental/schema/SchemaTenantMigrationContractTest.java`
**Interfaces:**
- Consumes: Validated tenant→schema catalog and Flyway migration gate.
- Produces: Bounded schema selection and per-tenant migration status without accepting raw schema names.
**Implementation requirements:**
- Map TenantId to a pre-registered schema identifier; no user-provided SQL identifier.
- Reset schema/search_path when returning pooled connections.
- Track migration version and failure per tenant.
- Rate-limit tenant migrations and support resume without auto-repair.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental.schema;
class SchemaTenantMigrationContractTest {
@Test
void migratesOnlyRegisteredSchemasAndResumesAfterFailure() {
orchestrator.migrateAll(List.of(TENANT_A, TENANT_B));
assertThat(status(TENANT_A).version()).isEqualTo(LATEST);
assertThatThrownBy(() -> orchestrator.migrate(new TenantId("../public")))
.isInstanceOf(IllegalArgumentException.class);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-schema:migrationTest --tests 'io.backend.skeleton.jpa.experimental.schema.SchemaTenantMigrationContractTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental.schema;
public final class SchemaTenantRegistry {
public String requireSchema(TenantId tenant) {
return Optional.ofNullable(schemaByTenant.get(tenant))
.orElseThrow(() -> new IllegalArgumentException("unregistered tenant schema"));
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-schema:migrationTest --tests 'io.backend.skeleton.jpa.experimental.schema.SchemaTenantMigrationContractTest'
./gradlew :modules:jpa-experimental:jpa-multitenancy-schema:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-multitenancy-schema/src/main/java/io/backend/skeleton/jpa/experimental/schema/SchemaTenantRegistry.java' 'modules/jpa-experimental/jpa-multitenancy-schema/src/main/java/io/backend/skeleton/jpa/experimental/schema/SchemaMultiTenantConnectionProvider.java' 'modules/jpa-experimental/jpa-multitenancy-schema/src/main/java/io/backend/skeleton/jpa/experimental/schema/SchemaTenantMigrationOrchestrator.java' 'modules/jpa-experimental/jpa-multitenancy-schema/src/migrationTest/java/io/backend/skeleton/jpa/experimental/schema/SchemaTenantMigrationContractTest.java'
git commit -m "feat: add experimental schema per tenant persistence"
```
### Task 5: Database-per-tenant DataSource Registry와 Capacity Guard 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantDataSourceRegistry.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantEntityManagerFactoryRegistry.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantPoolBudget.java`
- Create: `modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantDataSourceLifecycle.java`
- Test: `modules/jpa-experimental/jpa-multitenancy-database/src/performanceTest/java/io/backend/skeleton/jpa/experimental/database/TenantPoolCapacityContractTest.java`
**Interfaces:**
- Consumes: Secret-backed tenant connection profiles and global DB connection budget.
- Produces: Lazy bounded per-tenant pools with eviction, credential rotation and migration status.
**Implementation requirements:**
- Never create an unbounded Hikari pool per tenant.
- Enforce global maximum pools and connections before creating a DataSource.
- Drain and close pools on tenant removal or credential rotation.
- Do not expose tenant JDBC URLs or credentials in diagnostics.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental.database;
class TenantPoolCapacityContractTest {
@Test
void refusesNewTenantPoolWhenGlobalConnectionBudgetIsExhausted() {
registry.openTenants(globalBudget().maxTenants());
assertThatThrownBy(() -> registry.require(ANOTHER_TENANT))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("tenant pool budget");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-database:performanceTest --tests 'io.backend.skeleton.jpa.experimental.database.TenantPoolCapacityContractTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental.database;
public record TenantPoolBudget(
int maxOpenPools,
int maxConnectionsAcrossPools) {
public void requireCapacity(int openPools, int allocatedConnections) {
if (openPools >= maxOpenPools || allocatedConnections >= maxConnectionsAcrossPools) {
throw new IllegalStateException("tenant pool budget exhausted");
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-multitenancy-database:performanceTest --tests 'io.backend.skeleton.jpa.experimental.database.TenantPoolCapacityContractTest'
./gradlew :modules:jpa-experimental:jpa-multitenancy-database:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantDataSourceRegistry.java' 'modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantEntityManagerFactoryRegistry.java' 'modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantPoolBudget.java' 'modules/jpa-experimental/jpa-multitenancy-database/src/main/java/io/backend/skeleton/jpa/experimental/database/TenantDataSourceLifecycle.java' 'modules/jpa-experimental/jpa-multitenancy-database/src/performanceTest/java/io/backend/skeleton/jpa/experimental/database/TenantPoolCapacityContractTest.java'
git commit -m "feat: add experimental database per tenant registry"
```
### Task 6: Consistency-aware Read Replica Routing 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ReadConsistency.java`
- Create: `modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ConsistencyToken.java`
- Create: `modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ReplicaRoutingDecision.java`
- Create: `modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ConsistencyAwareDataSourceRouter.java`
- Create: `modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ReplicaLagMonitor.java`
- Test: `modules/jpa-experimental/jpa-read-replica/src/failureTest/java/io/backend/skeleton/jpa/experimental/replica/ReadAfterWriteRoutingContractTest.java`
**Interfaces:**
- Consumes: Primary/replica DataSources, transaction state, lock intent and replica lag evidence.
- Produces: Routing decisions for PRIMARY_REQUIRED, BOUNDED_STALENESS and EVENTUAL reads.
**Implementation requirements:**
- Writes, lock queries, REQUIRES_NEW writes and active write transactions always use primary.
- Read-after-write uses a consistency token or primary pin, not `readOnly=true` alone.
- Fallback to primary when replica lag exceeds policy or evidence is unavailable.
- Keep routing fixed for the life of one transaction.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental.replica;
class ReadAfterWriteRoutingContractTest {
@Test
void immediateReadAfterWriteUsesPrimaryUntilConsistencyTokenIsSatisfied() {
var token = service.writeAndReturnConsistencyToken();
var decision = router.route(readOnlyTransaction(), ReadConsistency.after(token));
assertThat(decision.target()).isEqualTo(PRIMARY);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-read-replica:failureTest --tests 'io.backend.skeleton.jpa.experimental.replica.ReadAfterWriteRoutingContractTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental.replica;
public final class ConsistencyAwareDataSourceRouter {
public ReplicaRoutingDecision route(
TransactionContext transaction,
ReadConsistency consistency) {
if (transaction.write() || transaction.locking() ||
!lagMonitor.satisfies(consistency)) {
return ReplicaRoutingDecision.primary();
}
return ReplicaRoutingDecision.replica();
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-read-replica:failureTest --tests 'io.backend.skeleton.jpa.experimental.replica.ReadAfterWriteRoutingContractTest'
./gradlew :modules:jpa-experimental:jpa-read-replica:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ReadConsistency.java' 'modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ConsistencyToken.java' 'modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ReplicaRoutingDecision.java' 'modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ConsistencyAwareDataSourceRouter.java' 'modules/jpa-experimental/jpa-read-replica/src/main/java/io/backend/skeleton/jpa/experimental/replica/ReplicaLagMonitor.java' 'modules/jpa-experimental/jpa-read-replica/src/failureTest/java/io/backend/skeleton/jpa/experimental/replica/ReadAfterWriteRoutingContractTest.java'
git commit -m "feat: add experimental consistency aware replica routing"
```
### Task 7: Jakarta Persistence 4.0 Compatibility Lane 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-next-compatibility/src/compatibilityJpa4/java/io/backend/skeleton/jpa/experimental/next/Jpa4CompatibilityTest.java`
- Create: `.github/workflows/jpa-next-jpa4.yml`
- Modify: `modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts`
- Test: `modules/jpa-experimental/jpa-next-compatibility/src/test/java/io/backend/skeleton/jpa/experimental/next/CompatibilityLaneDefinitionTest.java`
**Interfaces:**
- Consumes: Published Jakarta Persistence 4.0 milestone/final artifact when available and the Stable contract suite.
- Produces: A non-blocking compatibility report that does not alter Stable JPA 3.2 APIs.
**Implementation requirements:**
- Run the Stable public API compilation and selected mapping contracts against JPA 4.
- Record removed/changed APIs and provider support separately.
- Do not publish JPA4 compiled artifacts under Stable coordinates.
- [ ] **Step 1: Write the failing test**
```kotlin
package io.backend.skeleton.jpa.experimental.next;
class CompatibilityLaneDefinitionTest {
@Test
void jpaFourLaneIsExperimentalAndSeparateFromStablePublication() {
assertThat(lane("jpa4").publicationEnabled()).isFalse();
assertThat(lane("jpa4").supportLevel()).isEqualTo(EXPERIMENTAL);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test --tests 'io.backend.skeleton.jpa.experimental.next.CompatibilityLaneDefinitionTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```kotlin
testing {
suites {
register<JvmTestSuite>("compatibilityJpa4") {
useJUnitJupiter()
dependencies {
implementation(project(":modules:jpa:jpa-core-api"))
implementation(libs.jakarta.persistence.next)
}
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test --tests 'io.backend.skeleton.jpa.experimental.next.CompatibilityLaneDefinitionTest'
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-next-compatibility/src/compatibilityJpa4/java/io/backend/skeleton/jpa/experimental/next/Jpa4CompatibilityTest.java' '.github/workflows/jpa-next-jpa4.yml' 'modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts' 'modules/jpa-experimental/jpa-next-compatibility/src/test/java/io/backend/skeleton/jpa/experimental/next/CompatibilityLaneDefinitionTest.java'
git commit -m "test: add jakarta persistence four compatibility lane"
```
### Task 8: Hibernate ORM 8 Compatibility Lane 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-next-compatibility/src/compatibilityHibernate8/java/io/backend/skeleton/jpa/experimental/next/Hibernate8CompatibilityTest.java`
- Create: `.github/workflows/jpa-next-hibernate8.yml`
- Modify: `modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts`
- Test: `modules/jpa-experimental/jpa-next-compatibility/src/test/java/io/backend/skeleton/jpa/experimental/next/HibernateCompatibilityPolicyTest.java`
**Interfaces:**
- Consumes: Hibernate ORM 8 milestone/final artifact and Stable Hibernate 7.4 regression suites.
- Produces: Generated SQL, fetch pagination, statistics, batch and extension compatibility evidence.
**Implementation requirements:**
- Re-run collection fetch pagination, StatementInspector, Statistics, JSONB, Batch and StatelessSession contracts.
- Record SQL and performance differences without weakening the 7.4 Stable gate.
- Do not allow Hibernate 8 dependencies in Stable published modules.
- [ ] **Step 1: Write the failing test**
```kotlin
package io.backend.skeleton.jpa.experimental.next;
class HibernateCompatibilityPolicyTest {
@Test
void hibernateEightCannotReplaceStableProviderWithoutPromotion() {
assertThat(policy.stableProvider()).isEqualTo("7.4");
assertThat(policy.experimentalProviders()).contains("8");
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test --tests 'io.backend.skeleton.jpa.experimental.next.HibernateCompatibilityPolicyTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```kotlin
testing {
suites {
register<JvmTestSuite>("compatibilityHibernate8") {
useJUnitJupiter()
dependencies {
implementation(project(":modules:jpa:jpa-testkit-postgresql"))
implementation(libs.hibernate.orm.next)
}
}
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test --tests 'io.backend.skeleton.jpa.experimental.next.HibernateCompatibilityPolicyTest'
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-next-compatibility/src/compatibilityHibernate8/java/io/backend/skeleton/jpa/experimental/next/Hibernate8CompatibilityTest.java' '.github/workflows/jpa-next-hibernate8.yml' 'modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts' 'modules/jpa-experimental/jpa-next-compatibility/src/test/java/io/backend/skeleton/jpa/experimental/next/HibernateCompatibilityPolicyTest.java'
git commit -m "test: add hibernate eight compatibility lane"
```
### Task 9: PostgreSQL 19 Compatibility와 Stable 승격 Gate 구현
**Files:**
- Create: `modules/jpa-experimental/jpa-next-compatibility/src/compatibilityPostgresql19/java/io/backend/skeleton/jpa/experimental/next/PostgreSql19CompatibilityTest.java`
- Create: `docs/jpa/experimental-support-matrix.md`
- Create: `docs/jpa/experimental-promotion-checklist.md`
- Create: `.github/workflows/jpa-next-postgresql19.yml`
- Modify: `modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts`
- Test: `modules/jpa-experimental/jpa-next-compatibility/src/test/java/io/backend/skeleton/jpa/experimental/next/ExperimentalPromotionGateTest.java`
**Interfaces:**
- Consumes: PG19 image when GA, all Stable contracts, experimental security/failure/migration/performance reports.
- Produces: A promotion decision that requires evidence rather than version availability alone.
**Implementation requirements:**
- Run mapping, SQLSTATE, lock, batch, Flyway, plan and native extension contracts on PG19.
- Promotion requires two supported patch runs and no unresolved semantic regression.
- Multi-tenancy/replica promotion requires tenant leakage, failover, lag and pool-capacity evidence.
- Update Stable support matrix only through a reviewed ADR.
- [ ] **Step 1: Write the failing test**
```java
package io.backend.skeleton.jpa.experimental.next;
class ExperimentalPromotionGateTest {
@Test
void promotionRequiresAllEvidenceAndReviewedAdr() {
var evidence = evidence().withCompatibility(true).withSecurity(true).withFailure(true)
.withMigration(true).withPerformance(true).withReviewedAdr(false);
assertThat(gate.evaluate(evidence)).isEqualTo(BLOCKED_MISSING_ADR);
}
}
```
- [ ] **Step 2: Run the focused test and verify the failure**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test --tests 'io.backend.skeleton.jpa.experimental.next.ExperimentalPromotionGateTest'
```
Expected: FAIL because the production type or behavior does not exist yet.
- [ ] **Step 3: Implement the smallest complete production contract**
```java
package io.backend.skeleton.jpa.experimental.next;
public final class ExperimentalPromotionGate {
public PromotionDecision evaluate(PromotionEvidence evidence) {
if (!evidence.allTechnicalGatesPassed()) return BLOCKED_TECHNICAL;
if (!evidence.reviewedAdr()) return BLOCKED_MISSING_ADR;
return ELIGIBLE_FOR_STABLE_REVIEW;
}
}
```
Implement every file and invariant listed under **Implementation requirements**; the snippet fixes the public names and central behavior rather than replacing those requirements.
- [ ] **Step 4: Run the focused test and the module test suite**
Run:
```bash
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test --tests 'io.backend.skeleton.jpa.experimental.next.ExperimentalPromotionGateTest'
./gradlew :modules:jpa-experimental:jpa-next-compatibility:test
```
Expected: PASS with all assertions green.
- [ ] **Step 5: Commit the independently reviewable change**
```bash
git add 'modules/jpa-experimental/jpa-next-compatibility/src/compatibilityPostgresql19/java/io/backend/skeleton/jpa/experimental/next/PostgreSql19CompatibilityTest.java' 'docs/jpa/experimental-support-matrix.md' 'docs/jpa/experimental-promotion-checklist.md' '.github/workflows/jpa-next-postgresql19.yml' 'modules/jpa-experimental/jpa-next-compatibility/build.gradle.kts' 'modules/jpa-experimental/jpa-next-compatibility/src/test/java/io/backend/skeleton/jpa/experimental/next/ExperimentalPromotionGateTest.java'
git commit -m "docs: add jpa experimental promotion gates"
```
## 2. Experimental 완료 조건
```text
Stable starter가 Experimental module에 의존하지 않는다.
Tenant context 누락과 connection reuse에서 fail-closed다.
RLS runtime role이 policy를 bypass하지 못한다.
Schema/database tenant migration과 pool capacity가 bounded다.
Replica routing이 read-after-write와 lock query를 primary에 고정한다.
JPA4/Hibernate8/PG19 lane이 Stable artifacts를 변경하지 않는다.
승격은 ADR와 compatibility/security/failure/migration/performance 증거를 요구한다.
```
@@ -0,0 +1,210 @@
from __future__ import annotations
from pathlib import Path
import hashlib
import re
import sys
import zipfile
SCRIPT_DIR = Path(__file__).resolve().parent
PACKAGE_DESIGN = SCRIPT_DIR / 'docs/superpowers/specs/2026-08-11-jpa-persistence-platform-design.md'
if PACKAGE_DESIGN.exists():
ROOT = SCRIPT_DIR
DESIGN = PACKAGE_DESIGN
PLAN = ROOT / 'docs/superpowers/plans/2026-08-11-jpa-persistence-platform-implementation-plan.md'
EXPANSION = ROOT / 'docs/superpowers/plans/2026-08-11-jpa-persistence-experimental-expansion-plan.md'
VALIDATION = ROOT / 'VALIDATION.md'
ZIP_PATH = ROOT.parent / 'jpa-superpowers-package.zip'
else:
ROOT = Path('/mnt/data')
DESIGN = ROOT / 'jpa-persistence-platform-design.md'
PLAN = ROOT / 'jpa-persistence-platform-implementation-plan.md'
EXPANSION = ROOT / 'jpa-persistence-experimental-expansion-plan.md'
VALIDATION = ROOT / 'jpa-superpowers-validation.md'
ZIP_PATH = ROOT / 'jpa-superpowers-package.zip'
checks: list[tuple[str, bool, str]] = []
def check(name: str, condition: bool, detail: str = '') -> None:
checks.append((name, bool(condition), detail))
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open('rb') as f:
for chunk in iter(lambda: f.read(1024 * 1024), b''):
h.update(chunk)
return h.hexdigest()
def task_chunks(text: str) -> list[tuple[int, str]]:
matches = list(re.finditer(r'^### Task (\d+):', text, re.MULTILINE))
chunks: list[tuple[int, str]] = []
for i, match in enumerate(matches):
end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
chunks.append((int(match.group(1)), text[match.start():end]))
return chunks
for path, label in [(DESIGN, '설계서'), (PLAN, 'Stable 계획서'), (EXPANSION, 'Experimental 계획서')]:
check(f'{label} 존재', path.exists(), str(path))
if not all(path.exists() for path in [DESIGN, PLAN, EXPANSION]):
print('missing required documents', file=sys.stderr)
sys.exit(2)
design = DESIGN.read_text(encoding='utf-8')
plan = PLAN.read_text(encoding='utf-8')
expansion = EXPANSION.read_text(encoding='utf-8')
line_counts = {
'design': len(design.splitlines()),
'plan': len(plan.splitlines()),
'expansion': len(expansion.splitlines()),
}
check('설계서 최소 상세도', line_counts['design'] >= 2500, f"{line_counts['design']} lines")
check('Stable 계획서 최소 상세도', line_counts['plan'] >= 4000, f"{line_counts['plan']} lines")
check('Experimental 계획서 최소 상세도', line_counts['expansion'] >= 650, f"{line_counts['expansion']} lines")
for text, label in [(design, '설계서'), (plan, 'Stable 계획서'), (expansion, 'Experimental 계획서')]:
fences = len(re.findall(r'^```', text, re.MULTILINE))
check(f'{label} 코드 fence 균형', fences % 2 == 0, str(fences))
bad = re.findall(r'\b(?:TODO|TBD|FIXME|implement later|fill in details)\b', text, re.IGNORECASE)
check(f'{label} placeholder 부재', not bad, ', '.join(sorted(set(bad))))
required_design_terms = [
'GenericRepository',
'TransactionCompletionUnknownException',
'EvidenceAwareJpaTransactionManager',
'Application Service',
'OSIV',
'PostgreSQL 16·17·18',
'Hibernate 7.4 Collection Fetch Pagination',
'FOR UPDATE SKIP LOCKED',
'CREATE INDEX CONCURRENTLY',
'Flyway',
'Runtime·Migration·Admin',
'H2는 Local Convenience',
'전체 Transaction Retry',
'J1 Standard Persistence',
'J4 Admin / Operations',
'완료 정의',
]
for term in required_design_terms:
check(f'설계 핵심 계약: {term}', term in design)
check('GenericRepository 실제 선언 부재',
'public interface GenericRepository' not in design + plan and
'interface GenericRepository<' not in design + plan)
check('운영 ddl auto update 금지', '운영에서 `ddl-auto=update`' in plan)
check('Completion Unknown 자동 Retry 금지',
'TransactionCompletionUnknownException' in plan and
'RetryDecision.reconcile' in plan and
'Never retry completion unknown' in plan)
check('OSIV false 강제', 'spring.jpa.open-in-view must be false' in plan)
check('PG16·17·18 Matrix', 'PG_16' in plan and 'PG_17' in plan and 'PG_18' in plan)
check('Hibernate 7.4 fetch pagination gate', 'HibernateCollectionFetchPaginationContractTest' in plan)
check('Flyway snapshot upgrade gate', 'FlywayUpgradeContractTest' in plan)
check('Runtime role no DDL gate', 'runtimeRoleCanWriteRowsButCannotCreateTable' in plan)
check('Stable 계획에 Experimental create 경로 부재', '- Create: `modules/jpa-experimental/' not in plan)
stable_chunks = task_chunks(plan)
exp_chunks = task_chunks(expansion)
check('Stable Task 1~53 연속성', [n for n, _ in stable_chunks] == list(range(1, 54)), str([n for n, _ in stable_chunks]))
check('Experimental Task 1~9 연속성', [n for n, _ in exp_chunks] == list(range(1, 10)), str([n for n, _ in exp_chunks]))
check('Stable Task chunk 수', len(stable_chunks) == 53, str(len(stable_chunks)))
check('Experimental Task chunk 수', len(exp_chunks) == 9, str(len(exp_chunks)))
required_markers = [
'**Files:**',
'**Interfaces:**',
'**Implementation requirements:**',
'**Step 1:',
'**Step 2:',
'**Step 3:',
'**Step 4:',
'**Step 5:',
'Expected:',
'git commit -m',
]
for group_name, chunks in [('Stable', stable_chunks), ('Experimental', exp_chunks)]:
for number, chunk in chunks:
for marker in required_markers:
check(f'{group_name} Task {number} 필수 항목: {marker}', marker in chunk)
check(f'{group_name} Task {number} Gradle focused command', './gradlew ' in chunk and '--tests' in chunk)
check(f'{group_name} Task {number} exact path', '*' not in '\n'.join(
line for line in chunk.splitlines() if line.startswith(('- Create:', '- Modify:', '- Test:'))))
create_pattern = re.compile(r'^- Create: `([^`]+)`', re.MULTILINE)
stable_creates = create_pattern.findall(plan)
exp_creates = create_pattern.findall(expansion)
check('Stable Create 경로 중복 부재', len(stable_creates) == len(set(stable_creates)), str(len(stable_creates)))
check('Experimental Create 경로 중복 부재', len(exp_creates) == len(set(exp_creates)), str(len(exp_creates)))
check('Stable·Experimental Create 경로 충돌 부재', not (set(stable_creates) & set(exp_creates)), str(set(stable_creates) & set(exp_creates)))
check('Experimental 계획은 Stable Task 1~53 이후 시작', 'Stable 계획 Task 1~53이 완료되고' in expansion, '')
# Type/name consistency checks for high-risk cross-task contracts.
for term in [
'PersistenceOperationName',
'TransactionProfile',
'RetryProfile',
'JpaTransactionExecutor',
'JpaRetryPolicy',
'RetryDecision',
'JpaFailureContext',
'QueryName',
'KeysetPageRequest',
'KeysetSlice',
'TransactionCompletionUnknownException',
'PostgreSqlWorkClaimExecutor',
'FlywayValidationGate',
'JpaPlatformEndpoint',
]:
check(f'공통 타입 일관성: {term}', plan.count(term) >= 2, str(plan.count(term)))
check('Experimental Gradle 경로 정확성', ':modules:jpa-experimental:' in expansion)
check('Replica annotation-only routing 금지', 'readOnly=true`만으로 replica routing하지 않는다' in expansion)
check('RLS connection reuse 검증', 'pooledConnectionDoesNotLeakPriorTenantSetting' in expansion)
check('Stable 승격 ADR gate', 'BLOCKED_MISSING_ADR' in expansion)
# Source preservation check.
check('심층 리서치 원문 부록 포함', '# 부록 A. 심층 리서치 원문 보존본' in design and '# JPA 관계형 영속성 플랫폼 심층 리서치' in design)
passed = sum(1 for _, ok, _ in checks if ok)
failed = len(checks) - passed
status = 'PASS' if failed == 0 else 'FAIL'
lines = [
'# JPA Superpowers 문서 정적 검증',
'',
f'- 결과: **{status}**',
f'- 실행 검사: **{len(checks)}개**',
f'- 통과: **{passed}개**',
f'- 실패: **{failed}개**',
f'- 설계서: **{line_counts["design"]:,}행**',
f'- Stable 구현 계획서: **{line_counts["plan"]:,}행**',
f'- Experimental 확장 계획서: **{line_counts["expansion"]:,}행**',
f'- Stable Task: **{len(stable_chunks)}개**',
f'- Experimental Task: **{len(exp_chunks)}개**',
f'- Stable Create 경로: **{len(stable_creates)}개**',
f'- Experimental Create 경로: **{len(exp_creates)}개**',
f'- 설계 SHA-256: `{sha256(DESIGN)}`',
f'- Stable 계획 SHA-256: `{sha256(PLAN)}`',
f'- Experimental 계획 SHA-256: `{sha256(EXPANSION)}`',
'',
'## 검사 결과',
'',
'| 검사 | 결과 | 상세 |',
'|---|---:|---|',
]
for name, ok, detail in checks:
safe_detail = detail.replace('|', '\\|').replace('\n', ' ')[:500]
lines.append(f'| {name} | {"PASS" if ok else "FAIL"} | {safe_detail} |')
VALIDATION.write_text('\n'.join(lines) + '\n', encoding='utf-8')
print(f'{status}: {passed}/{len(checks)} checks passed')
if failed:
for name, ok, detail in checks:
if not ok:
print(f'FAIL: {name}: {detail}')
sys.exit(1)
@@ -0,0 +1,5 @@
e0703a77df8aac482823491d4f6ee43af1eace6287444a6006adb1e4f268e15c docs/superpowers/specs/2026-08-10-messaging-platform-design.md
4ad5d445f74bede368c75d482fd2adcf61ff1c2dbe93f918a4719bb1de1f7eee docs/superpowers/plans/2026-08-10-messaging-platform-implementation-plan.md
f1f8289d07a8f70c0b113349c4e14671b049c14f25d7e28f28ac71b4677960bd VALIDATION.md
3fc835294e07588cf0c854a6182e3d2fc02903aca29eae5f6f951a58524b81eb validate_messaging_docs.py
592a7cebd442da3bbc7cf822a7bacc492c412b9b84f82ea90c0d867a12f03b90 README.md
+55
View File
@@ -0,0 +1,55 @@
# Messaging Platform Superpowers Package
이 패키지는 `Java/Spring Messaging 플랫폼 심층 리서치`를 구현 기준으로 변환한 설계서와 구현 계획서다.
## 포함 파일
```text
docs/superpowers/specs/2026-08-10-messaging-platform-design.md
docs/superpowers/plans/2026-08-10-messaging-platform-implementation-plan.md
VALIDATION.md
validate_messaging_docs.py
MANIFEST.sha256
```
## 설계 경계
- 자체 `messaging-core`가 공개 계약을 소유한다.
- Kafka 4.2+/4.3.x와 RabbitMQ 4.3.x는 Stable Adapter다.
- Kafka Share Group, Pulsar 4.0 LTS·4.2, NATS JetStream 2.14.x는 Experimental Adapter다.
- 공통 `EXACTLY_ONCE` 또는 전역 순서 옵션을 제공하지 않는다.
- publish 결과는 `CONFIRMED`, `REJECTED`, `AMBIGUOUS`와 broker evidence로 표현한다.
- M1 Typed, M2 Advanced, M3 Native Capability, M4 Admin Plane을 분리한다.
- retry·DLQ·redrive에서 logical `messageId`를 유지한다.
- DLQ publish 확인 전에 source message를 settlement하지 않는다.
- Transactional Outbox, Inbox, Idempotent Consumer, Claim Check를 Reliability 모듈로 분리한다.
## 구현 가정
실제 Backend Skeleton 저장소가 입력에 포함되지 않았기 때문에 계획은 다음 기준을 사용한다.
- Java 21
- Gradle Kotlin DSL
- root package: `io.backend.skeleton.messaging`
- module root: `modules/messaging`
- PostgreSQL 16, Flyway, Spring JDBC/JPA
- JUnit 5, AssertJ, ArchUnit, Testcontainers, Toxiproxy
- Spring Framework 6.2 공통 호환선과 Spring 7.0 호환성 job
실제 저장소 구조가 다르면 파일 경로와 dependency catalog만 매핑한다. 공개 계약, 모듈 의존 방향, 신뢰성 상태 모델과 검증 기준은 그대로 유지한다.
## 문서 검증
패키지 루트에서 다음 명령을 실행한다.
```bash
./validate_messaging_docs.py \
docs/superpowers/specs/2026-08-10-messaging-platform-design.md \
docs/superpowers/plans/2026-08-10-messaging-platform-implementation-plan.md
```
`VALIDATION.md`는 문서 구조, Task 1~44 연속성, TDD 단계, 필수 설계 계약, placeholder, 중복 파일 경로를 확인한 정적 검증 결과다.
## 검증 한계
현재 검증은 설계·계획 문서에 대한 정적 검증이다. 실제 저장소가 제공되지 않아 Gradle compile, broker 통합 테스트, chaos test, 성능 인증은 실행하지 않았다. 구현 계획의 각 Task가 해당 실행 검증을 추가하도록 구성되어 있다.
+432
View File
@@ -0,0 +1,432 @@
# Messaging Superpowers 문서 정적 검증
- 설계서: `messaging-platform-design.md` — 2,608행, 74,649 bytes
- 계획서: `messaging-platform-implementation-plan.md` — 5,056행, 200,320 bytes
- Task 수: 44
- Create 경로 수: 356
- 검증 항목 수: 422
## 결과: PASS
- PASS: 설계서가 2,000행 이상이다
- PASS: 구현 계획서가 4,000행 이상이다
- PASS: 설계서 Markdown 코드 블록이 균형을 이룬다
- PASS: 계획서 Markdown 코드 블록이 균형을 이룬다
- PASS: 설계서에 unresolved todo marker가 없다
- PASS: 계획서에 unresolved todo marker가 없다
- PASS: 설계서에 unresolved tbd marker가 없다
- PASS: 계획서에 unresolved tbd marker가 없다
- PASS: 설계서에 unresolved fix marker가 없다
- PASS: 계획서에 unresolved fix marker가 없다
- PASS: 설계서에 placeholder ADR number가 없다
- PASS: 계획서에 placeholder ADR number가 없다
- PASS: 설계서에 wildcard build path가 없다
- PASS: 계획서에 wildcard build path가 없다
- PASS: 설계서에 deferred implementation phrase가 없다
- PASS: 계획서에 deferred implementation phrase가 없다
- PASS: 설계서가 필수 항목 'M1 Typed Messaging API'을 포함한다
- PASS: 설계서가 필수 항목 'M2 Advanced API'을 포함한다
- PASS: 설계서가 필수 항목 'M3 Native Capability'을 포함한다
- PASS: 설계서가 필수 항목 'M4 Admin Plane'을 포함한다
- PASS: 설계서가 필수 항목 'PublishCompletion'을 포함한다
- PASS: 설계서가 필수 항목 'AMBIGUOUS'을 포함한다
- PASS: 설계서가 필수 항목 'MessageEnvelope'을 포함한다
- PASS: 설계서가 필수 항목 'DeliveryGuarantee'을 포함한다
- PASS: 설계서가 필수 항목 'OrderingScope'을 포함한다
- PASS: 설계서가 필수 항목 'Retry Policy Engine'을 포함한다
- PASS: 설계서가 필수 항목 'DLQ·Parking·Redrive'을 포함한다
- PASS: 설계서가 필수 항목 'Kafka Stable Adapter'을 포함한다
- PASS: 설계서가 필수 항목 'RabbitMQ Stable Adapter'을 포함한다
- PASS: 설계서가 필수 항목 'Transactional Outbox'을 포함한다
- PASS: 설계서가 필수 항목 'Inbox와 Idempotent Consumer'을 포함한다
- PASS: 설계서가 필수 항목 'Claim Check'을 포함한다
- PASS: 설계서가 필수 항목 'Pulsar Experimental Adapter'을 포함한다
- PASS: 설계서가 필수 항목 'NATS JetStream Experimental Adapter'을 포함한다
- PASS: 설계서가 필수 항목 'Spring Cloud Stream Bridge'을 포함한다
- PASS: 설계서가 필수 항목 'Security'을 포함한다
- PASS: 설계서가 필수 항목 'Observability'을 포함한다
- PASS: 설계서가 필수 항목 '호환성 인증 매트릭스'을 포함한다
- PASS: 설계서가 필수 항목 '비지원 범위'을 포함한다
- PASS: 설계서가 필수 항목 '완료 정의'을 포함한다
- PASS: 공통 DeliveryGuarantee가 두 가지 보장만 선언한다
- PASS: 공통 DeliveryGuarantee enum에 EXACTLY_ONCE를 선언하지 않는다
- PASS: 공통 OrderingScope enum에 GLOBAL을 선언하지 않는다
- PASS: DLQ confirm 후 source settlement 순서를 명시한다
- PASS: retry와 reliability에서 동일 message ID를 유지한다
- PASS: Task 번호가 1부터 44까지 연속이다
- PASS: Task 1가 '**Files:**'을 포함한다
- PASS: Task 1가 '**Interfaces:**'을 포함한다
- PASS: Task 1가 'Step 1'을 포함한다
- PASS: Task 1가 'Step 2'을 포함한다
- PASS: Task 1가 'Step 3'을 포함한다
- PASS: Task 1가 'Step 4'을 포함한다
- PASS: Task 1가 'Step 5'을 포함한다
- PASS: Task 1가 'git commit -m'을 포함한다
- PASS: Task 2가 '**Files:**'을 포함한다
- PASS: Task 2가 '**Interfaces:**'을 포함한다
- PASS: Task 2가 'Step 1'을 포함한다
- PASS: Task 2가 'Step 2'을 포함한다
- PASS: Task 2가 'Step 3'을 포함한다
- PASS: Task 2가 'Step 4'을 포함한다
- PASS: Task 2가 'Step 5'을 포함한다
- PASS: Task 2가 'git commit -m'을 포함한다
- PASS: Task 3가 '**Files:**'을 포함한다
- PASS: Task 3가 '**Interfaces:**'을 포함한다
- PASS: Task 3가 'Step 1'을 포함한다
- PASS: Task 3가 'Step 2'을 포함한다
- PASS: Task 3가 'Step 3'을 포함한다
- PASS: Task 3가 'Step 4'을 포함한다
- PASS: Task 3가 'Step 5'을 포함한다
- PASS: Task 3가 'git commit -m'을 포함한다
- PASS: Task 4가 '**Files:**'을 포함한다
- PASS: Task 4가 '**Interfaces:**'을 포함한다
- PASS: Task 4가 'Step 1'을 포함한다
- PASS: Task 4가 'Step 2'을 포함한다
- PASS: Task 4가 'Step 3'을 포함한다
- PASS: Task 4가 'Step 4'을 포함한다
- PASS: Task 4가 'Step 5'을 포함한다
- PASS: Task 4가 'git commit -m'을 포함한다
- PASS: Task 5가 '**Files:**'을 포함한다
- PASS: Task 5가 '**Interfaces:**'을 포함한다
- PASS: Task 5가 'Step 1'을 포함한다
- PASS: Task 5가 'Step 2'을 포함한다
- PASS: Task 5가 'Step 3'을 포함한다
- PASS: Task 5가 'Step 4'을 포함한다
- PASS: Task 5가 'Step 5'을 포함한다
- PASS: Task 5가 'git commit -m'을 포함한다
- PASS: Task 6가 '**Files:**'을 포함한다
- PASS: Task 6가 '**Interfaces:**'을 포함한다
- PASS: Task 6가 'Step 1'을 포함한다
- PASS: Task 6가 'Step 2'을 포함한다
- PASS: Task 6가 'Step 3'을 포함한다
- PASS: Task 6가 'Step 4'을 포함한다
- PASS: Task 6가 'Step 5'을 포함한다
- PASS: Task 6가 'git commit -m'을 포함한다
- PASS: Task 7가 '**Files:**'을 포함한다
- PASS: Task 7가 '**Interfaces:**'을 포함한다
- PASS: Task 7가 'Step 1'을 포함한다
- PASS: Task 7가 'Step 2'을 포함한다
- PASS: Task 7가 'Step 3'을 포함한다
- PASS: Task 7가 'Step 4'을 포함한다
- PASS: Task 7가 'Step 5'을 포함한다
- PASS: Task 7가 'git commit -m'을 포함한다
- PASS: Task 8가 '**Files:**'을 포함한다
- PASS: Task 8가 '**Interfaces:**'을 포함한다
- PASS: Task 8가 'Step 1'을 포함한다
- PASS: Task 8가 'Step 2'을 포함한다
- PASS: Task 8가 'Step 3'을 포함한다
- PASS: Task 8가 'Step 4'을 포함한다
- PASS: Task 8가 'Step 5'을 포함한다
- PASS: Task 8가 'git commit -m'을 포함한다
- PASS: Task 9가 '**Files:**'을 포함한다
- PASS: Task 9가 '**Interfaces:**'을 포함한다
- PASS: Task 9가 'Step 1'을 포함한다
- PASS: Task 9가 'Step 2'을 포함한다
- PASS: Task 9가 'Step 3'을 포함한다
- PASS: Task 9가 'Step 4'을 포함한다
- PASS: Task 9가 'Step 5'을 포함한다
- PASS: Task 9가 'git commit -m'을 포함한다
- PASS: Task 10가 '**Files:**'을 포함한다
- PASS: Task 10가 '**Interfaces:**'을 포함한다
- PASS: Task 10가 'Step 1'을 포함한다
- PASS: Task 10가 'Step 2'을 포함한다
- PASS: Task 10가 'Step 3'을 포함한다
- PASS: Task 10가 'Step 4'을 포함한다
- PASS: Task 10가 'Step 5'을 포함한다
- PASS: Task 10가 'git commit -m'을 포함한다
- PASS: Task 11가 '**Files:**'을 포함한다
- PASS: Task 11가 '**Interfaces:**'을 포함한다
- PASS: Task 11가 'Step 1'을 포함한다
- PASS: Task 11가 'Step 2'을 포함한다
- PASS: Task 11가 'Step 3'을 포함한다
- PASS: Task 11가 'Step 4'을 포함한다
- PASS: Task 11가 'Step 5'을 포함한다
- PASS: Task 11가 'git commit -m'을 포함한다
- PASS: Task 12가 '**Files:**'을 포함한다
- PASS: Task 12가 '**Interfaces:**'을 포함한다
- PASS: Task 12가 'Step 1'을 포함한다
- PASS: Task 12가 'Step 2'을 포함한다
- PASS: Task 12가 'Step 3'을 포함한다
- PASS: Task 12가 'Step 4'을 포함한다
- PASS: Task 12가 'Step 5'을 포함한다
- PASS: Task 12가 'git commit -m'을 포함한다
- PASS: Task 13가 '**Files:**'을 포함한다
- PASS: Task 13가 '**Interfaces:**'을 포함한다
- PASS: Task 13가 'Step 1'을 포함한다
- PASS: Task 13가 'Step 2'을 포함한다
- PASS: Task 13가 'Step 3'을 포함한다
- PASS: Task 13가 'Step 4'을 포함한다
- PASS: Task 13가 'Step 5'을 포함한다
- PASS: Task 13가 'git commit -m'을 포함한다
- PASS: Task 14가 '**Files:**'을 포함한다
- PASS: Task 14가 '**Interfaces:**'을 포함한다
- PASS: Task 14가 'Step 1'을 포함한다
- PASS: Task 14가 'Step 2'을 포함한다
- PASS: Task 14가 'Step 3'을 포함한다
- PASS: Task 14가 'Step 4'을 포함한다
- PASS: Task 14가 'Step 5'을 포함한다
- PASS: Task 14가 'git commit -m'을 포함한다
- PASS: Task 15가 '**Files:**'을 포함한다
- PASS: Task 15가 '**Interfaces:**'을 포함한다
- PASS: Task 15가 'Step 1'을 포함한다
- PASS: Task 15가 'Step 2'을 포함한다
- PASS: Task 15가 'Step 3'을 포함한다
- PASS: Task 15가 'Step 4'을 포함한다
- PASS: Task 15가 'Step 5'을 포함한다
- PASS: Task 15가 'git commit -m'을 포함한다
- PASS: Task 16가 '**Files:**'을 포함한다
- PASS: Task 16가 '**Interfaces:**'을 포함한다
- PASS: Task 16가 'Step 1'을 포함한다
- PASS: Task 16가 'Step 2'을 포함한다
- PASS: Task 16가 'Step 3'을 포함한다
- PASS: Task 16가 'Step 4'을 포함한다
- PASS: Task 16가 'Step 5'을 포함한다
- PASS: Task 16가 'git commit -m'을 포함한다
- PASS: Task 17가 '**Files:**'을 포함한다
- PASS: Task 17가 '**Interfaces:**'을 포함한다
- PASS: Task 17가 'Step 1'을 포함한다
- PASS: Task 17가 'Step 2'을 포함한다
- PASS: Task 17가 'Step 3'을 포함한다
- PASS: Task 17가 'Step 4'을 포함한다
- PASS: Task 17가 'Step 5'을 포함한다
- PASS: Task 17가 'git commit -m'을 포함한다
- PASS: Task 18가 '**Files:**'을 포함한다
- PASS: Task 18가 '**Interfaces:**'을 포함한다
- PASS: Task 18가 'Step 1'을 포함한다
- PASS: Task 18가 'Step 2'을 포함한다
- PASS: Task 18가 'Step 3'을 포함한다
- PASS: Task 18가 'Step 4'을 포함한다
- PASS: Task 18가 'Step 5'을 포함한다
- PASS: Task 18가 'git commit -m'을 포함한다
- PASS: Task 19가 '**Files:**'을 포함한다
- PASS: Task 19가 '**Interfaces:**'을 포함한다
- PASS: Task 19가 'Step 1'을 포함한다
- PASS: Task 19가 'Step 2'을 포함한다
- PASS: Task 19가 'Step 3'을 포함한다
- PASS: Task 19가 'Step 4'을 포함한다
- PASS: Task 19가 'Step 5'을 포함한다
- PASS: Task 19가 'git commit -m'을 포함한다
- PASS: Task 20가 '**Files:**'을 포함한다
- PASS: Task 20가 '**Interfaces:**'을 포함한다
- PASS: Task 20가 'Step 1'을 포함한다
- PASS: Task 20가 'Step 2'을 포함한다
- PASS: Task 20가 'Step 3'을 포함한다
- PASS: Task 20가 'Step 4'을 포함한다
- PASS: Task 20가 'Step 5'을 포함한다
- PASS: Task 20가 'git commit -m'을 포함한다
- PASS: Task 21가 '**Files:**'을 포함한다
- PASS: Task 21가 '**Interfaces:**'을 포함한다
- PASS: Task 21가 'Step 1'을 포함한다
- PASS: Task 21가 'Step 2'을 포함한다
- PASS: Task 21가 'Step 3'을 포함한다
- PASS: Task 21가 'Step 4'을 포함한다
- PASS: Task 21가 'Step 5'을 포함한다
- PASS: Task 21가 'git commit -m'을 포함한다
- PASS: Task 22가 '**Files:**'을 포함한다
- PASS: Task 22가 '**Interfaces:**'을 포함한다
- PASS: Task 22가 'Step 1'을 포함한다
- PASS: Task 22가 'Step 2'을 포함한다
- PASS: Task 22가 'Step 3'을 포함한다
- PASS: Task 22가 'Step 4'을 포함한다
- PASS: Task 22가 'Step 5'을 포함한다
- PASS: Task 22가 'git commit -m'을 포함한다
- PASS: Task 23가 '**Files:**'을 포함한다
- PASS: Task 23가 '**Interfaces:**'을 포함한다
- PASS: Task 23가 'Step 1'을 포함한다
- PASS: Task 23가 'Step 2'을 포함한다
- PASS: Task 23가 'Step 3'을 포함한다
- PASS: Task 23가 'Step 4'을 포함한다
- PASS: Task 23가 'Step 5'을 포함한다
- PASS: Task 23가 'git commit -m'을 포함한다
- PASS: Task 24가 '**Files:**'을 포함한다
- PASS: Task 24가 '**Interfaces:**'을 포함한다
- PASS: Task 24가 'Step 1'을 포함한다
- PASS: Task 24가 'Step 2'을 포함한다
- PASS: Task 24가 'Step 3'을 포함한다
- PASS: Task 24가 'Step 4'을 포함한다
- PASS: Task 24가 'Step 5'을 포함한다
- PASS: Task 24가 'git commit -m'을 포함한다
- PASS: Task 25가 '**Files:**'을 포함한다
- PASS: Task 25가 '**Interfaces:**'을 포함한다
- PASS: Task 25가 'Step 1'을 포함한다
- PASS: Task 25가 'Step 2'을 포함한다
- PASS: Task 25가 'Step 3'을 포함한다
- PASS: Task 25가 'Step 4'을 포함한다
- PASS: Task 25가 'Step 5'을 포함한다
- PASS: Task 25가 'git commit -m'을 포함한다
- PASS: Task 26가 '**Files:**'을 포함한다
- PASS: Task 26가 '**Interfaces:**'을 포함한다
- PASS: Task 26가 'Step 1'을 포함한다
- PASS: Task 26가 'Step 2'을 포함한다
- PASS: Task 26가 'Step 3'을 포함한다
- PASS: Task 26가 'Step 4'을 포함한다
- PASS: Task 26가 'Step 5'을 포함한다
- PASS: Task 26가 'git commit -m'을 포함한다
- PASS: Task 27가 '**Files:**'을 포함한다
- PASS: Task 27가 '**Interfaces:**'을 포함한다
- PASS: Task 27가 'Step 1'을 포함한다
- PASS: Task 27가 'Step 2'을 포함한다
- PASS: Task 27가 'Step 3'을 포함한다
- PASS: Task 27가 'Step 4'을 포함한다
- PASS: Task 27가 'Step 5'을 포함한다
- PASS: Task 27가 'git commit -m'을 포함한다
- PASS: Task 28가 '**Files:**'을 포함한다
- PASS: Task 28가 '**Interfaces:**'을 포함한다
- PASS: Task 28가 'Step 1'을 포함한다
- PASS: Task 28가 'Step 2'을 포함한다
- PASS: Task 28가 'Step 3'을 포함한다
- PASS: Task 28가 'Step 4'을 포함한다
- PASS: Task 28가 'Step 5'을 포함한다
- PASS: Task 28가 'git commit -m'을 포함한다
- PASS: Task 29가 '**Files:**'을 포함한다
- PASS: Task 29가 '**Interfaces:**'을 포함한다
- PASS: Task 29가 'Step 1'을 포함한다
- PASS: Task 29가 'Step 2'을 포함한다
- PASS: Task 29가 'Step 3'을 포함한다
- PASS: Task 29가 'Step 4'을 포함한다
- PASS: Task 29가 'Step 5'을 포함한다
- PASS: Task 29가 'git commit -m'을 포함한다
- PASS: Task 30가 '**Files:**'을 포함한다
- PASS: Task 30가 '**Interfaces:**'을 포함한다
- PASS: Task 30가 'Step 1'을 포함한다
- PASS: Task 30가 'Step 2'을 포함한다
- PASS: Task 30가 'Step 3'을 포함한다
- PASS: Task 30가 'Step 4'을 포함한다
- PASS: Task 30가 'Step 5'을 포함한다
- PASS: Task 30가 'git commit -m'을 포함한다
- PASS: Task 31가 '**Files:**'을 포함한다
- PASS: Task 31가 '**Interfaces:**'을 포함한다
- PASS: Task 31가 'Step 1'을 포함한다
- PASS: Task 31가 'Step 2'을 포함한다
- PASS: Task 31가 'Step 3'을 포함한다
- PASS: Task 31가 'Step 4'을 포함한다
- PASS: Task 31가 'Step 5'을 포함한다
- PASS: Task 31가 'git commit -m'을 포함한다
- PASS: Task 32가 '**Files:**'을 포함한다
- PASS: Task 32가 '**Interfaces:**'을 포함한다
- PASS: Task 32가 'Step 1'을 포함한다
- PASS: Task 32가 'Step 2'을 포함한다
- PASS: Task 32가 'Step 3'을 포함한다
- PASS: Task 32가 'Step 4'을 포함한다
- PASS: Task 32가 'Step 5'을 포함한다
- PASS: Task 32가 'git commit -m'을 포함한다
- PASS: Task 33가 '**Files:**'을 포함한다
- PASS: Task 33가 '**Interfaces:**'을 포함한다
- PASS: Task 33가 'Step 1'을 포함한다
- PASS: Task 33가 'Step 2'을 포함한다
- PASS: Task 33가 'Step 3'을 포함한다
- PASS: Task 33가 'Step 4'을 포함한다
- PASS: Task 33가 'Step 5'을 포함한다
- PASS: Task 33가 'git commit -m'을 포함한다
- PASS: Task 34가 '**Files:**'을 포함한다
- PASS: Task 34가 '**Interfaces:**'을 포함한다
- PASS: Task 34가 'Step 1'을 포함한다
- PASS: Task 34가 'Step 2'을 포함한다
- PASS: Task 34가 'Step 3'을 포함한다
- PASS: Task 34가 'Step 4'을 포함한다
- PASS: Task 34가 'Step 5'을 포함한다
- PASS: Task 34가 'git commit -m'을 포함한다
- PASS: Task 35가 '**Files:**'을 포함한다
- PASS: Task 35가 '**Interfaces:**'을 포함한다
- PASS: Task 35가 'Step 1'을 포함한다
- PASS: Task 35가 'Step 2'을 포함한다
- PASS: Task 35가 'Step 3'을 포함한다
- PASS: Task 35가 'Step 4'을 포함한다
- PASS: Task 35가 'Step 5'을 포함한다
- PASS: Task 35가 'git commit -m'을 포함한다
- PASS: Task 36가 '**Files:**'을 포함한다
- PASS: Task 36가 '**Interfaces:**'을 포함한다
- PASS: Task 36가 'Step 1'을 포함한다
- PASS: Task 36가 'Step 2'을 포함한다
- PASS: Task 36가 'Step 3'을 포함한다
- PASS: Task 36가 'Step 4'을 포함한다
- PASS: Task 36가 'Step 5'을 포함한다
- PASS: Task 36가 'git commit -m'을 포함한다
- PASS: Task 37가 '**Files:**'을 포함한다
- PASS: Task 37가 '**Interfaces:**'을 포함한다
- PASS: Task 37가 'Step 1'을 포함한다
- PASS: Task 37가 'Step 2'을 포함한다
- PASS: Task 37가 'Step 3'을 포함한다
- PASS: Task 37가 'Step 4'을 포함한다
- PASS: Task 37가 'Step 5'을 포함한다
- PASS: Task 37가 'git commit -m'을 포함한다
- PASS: Task 38가 '**Files:**'을 포함한다
- PASS: Task 38가 '**Interfaces:**'을 포함한다
- PASS: Task 38가 'Step 1'을 포함한다
- PASS: Task 38가 'Step 2'을 포함한다
- PASS: Task 38가 'Step 3'을 포함한다
- PASS: Task 38가 'Step 4'을 포함한다
- PASS: Task 38가 'Step 5'을 포함한다
- PASS: Task 38가 'git commit -m'을 포함한다
- PASS: Task 39가 '**Files:**'을 포함한다
- PASS: Task 39가 '**Interfaces:**'을 포함한다
- PASS: Task 39가 'Step 1'을 포함한다
- PASS: Task 39가 'Step 2'을 포함한다
- PASS: Task 39가 'Step 3'을 포함한다
- PASS: Task 39가 'Step 4'을 포함한다
- PASS: Task 39가 'Step 5'을 포함한다
- PASS: Task 39가 'git commit -m'을 포함한다
- PASS: Task 40가 '**Files:**'을 포함한다
- PASS: Task 40가 '**Interfaces:**'을 포함한다
- PASS: Task 40가 'Step 1'을 포함한다
- PASS: Task 40가 'Step 2'을 포함한다
- PASS: Task 40가 'Step 3'을 포함한다
- PASS: Task 40가 'Step 4'을 포함한다
- PASS: Task 40가 'Step 5'을 포함한다
- PASS: Task 40가 'git commit -m'을 포함한다
- PASS: Task 41가 '**Files:**'을 포함한다
- PASS: Task 41가 '**Interfaces:**'을 포함한다
- PASS: Task 41가 'Step 1'을 포함한다
- PASS: Task 41가 'Step 2'을 포함한다
- PASS: Task 41가 'Step 3'을 포함한다
- PASS: Task 41가 'Step 4'을 포함한다
- PASS: Task 41가 'Step 5'을 포함한다
- PASS: Task 41가 'git commit -m'을 포함한다
- PASS: Task 42가 '**Files:**'을 포함한다
- PASS: Task 42가 '**Interfaces:**'을 포함한다
- PASS: Task 42가 'Step 1'을 포함한다
- PASS: Task 42가 'Step 2'을 포함한다
- PASS: Task 42가 'Step 3'을 포함한다
- PASS: Task 42가 'Step 4'을 포함한다
- PASS: Task 42가 'Step 5'을 포함한다
- PASS: Task 42가 'git commit -m'을 포함한다
- PASS: Task 43가 '**Files:**'을 포함한다
- PASS: Task 43가 '**Interfaces:**'을 포함한다
- PASS: Task 43가 'Step 1'을 포함한다
- PASS: Task 43가 'Step 2'을 포함한다
- PASS: Task 43가 'Step 3'을 포함한다
- PASS: Task 43가 'Step 4'을 포함한다
- PASS: Task 43가 'Step 5'을 포함한다
- PASS: Task 43가 'git commit -m'을 포함한다
- PASS: Task 44가 '**Files:**'을 포함한다
- PASS: Task 44가 '**Interfaces:**'을 포함한다
- PASS: Task 44가 'Step 1'을 포함한다
- PASS: Task 44가 'Step 2'을 포함한다
- PASS: Task 44가 'Step 3'을 포함한다
- PASS: Task 44가 'Step 4'을 포함한다
- PASS: Task 44가 'Step 5'을 포함한다
- PASS: Task 44가 'git commit -m'을 포함한다
- PASS: 중복된 Create 파일 경로가 없다
- PASS: Create 파일 경로에 wildcard가 없다
- PASS: 계획서가 필수 작업 'Kafka Producer Adapter와 Publish Evidence'을 포함한다
- PASS: 계획서가 필수 작업 'Kafka Consumer Group, Partition Coordinator'을 포함한다
- PASS: 계획서가 필수 작업 'Kafka Native Transaction Capability'을 포함한다
- PASS: 계획서가 필수 작업 'Kafka Share Group Experimental Adapter'을 포함한다
- PASS: 계획서가 필수 작업 'Rabbit Publisher Confirm·Return Evidence Adapter'을 포함한다
- PASS: 계획서가 필수 작업 'Rabbit Consumer Manual ACK'을 포함한다
- PASS: 계획서가 필수 작업 'Transactional Outbox Repository'을 포함한다
- PASS: 계획서가 필수 작업 'Inbox Transactional Idempotent Consumer'을 포함한다
- PASS: 계획서가 필수 작업 'Debezium Outbox Event Router'을 포함한다
- PASS: 계획서가 필수 작업 'Pulsar Experimental Adapter'을 포함한다
- PASS: 계획서가 필수 작업 'NATS JetStream Experimental Adapter'을 포함한다
- PASS: 계획서가 필수 작업 'Spring Cloud Stream Optional Bridge'을 포함한다
- PASS: 계획서가 필수 작업 'Global Backpressure'을 포함한다
- PASS: 계획서가 필수 작업 'Cross-broker 장애·보안·Reliability Contract Suite'을 포함한다
- PASS: 계획서가 필수 작업 '성능 인증, Compatibility Matrix'을 포함한다
- PASS: 계획서가 필수 작업 '지원 문서, Runbook, ADR, Release Gate'을 포함한다
- PASS: 계획서가 message identity 보존을 명시한다
- PASS: 계획서가 DLQ 실패 시 source ACK 금지를 명시한다
- PASS: 계획서가 credential 분리를 명시한다
- PASS: Stable chaos aggregate task가 계획에 존재한다
- PASS: performance aggregate task가 계획에 존재한다
- PASS: compatibility aggregate task가 계획에 존재한다
+164
View File
@@ -0,0 +1,164 @@
#!/usr/bin/env python3
from __future__ import annotations
import re
import sys
from collections import Counter
from pathlib import Path
DESIGN = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("messaging-platform-design.md")
PLAN = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("messaging-platform-implementation-plan.md")
errors: list[str] = []
checks: list[str] = []
def require(condition: bool, message: str) -> None:
if condition:
checks.append(message)
else:
errors.append(message)
def balanced_fences(text: str) -> bool:
return len(re.findall(r"^```", text, re.MULTILINE)) % 2 == 0
def line_count(text: str) -> int:
return len(text.splitlines())
design = DESIGN.read_text(encoding="utf-8")
plan = PLAN.read_text(encoding="utf-8")
require(line_count(design) >= 2_000, "설계서가 2,000행 이상이다")
require(line_count(plan) >= 4_000, "구현 계획서가 4,000행 이상이다")
require(balanced_fences(design), "설계서 Markdown 코드 블록이 균형을 이룬다")
require(balanced_fences(plan), "계획서 Markdown 코드 블록이 균형을 이룬다")
placeholder_patterns = {
"unresolved todo marker": r"\bT[O]DO\b",
"unresolved tbd marker": r"\bT[B]D\b",
"unresolved fix marker": r"\bF[I]XME\b",
"placeholder ADR number": r"ADR-X{2,}",
"wildcard build path": r"modules/messaging/\*/build\.gradle\.kts",
"deferred implementation phrase": r"implement\s+later|fill\s+in\s+details|similar\s+to\s+Task",
}
for name, pattern in placeholder_patterns.items():
require(not re.search(pattern, design, re.IGNORECASE), f"설계서에 {name}가 없다")
require(not re.search(pattern, plan, re.IGNORECASE), f"계획서에 {name}가 없다")
required_design_terms = [
"M1 Typed Messaging API",
"M2 Advanced API",
"M3 Native Capability",
"M4 Admin Plane",
"PublishCompletion",
"AMBIGUOUS",
"MessageEnvelope",
"DeliveryGuarantee",
"OrderingScope",
"Retry Policy Engine",
"DLQ·Parking·Redrive",
"Kafka Stable Adapter",
"RabbitMQ Stable Adapter",
"Transactional Outbox",
"Inbox와 Idempotent Consumer",
"Claim Check",
"Pulsar Experimental Adapter",
"NATS JetStream Experimental Adapter",
"Spring Cloud Stream Bridge",
"Security",
"Observability",
"호환성 인증 매트릭스",
"비지원 범위",
"완료 정의",
]
for term in required_design_terms:
require(term in design, f"설계서가 필수 항목 '{term}'을 포함한다")
require("AT_MOST_ONCE,\n AT_LEAST_ONCE" in design, "공통 DeliveryGuarantee가 두 가지 보장만 선언한다")
delivery_match = re.search(r"public enum DeliveryGuarantee \{(?P<body>.*?)\n\}", design, re.DOTALL)
ordering_match = re.search(r"public enum OrderingScope \{(?P<body>.*?)\n\}", design, re.DOTALL)
require(delivery_match is not None and "EXACTLY_ONCE" not in delivery_match.group("body"), "공통 DeliveryGuarantee enum에 EXACTLY_ONCE를 선언하지 않는다")
require(ordering_match is not None and "GLOBAL" not in ordering_match.group("body"), "공통 OrderingScope enum에 GLOBAL을 선언하지 않는다")
require("DLQ broker confirmation 확인\n→ source settlement" in design, "DLQ confirm 후 source settlement 순서를 명시한다")
require("같은 `messageId`" in design, "retry와 reliability에서 동일 message ID를 유지한다")
# Plan task structure.
task_numbers = [int(value) for value in re.findall(r"^### Task (\d+):", plan, re.MULTILINE)]
require(task_numbers == list(range(1, 45)), "Task 번호가 1부터 44까지 연속이다")
for task_number in task_numbers:
start = plan.index(f"### Task {task_number}:")
end = (
plan.index(f"### Task {task_number + 1}:", start)
if task_number < 44
else plan.index("## 3. Plan Self-Review Checklist", start)
)
section = plan[start:end]
for required in (
"**Files:**",
"**Interfaces:**",
"Step 1",
"Step 2",
"Step 3",
"Step 4",
"Step 5",
"git commit -m",
):
require(required in section, f"Task {task_number}'{required}'을 포함한다")
create_paths = re.findall(r"^- Create: `([^`]+)`", plan, re.MULTILINE)
duplicates = [path for path, count in Counter(create_paths).items() if count > 1]
require(not duplicates, "중복된 Create 파일 경로가 없다")
require(all("*" not in path for path in create_paths), "Create 파일 경로에 wildcard가 없다")
required_plan_terms = [
"Kafka Producer Adapter와 Publish Evidence",
"Kafka Consumer Group, Partition Coordinator",
"Kafka Native Transaction Capability",
"Kafka Share Group Experimental Adapter",
"Rabbit Publisher Confirm·Return Evidence Adapter",
"Rabbit Consumer Manual ACK",
"Transactional Outbox Repository",
"Inbox Transactional Idempotent Consumer",
"Debezium Outbox Event Router",
"Pulsar Experimental Adapter",
"NATS JetStream Experimental Adapter",
"Spring Cloud Stream Optional Bridge",
"Global Backpressure",
"Cross-broker 장애·보안·Reliability Contract Suite",
"성능 인증, Compatibility Matrix",
"지원 문서, Runbook, ADR, Release Gate",
]
for term in required_plan_terms:
require(term in plan, f"계획서가 필수 작업 '{term}'을 포함한다")
require("messageId`를 유지" in plan or "message ID를 유지" in plan, "계획서가 message identity 보존을 명시한다")
require("source를 ACK하지 않는다" in plan or "source ACK하지 않는다" in plan, "계획서가 DLQ 실패 시 source ACK 금지를 명시한다")
require("producer, consumer, admin credential" in plan, "계획서가 credential 분리를 명시한다")
require("messagingStableChaos" in plan, "Stable chaos aggregate task가 계획에 존재한다")
require("messagingPerformance" in plan, "performance aggregate task가 계획에 존재한다")
require("messagingCompatibility" in plan, "compatibility aggregate task가 계획에 존재한다")
print("# Messaging Superpowers 문서 정적 검증")
print()
print(f"- 설계서: `{DESIGN}` — {line_count(design):,}행, {len(design.encode('utf-8')):,} bytes")
print(f"- 계획서: `{PLAN}` — {line_count(plan):,}행, {len(plan.encode('utf-8')):,} bytes")
print(f"- Task 수: {len(task_numbers)}")
print(f"- Create 경로 수: {len(create_paths)}")
print(f"- 검증 항목 수: {len(checks) + len(errors)}")
print()
if errors:
print("## 결과: FAIL")
print()
for error in errors:
print(f"- FAIL: {error}")
sys.exit(1)
print("## 결과: PASS")
print()
for check in checks:
print(f"- PASS: {check}")
@@ -0,0 +1,6 @@
a6588890cf1eed348dc6d679515d0a403839416945a40fac1369963f7ee58167 docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md
d2897d321ed6868e46f02f5e5d327425a48ff285b532d10923323477a395d6de docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md
f677e44fcf138b38154c27230d6322d4c18919f5960fc30c07f03cea40758acd docs/superpowers/plans/2026-08-11-mongodb-advanced-capabilities-expansion-plan.md
e8fa3d09372956ad65a1984c2fd3b4d30c34896593a34bc40eb5d147cbc593a8 README.md
d196c4e4c1510273e29f9f7267ab74bce32f3da59a40a3d6f1615b9c32428745 VALIDATION.md
a5e807fd4416a718818729ea37222cdf104c58962cac5154e1e5976011b03f12 validate_mongodb_docs.py
+42
View File
@@ -0,0 +1,42 @@
# MongoDB Superpowers 문서 패키지
이 패키지는 첨부된 `MongoDB 문서 영속성 플랫폼 심층 리서치`를 요구사항 원본으로 사용해 작성한 설계서와 구현 계획서다.
## 파일
```text
docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md
docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md
docs/superpowers/plans/2026-08-11-mongodb-advanced-capabilities-expansion-plan.md
VALIDATION.md
validate_mongodb_docs.py
MANIFEST.sha256
```
## 적용 가정
```text
Java 21
Gradle Kotlin DSL
Spring Boot 4.1 BOM
Spring Data MongoDB 5.1
Boot-managed MongoDB Java Driver
MongoDB 8.0 primary certification
MongoDB 7.0 compatibility
root package: io.backend.skeleton.mongodb
stable modules: modules/mongodb
advanced modules: modules/mongodb-advanced
```
실제 Backend Skeleton 저장소 구조가 입력에 포함되지 않았으므로 파일 경로는 위 가정에 따라 확정했다. 적용 시 기존 version catalog와 공통 build logic에 경로만 매핑하고 공개 계약과 신뢰성 의미론은 유지한다.
## 실행 순서
```text
Stable Implementation Plan Task 1~50
→ Stable Release Gate
→ Advanced Plan Task 1~15
→ capability별 Promotion Gate
```
Stable 구현 전에 Advanced 모듈을 Starter에 추가하지 않는다.
+737
View File
@@ -0,0 +1,737 @@
# MongoDB Superpowers 문서 정적 검증 결과
## 결과
```text
SUMMARY | total=687 pass=687 fail=0
```
| 항목 | 값 |
|---|---:|
| 설계서 행 수 | 1303 |
| Stable 구현 계획서 행 수 | 3943 |
| Advanced 구현 계획서 행 수 | 1139 |
| Stable Task | 50 |
| Advanced Task | 15 |
| Stable Create 경로 | 252 |
| Advanced Create 경로 | 77 |
## 검증 범위
```text
설계 핵심 결정 존재
Task 번호 연속성
모든 Task의 Files·Interfaces·Implementation requirements
모든 Task의 Step 1~5
Task별 실패·통과 예상 결과와 Commit
Create 경로 중복·충돌
Markdown code fence 균형
TODO·TBD·FIXME 부재
범용 Mongo Repository 실제 선언 부재
Runtime arbitrary runCommand public API 부재
Stable API V1 strict와 Replica Set 기준
Transaction body retry와 commit retry 분리
Change Stream at-least-once 계약
TTL cleanup-only 계약
GridFS compatibility-only 계약
Stable·Advanced dependency 격리
MongoDB 7.0·8.0 호환 Matrix
Advanced actual topology promotion gate
```
## 전체 검사 로그
```text
PASS | design exists
PASS | stable plan exists
PASS | advanced plan exists
PASS | design purpose
PASS | domain ownership
PASS | no generic repository design
PASS | stable api strict
PASS | local replica set
PASS | standalone smoke only
PASS | bson manifest
PASS | transaction retry separation
PASS | change stream at least once
PASS | ttl cleanup only
PASS | gridfs compatibility only
PASS | driver native observability
PASS | stable task count | 50
PASS | stable task sequence | [1, 2, 3]...[48, 49, 50]
PASS | stable task 1 has **Files:**
PASS | stable task 1 has **Interfaces:**
PASS | stable task 1 has **Implementation requirements:**
PASS | stable task 1 has **Step 1:
PASS | stable task 1 has **Step 2:
PASS | stable task 1 has **Step 3:
PASS | stable task 1 has **Step 4:
PASS | stable task 1 has **Step 5:
PASS | stable task 1 has commit
PASS | stable task 1 has expected result
PASS | stable task 2 has **Files:**
PASS | stable task 2 has **Interfaces:**
PASS | stable task 2 has **Implementation requirements:**
PASS | stable task 2 has **Step 1:
PASS | stable task 2 has **Step 2:
PASS | stable task 2 has **Step 3:
PASS | stable task 2 has **Step 4:
PASS | stable task 2 has **Step 5:
PASS | stable task 2 has commit
PASS | stable task 2 has expected result
PASS | stable task 3 has **Files:**
PASS | stable task 3 has **Interfaces:**
PASS | stable task 3 has **Implementation requirements:**
PASS | stable task 3 has **Step 1:
PASS | stable task 3 has **Step 2:
PASS | stable task 3 has **Step 3:
PASS | stable task 3 has **Step 4:
PASS | stable task 3 has **Step 5:
PASS | stable task 3 has commit
PASS | stable task 3 has expected result
PASS | stable task 4 has **Files:**
PASS | stable task 4 has **Interfaces:**
PASS | stable task 4 has **Implementation requirements:**
PASS | stable task 4 has **Step 1:
PASS | stable task 4 has **Step 2:
PASS | stable task 4 has **Step 3:
PASS | stable task 4 has **Step 4:
PASS | stable task 4 has **Step 5:
PASS | stable task 4 has commit
PASS | stable task 4 has expected result
PASS | stable task 5 has **Files:**
PASS | stable task 5 has **Interfaces:**
PASS | stable task 5 has **Implementation requirements:**
PASS | stable task 5 has **Step 1:
PASS | stable task 5 has **Step 2:
PASS | stable task 5 has **Step 3:
PASS | stable task 5 has **Step 4:
PASS | stable task 5 has **Step 5:
PASS | stable task 5 has commit
PASS | stable task 5 has expected result
PASS | stable task 6 has **Files:**
PASS | stable task 6 has **Interfaces:**
PASS | stable task 6 has **Implementation requirements:**
PASS | stable task 6 has **Step 1:
PASS | stable task 6 has **Step 2:
PASS | stable task 6 has **Step 3:
PASS | stable task 6 has **Step 4:
PASS | stable task 6 has **Step 5:
PASS | stable task 6 has commit
PASS | stable task 6 has expected result
PASS | stable task 7 has **Files:**
PASS | stable task 7 has **Interfaces:**
PASS | stable task 7 has **Implementation requirements:**
PASS | stable task 7 has **Step 1:
PASS | stable task 7 has **Step 2:
PASS | stable task 7 has **Step 3:
PASS | stable task 7 has **Step 4:
PASS | stable task 7 has **Step 5:
PASS | stable task 7 has commit
PASS | stable task 7 has expected result
PASS | stable task 8 has **Files:**
PASS | stable task 8 has **Interfaces:**
PASS | stable task 8 has **Implementation requirements:**
PASS | stable task 8 has **Step 1:
PASS | stable task 8 has **Step 2:
PASS | stable task 8 has **Step 3:
PASS | stable task 8 has **Step 4:
PASS | stable task 8 has **Step 5:
PASS | stable task 8 has commit
PASS | stable task 8 has expected result
PASS | stable task 9 has **Files:**
PASS | stable task 9 has **Interfaces:**
PASS | stable task 9 has **Implementation requirements:**
PASS | stable task 9 has **Step 1:
PASS | stable task 9 has **Step 2:
PASS | stable task 9 has **Step 3:
PASS | stable task 9 has **Step 4:
PASS | stable task 9 has **Step 5:
PASS | stable task 9 has commit
PASS | stable task 9 has expected result
PASS | stable task 10 has **Files:**
PASS | stable task 10 has **Interfaces:**
PASS | stable task 10 has **Implementation requirements:**
PASS | stable task 10 has **Step 1:
PASS | stable task 10 has **Step 2:
PASS | stable task 10 has **Step 3:
PASS | stable task 10 has **Step 4:
PASS | stable task 10 has **Step 5:
PASS | stable task 10 has commit
PASS | stable task 10 has expected result
PASS | stable task 11 has **Files:**
PASS | stable task 11 has **Interfaces:**
PASS | stable task 11 has **Implementation requirements:**
PASS | stable task 11 has **Step 1:
PASS | stable task 11 has **Step 2:
PASS | stable task 11 has **Step 3:
PASS | stable task 11 has **Step 4:
PASS | stable task 11 has **Step 5:
PASS | stable task 11 has commit
PASS | stable task 11 has expected result
PASS | stable task 12 has **Files:**
PASS | stable task 12 has **Interfaces:**
PASS | stable task 12 has **Implementation requirements:**
PASS | stable task 12 has **Step 1:
PASS | stable task 12 has **Step 2:
PASS | stable task 12 has **Step 3:
PASS | stable task 12 has **Step 4:
PASS | stable task 12 has **Step 5:
PASS | stable task 12 has commit
PASS | stable task 12 has expected result
PASS | stable task 13 has **Files:**
PASS | stable task 13 has **Interfaces:**
PASS | stable task 13 has **Implementation requirements:**
PASS | stable task 13 has **Step 1:
PASS | stable task 13 has **Step 2:
PASS | stable task 13 has **Step 3:
PASS | stable task 13 has **Step 4:
PASS | stable task 13 has **Step 5:
PASS | stable task 13 has commit
PASS | stable task 13 has expected result
PASS | stable task 14 has **Files:**
PASS | stable task 14 has **Interfaces:**
PASS | stable task 14 has **Implementation requirements:**
PASS | stable task 14 has **Step 1:
PASS | stable task 14 has **Step 2:
PASS | stable task 14 has **Step 3:
PASS | stable task 14 has **Step 4:
PASS | stable task 14 has **Step 5:
PASS | stable task 14 has commit
PASS | stable task 14 has expected result
PASS | stable task 15 has **Files:**
PASS | stable task 15 has **Interfaces:**
PASS | stable task 15 has **Implementation requirements:**
PASS | stable task 15 has **Step 1:
PASS | stable task 15 has **Step 2:
PASS | stable task 15 has **Step 3:
PASS | stable task 15 has **Step 4:
PASS | stable task 15 has **Step 5:
PASS | stable task 15 has commit
PASS | stable task 15 has expected result
PASS | stable task 16 has **Files:**
PASS | stable task 16 has **Interfaces:**
PASS | stable task 16 has **Implementation requirements:**
PASS | stable task 16 has **Step 1:
PASS | stable task 16 has **Step 2:
PASS | stable task 16 has **Step 3:
PASS | stable task 16 has **Step 4:
PASS | stable task 16 has **Step 5:
PASS | stable task 16 has commit
PASS | stable task 16 has expected result
PASS | stable task 17 has **Files:**
PASS | stable task 17 has **Interfaces:**
PASS | stable task 17 has **Implementation requirements:**
PASS | stable task 17 has **Step 1:
PASS | stable task 17 has **Step 2:
PASS | stable task 17 has **Step 3:
PASS | stable task 17 has **Step 4:
PASS | stable task 17 has **Step 5:
PASS | stable task 17 has commit
PASS | stable task 17 has expected result
PASS | stable task 18 has **Files:**
PASS | stable task 18 has **Interfaces:**
PASS | stable task 18 has **Implementation requirements:**
PASS | stable task 18 has **Step 1:
PASS | stable task 18 has **Step 2:
PASS | stable task 18 has **Step 3:
PASS | stable task 18 has **Step 4:
PASS | stable task 18 has **Step 5:
PASS | stable task 18 has commit
PASS | stable task 18 has expected result
PASS | stable task 19 has **Files:**
PASS | stable task 19 has **Interfaces:**
PASS | stable task 19 has **Implementation requirements:**
PASS | stable task 19 has **Step 1:
PASS | stable task 19 has **Step 2:
PASS | stable task 19 has **Step 3:
PASS | stable task 19 has **Step 4:
PASS | stable task 19 has **Step 5:
PASS | stable task 19 has commit
PASS | stable task 19 has expected result
PASS | stable task 20 has **Files:**
PASS | stable task 20 has **Interfaces:**
PASS | stable task 20 has **Implementation requirements:**
PASS | stable task 20 has **Step 1:
PASS | stable task 20 has **Step 2:
PASS | stable task 20 has **Step 3:
PASS | stable task 20 has **Step 4:
PASS | stable task 20 has **Step 5:
PASS | stable task 20 has commit
PASS | stable task 20 has expected result
PASS | stable task 21 has **Files:**
PASS | stable task 21 has **Interfaces:**
PASS | stable task 21 has **Implementation requirements:**
PASS | stable task 21 has **Step 1:
PASS | stable task 21 has **Step 2:
PASS | stable task 21 has **Step 3:
PASS | stable task 21 has **Step 4:
PASS | stable task 21 has **Step 5:
PASS | stable task 21 has commit
PASS | stable task 21 has expected result
PASS | stable task 22 has **Files:**
PASS | stable task 22 has **Interfaces:**
PASS | stable task 22 has **Implementation requirements:**
PASS | stable task 22 has **Step 1:
PASS | stable task 22 has **Step 2:
PASS | stable task 22 has **Step 3:
PASS | stable task 22 has **Step 4:
PASS | stable task 22 has **Step 5:
PASS | stable task 22 has commit
PASS | stable task 22 has expected result
PASS | stable task 23 has **Files:**
PASS | stable task 23 has **Interfaces:**
PASS | stable task 23 has **Implementation requirements:**
PASS | stable task 23 has **Step 1:
PASS | stable task 23 has **Step 2:
PASS | stable task 23 has **Step 3:
PASS | stable task 23 has **Step 4:
PASS | stable task 23 has **Step 5:
PASS | stable task 23 has commit
PASS | stable task 23 has expected result
PASS | stable task 24 has **Files:**
PASS | stable task 24 has **Interfaces:**
PASS | stable task 24 has **Implementation requirements:**
PASS | stable task 24 has **Step 1:
PASS | stable task 24 has **Step 2:
PASS | stable task 24 has **Step 3:
PASS | stable task 24 has **Step 4:
PASS | stable task 24 has **Step 5:
PASS | stable task 24 has commit
PASS | stable task 24 has expected result
PASS | stable task 25 has **Files:**
PASS | stable task 25 has **Interfaces:**
PASS | stable task 25 has **Implementation requirements:**
PASS | stable task 25 has **Step 1:
PASS | stable task 25 has **Step 2:
PASS | stable task 25 has **Step 3:
PASS | stable task 25 has **Step 4:
PASS | stable task 25 has **Step 5:
PASS | stable task 25 has commit
PASS | stable task 25 has expected result
PASS | stable task 26 has **Files:**
PASS | stable task 26 has **Interfaces:**
PASS | stable task 26 has **Implementation requirements:**
PASS | stable task 26 has **Step 1:
PASS | stable task 26 has **Step 2:
PASS | stable task 26 has **Step 3:
PASS | stable task 26 has **Step 4:
PASS | stable task 26 has **Step 5:
PASS | stable task 26 has commit
PASS | stable task 26 has expected result
PASS | stable task 27 has **Files:**
PASS | stable task 27 has **Interfaces:**
PASS | stable task 27 has **Implementation requirements:**
PASS | stable task 27 has **Step 1:
PASS | stable task 27 has **Step 2:
PASS | stable task 27 has **Step 3:
PASS | stable task 27 has **Step 4:
PASS | stable task 27 has **Step 5:
PASS | stable task 27 has commit
PASS | stable task 27 has expected result
PASS | stable task 28 has **Files:**
PASS | stable task 28 has **Interfaces:**
PASS | stable task 28 has **Implementation requirements:**
PASS | stable task 28 has **Step 1:
PASS | stable task 28 has **Step 2:
PASS | stable task 28 has **Step 3:
PASS | stable task 28 has **Step 4:
PASS | stable task 28 has **Step 5:
PASS | stable task 28 has commit
PASS | stable task 28 has expected result
PASS | stable task 29 has **Files:**
PASS | stable task 29 has **Interfaces:**
PASS | stable task 29 has **Implementation requirements:**
PASS | stable task 29 has **Step 1:
PASS | stable task 29 has **Step 2:
PASS | stable task 29 has **Step 3:
PASS | stable task 29 has **Step 4:
PASS | stable task 29 has **Step 5:
PASS | stable task 29 has commit
PASS | stable task 29 has expected result
PASS | stable task 30 has **Files:**
PASS | stable task 30 has **Interfaces:**
PASS | stable task 30 has **Implementation requirements:**
PASS | stable task 30 has **Step 1:
PASS | stable task 30 has **Step 2:
PASS | stable task 30 has **Step 3:
PASS | stable task 30 has **Step 4:
PASS | stable task 30 has **Step 5:
PASS | stable task 30 has commit
PASS | stable task 30 has expected result
PASS | stable task 31 has **Files:**
PASS | stable task 31 has **Interfaces:**
PASS | stable task 31 has **Implementation requirements:**
PASS | stable task 31 has **Step 1:
PASS | stable task 31 has **Step 2:
PASS | stable task 31 has **Step 3:
PASS | stable task 31 has **Step 4:
PASS | stable task 31 has **Step 5:
PASS | stable task 31 has commit
PASS | stable task 31 has expected result
PASS | stable task 32 has **Files:**
PASS | stable task 32 has **Interfaces:**
PASS | stable task 32 has **Implementation requirements:**
PASS | stable task 32 has **Step 1:
PASS | stable task 32 has **Step 2:
PASS | stable task 32 has **Step 3:
PASS | stable task 32 has **Step 4:
PASS | stable task 32 has **Step 5:
PASS | stable task 32 has commit
PASS | stable task 32 has expected result
PASS | stable task 33 has **Files:**
PASS | stable task 33 has **Interfaces:**
PASS | stable task 33 has **Implementation requirements:**
PASS | stable task 33 has **Step 1:
PASS | stable task 33 has **Step 2:
PASS | stable task 33 has **Step 3:
PASS | stable task 33 has **Step 4:
PASS | stable task 33 has **Step 5:
PASS | stable task 33 has commit
PASS | stable task 33 has expected result
PASS | stable task 34 has **Files:**
PASS | stable task 34 has **Interfaces:**
PASS | stable task 34 has **Implementation requirements:**
PASS | stable task 34 has **Step 1:
PASS | stable task 34 has **Step 2:
PASS | stable task 34 has **Step 3:
PASS | stable task 34 has **Step 4:
PASS | stable task 34 has **Step 5:
PASS | stable task 34 has commit
PASS | stable task 34 has expected result
PASS | stable task 35 has **Files:**
PASS | stable task 35 has **Interfaces:**
PASS | stable task 35 has **Implementation requirements:**
PASS | stable task 35 has **Step 1:
PASS | stable task 35 has **Step 2:
PASS | stable task 35 has **Step 3:
PASS | stable task 35 has **Step 4:
PASS | stable task 35 has **Step 5:
PASS | stable task 35 has commit
PASS | stable task 35 has expected result
PASS | stable task 36 has **Files:**
PASS | stable task 36 has **Interfaces:**
PASS | stable task 36 has **Implementation requirements:**
PASS | stable task 36 has **Step 1:
PASS | stable task 36 has **Step 2:
PASS | stable task 36 has **Step 3:
PASS | stable task 36 has **Step 4:
PASS | stable task 36 has **Step 5:
PASS | stable task 36 has commit
PASS | stable task 36 has expected result
PASS | stable task 37 has **Files:**
PASS | stable task 37 has **Interfaces:**
PASS | stable task 37 has **Implementation requirements:**
PASS | stable task 37 has **Step 1:
PASS | stable task 37 has **Step 2:
PASS | stable task 37 has **Step 3:
PASS | stable task 37 has **Step 4:
PASS | stable task 37 has **Step 5:
PASS | stable task 37 has commit
PASS | stable task 37 has expected result
PASS | stable task 38 has **Files:**
PASS | stable task 38 has **Interfaces:**
PASS | stable task 38 has **Implementation requirements:**
PASS | stable task 38 has **Step 1:
PASS | stable task 38 has **Step 2:
PASS | stable task 38 has **Step 3:
PASS | stable task 38 has **Step 4:
PASS | stable task 38 has **Step 5:
PASS | stable task 38 has commit
PASS | stable task 38 has expected result
PASS | stable task 39 has **Files:**
PASS | stable task 39 has **Interfaces:**
PASS | stable task 39 has **Implementation requirements:**
PASS | stable task 39 has **Step 1:
PASS | stable task 39 has **Step 2:
PASS | stable task 39 has **Step 3:
PASS | stable task 39 has **Step 4:
PASS | stable task 39 has **Step 5:
PASS | stable task 39 has commit
PASS | stable task 39 has expected result
PASS | stable task 40 has **Files:**
PASS | stable task 40 has **Interfaces:**
PASS | stable task 40 has **Implementation requirements:**
PASS | stable task 40 has **Step 1:
PASS | stable task 40 has **Step 2:
PASS | stable task 40 has **Step 3:
PASS | stable task 40 has **Step 4:
PASS | stable task 40 has **Step 5:
PASS | stable task 40 has commit
PASS | stable task 40 has expected result
PASS | stable task 41 has **Files:**
PASS | stable task 41 has **Interfaces:**
PASS | stable task 41 has **Implementation requirements:**
PASS | stable task 41 has **Step 1:
PASS | stable task 41 has **Step 2:
PASS | stable task 41 has **Step 3:
PASS | stable task 41 has **Step 4:
PASS | stable task 41 has **Step 5:
PASS | stable task 41 has commit
PASS | stable task 41 has expected result
PASS | stable task 42 has **Files:**
PASS | stable task 42 has **Interfaces:**
PASS | stable task 42 has **Implementation requirements:**
PASS | stable task 42 has **Step 1:
PASS | stable task 42 has **Step 2:
PASS | stable task 42 has **Step 3:
PASS | stable task 42 has **Step 4:
PASS | stable task 42 has **Step 5:
PASS | stable task 42 has commit
PASS | stable task 42 has expected result
PASS | stable task 43 has **Files:**
PASS | stable task 43 has **Interfaces:**
PASS | stable task 43 has **Implementation requirements:**
PASS | stable task 43 has **Step 1:
PASS | stable task 43 has **Step 2:
PASS | stable task 43 has **Step 3:
PASS | stable task 43 has **Step 4:
PASS | stable task 43 has **Step 5:
PASS | stable task 43 has commit
PASS | stable task 43 has expected result
PASS | stable task 44 has **Files:**
PASS | stable task 44 has **Interfaces:**
PASS | stable task 44 has **Implementation requirements:**
PASS | stable task 44 has **Step 1:
PASS | stable task 44 has **Step 2:
PASS | stable task 44 has **Step 3:
PASS | stable task 44 has **Step 4:
PASS | stable task 44 has **Step 5:
PASS | stable task 44 has commit
PASS | stable task 44 has expected result
PASS | stable task 45 has **Files:**
PASS | stable task 45 has **Interfaces:**
PASS | stable task 45 has **Implementation requirements:**
PASS | stable task 45 has **Step 1:
PASS | stable task 45 has **Step 2:
PASS | stable task 45 has **Step 3:
PASS | stable task 45 has **Step 4:
PASS | stable task 45 has **Step 5:
PASS | stable task 45 has commit
PASS | stable task 45 has expected result
PASS | stable task 46 has **Files:**
PASS | stable task 46 has **Interfaces:**
PASS | stable task 46 has **Implementation requirements:**
PASS | stable task 46 has **Step 1:
PASS | stable task 46 has **Step 2:
PASS | stable task 46 has **Step 3:
PASS | stable task 46 has **Step 4:
PASS | stable task 46 has **Step 5:
PASS | stable task 46 has commit
PASS | stable task 46 has expected result
PASS | stable task 47 has **Files:**
PASS | stable task 47 has **Interfaces:**
PASS | stable task 47 has **Implementation requirements:**
PASS | stable task 47 has **Step 1:
PASS | stable task 47 has **Step 2:
PASS | stable task 47 has **Step 3:
PASS | stable task 47 has **Step 4:
PASS | stable task 47 has **Step 5:
PASS | stable task 47 has commit
PASS | stable task 47 has expected result
PASS | stable task 48 has **Files:**
PASS | stable task 48 has **Interfaces:**
PASS | stable task 48 has **Implementation requirements:**
PASS | stable task 48 has **Step 1:
PASS | stable task 48 has **Step 2:
PASS | stable task 48 has **Step 3:
PASS | stable task 48 has **Step 4:
PASS | stable task 48 has **Step 5:
PASS | stable task 48 has commit
PASS | stable task 48 has expected result
PASS | stable task 49 has **Files:**
PASS | stable task 49 has **Interfaces:**
PASS | stable task 49 has **Implementation requirements:**
PASS | stable task 49 has **Step 1:
PASS | stable task 49 has **Step 2:
PASS | stable task 49 has **Step 3:
PASS | stable task 49 has **Step 4:
PASS | stable task 49 has **Step 5:
PASS | stable task 49 has commit
PASS | stable task 49 has expected result
PASS | stable task 50 has **Files:**
PASS | stable task 50 has **Interfaces:**
PASS | stable task 50 has **Implementation requirements:**
PASS | stable task 50 has **Step 1:
PASS | stable task 50 has **Step 2:
PASS | stable task 50 has **Step 3:
PASS | stable task 50 has **Step 4:
PASS | stable task 50 has **Step 5:
PASS | stable task 50 has commit
PASS | stable task 50 has expected result
PASS | advanced task count | 15
PASS | advanced task sequence | [1, 2, 3]...[13, 14, 15]
PASS | advanced task 1 has **Files:**
PASS | advanced task 1 has **Interfaces:**
PASS | advanced task 1 has **Implementation requirements:**
PASS | advanced task 1 has **Step 1:
PASS | advanced task 1 has **Step 2:
PASS | advanced task 1 has **Step 3:
PASS | advanced task 1 has **Step 4:
PASS | advanced task 1 has **Step 5:
PASS | advanced task 1 has commit
PASS | advanced task 1 has expected result
PASS | advanced task 2 has **Files:**
PASS | advanced task 2 has **Interfaces:**
PASS | advanced task 2 has **Implementation requirements:**
PASS | advanced task 2 has **Step 1:
PASS | advanced task 2 has **Step 2:
PASS | advanced task 2 has **Step 3:
PASS | advanced task 2 has **Step 4:
PASS | advanced task 2 has **Step 5:
PASS | advanced task 2 has commit
PASS | advanced task 2 has expected result
PASS | advanced task 3 has **Files:**
PASS | advanced task 3 has **Interfaces:**
PASS | advanced task 3 has **Implementation requirements:**
PASS | advanced task 3 has **Step 1:
PASS | advanced task 3 has **Step 2:
PASS | advanced task 3 has **Step 3:
PASS | advanced task 3 has **Step 4:
PASS | advanced task 3 has **Step 5:
PASS | advanced task 3 has commit
PASS | advanced task 3 has expected result
PASS | advanced task 4 has **Files:**
PASS | advanced task 4 has **Interfaces:**
PASS | advanced task 4 has **Implementation requirements:**
PASS | advanced task 4 has **Step 1:
PASS | advanced task 4 has **Step 2:
PASS | advanced task 4 has **Step 3:
PASS | advanced task 4 has **Step 4:
PASS | advanced task 4 has **Step 5:
PASS | advanced task 4 has commit
PASS | advanced task 4 has expected result
PASS | advanced task 5 has **Files:**
PASS | advanced task 5 has **Interfaces:**
PASS | advanced task 5 has **Implementation requirements:**
PASS | advanced task 5 has **Step 1:
PASS | advanced task 5 has **Step 2:
PASS | advanced task 5 has **Step 3:
PASS | advanced task 5 has **Step 4:
PASS | advanced task 5 has **Step 5:
PASS | advanced task 5 has commit
PASS | advanced task 5 has expected result
PASS | advanced task 6 has **Files:**
PASS | advanced task 6 has **Interfaces:**
PASS | advanced task 6 has **Implementation requirements:**
PASS | advanced task 6 has **Step 1:
PASS | advanced task 6 has **Step 2:
PASS | advanced task 6 has **Step 3:
PASS | advanced task 6 has **Step 4:
PASS | advanced task 6 has **Step 5:
PASS | advanced task 6 has commit
PASS | advanced task 6 has expected result
PASS | advanced task 7 has **Files:**
PASS | advanced task 7 has **Interfaces:**
PASS | advanced task 7 has **Implementation requirements:**
PASS | advanced task 7 has **Step 1:
PASS | advanced task 7 has **Step 2:
PASS | advanced task 7 has **Step 3:
PASS | advanced task 7 has **Step 4:
PASS | advanced task 7 has **Step 5:
PASS | advanced task 7 has commit
PASS | advanced task 7 has expected result
PASS | advanced task 8 has **Files:**
PASS | advanced task 8 has **Interfaces:**
PASS | advanced task 8 has **Implementation requirements:**
PASS | advanced task 8 has **Step 1:
PASS | advanced task 8 has **Step 2:
PASS | advanced task 8 has **Step 3:
PASS | advanced task 8 has **Step 4:
PASS | advanced task 8 has **Step 5:
PASS | advanced task 8 has commit
PASS | advanced task 8 has expected result
PASS | advanced task 9 has **Files:**
PASS | advanced task 9 has **Interfaces:**
PASS | advanced task 9 has **Implementation requirements:**
PASS | advanced task 9 has **Step 1:
PASS | advanced task 9 has **Step 2:
PASS | advanced task 9 has **Step 3:
PASS | advanced task 9 has **Step 4:
PASS | advanced task 9 has **Step 5:
PASS | advanced task 9 has commit
PASS | advanced task 9 has expected result
PASS | advanced task 10 has **Files:**
PASS | advanced task 10 has **Interfaces:**
PASS | advanced task 10 has **Implementation requirements:**
PASS | advanced task 10 has **Step 1:
PASS | advanced task 10 has **Step 2:
PASS | advanced task 10 has **Step 3:
PASS | advanced task 10 has **Step 4:
PASS | advanced task 10 has **Step 5:
PASS | advanced task 10 has commit
PASS | advanced task 10 has expected result
PASS | advanced task 11 has **Files:**
PASS | advanced task 11 has **Interfaces:**
PASS | advanced task 11 has **Implementation requirements:**
PASS | advanced task 11 has **Step 1:
PASS | advanced task 11 has **Step 2:
PASS | advanced task 11 has **Step 3:
PASS | advanced task 11 has **Step 4:
PASS | advanced task 11 has **Step 5:
PASS | advanced task 11 has commit
PASS | advanced task 11 has expected result
PASS | advanced task 12 has **Files:**
PASS | advanced task 12 has **Interfaces:**
PASS | advanced task 12 has **Implementation requirements:**
PASS | advanced task 12 has **Step 1:
PASS | advanced task 12 has **Step 2:
PASS | advanced task 12 has **Step 3:
PASS | advanced task 12 has **Step 4:
PASS | advanced task 12 has **Step 5:
PASS | advanced task 12 has commit
PASS | advanced task 12 has expected result
PASS | advanced task 13 has **Files:**
PASS | advanced task 13 has **Interfaces:**
PASS | advanced task 13 has **Implementation requirements:**
PASS | advanced task 13 has **Step 1:
PASS | advanced task 13 has **Step 2:
PASS | advanced task 13 has **Step 3:
PASS | advanced task 13 has **Step 4:
PASS | advanced task 13 has **Step 5:
PASS | advanced task 13 has commit
PASS | advanced task 13 has expected result
PASS | advanced task 14 has **Files:**
PASS | advanced task 14 has **Interfaces:**
PASS | advanced task 14 has **Implementation requirements:**
PASS | advanced task 14 has **Step 1:
PASS | advanced task 14 has **Step 2:
PASS | advanced task 14 has **Step 3:
PASS | advanced task 14 has **Step 4:
PASS | advanced task 14 has **Step 5:
PASS | advanced task 14 has commit
PASS | advanced task 14 has expected result
PASS | advanced task 15 has **Files:**
PASS | advanced task 15 has **Interfaces:**
PASS | advanced task 15 has **Implementation requirements:**
PASS | advanced task 15 has **Step 1:
PASS | advanced task 15 has **Step 2:
PASS | advanced task 15 has **Step 3:
PASS | advanced task 15 has **Step 4:
PASS | advanced task 15 has **Step 5:
PASS | advanced task 15 has commit
PASS | advanced task 15 has expected result
PASS | 2026-08-11-mongodb-document-persistence-platform-design.md code fences balanced | 132
PASS | 2026-08-11-mongodb-document-persistence-platform-design.md no TODO markers
PASS | 2026-08-11-mongodb-document-persistence-platform-design.md no wildcard create paths
PASS | 2026-08-11-mongodb-document-persistence-platform-implementation-plan.md code fences balanced | 504
PASS | 2026-08-11-mongodb-document-persistence-platform-implementation-plan.md no TODO markers
PASS | 2026-08-11-mongodb-document-persistence-platform-implementation-plan.md no wildcard create paths
PASS | 2026-08-11-mongodb-advanced-capabilities-expansion-plan.md code fences balanced | 150
PASS | 2026-08-11-mongodb-advanced-capabilities-expansion-plan.md no TODO markers
PASS | 2026-08-11-mongodb-advanced-capabilities-expansion-plan.md no wildcard create paths
PASS | stable no duplicate create paths
PASS | advanced no duplicate create paths
PASS | stable and advanced create paths do not collide
PASS | no real generic repository declaration
PASS | no public arbitrary run command
PASS | stable starter excludes advanced
PASS | unknown commit body retry forbidden
PASS | mongo seven and eight matrix
PASS | advanced actual topology gate
SUMMARY | total=687 pass=687 fail=0
```
## 검증 한계
현재 검증은 설계서와 구현 계획서의 정적 구조·일관성 검사다. 실제 Backend Skeleton 저장소가 제공되지 않았으므로 Gradle configuration, compile, Spring Context, MongoDB Replica Set, failover, sharded cluster, Atlas/KMS, performance와 Git commit은 실행하지 않았다.
@@ -0,0 +1,73 @@
from pathlib import Path
import re
import sys
ROOT = Path(__file__).resolve().parent
DESIGN = ROOT / 'docs/superpowers/specs/2026-08-11-mongodb-document-persistence-platform-design.md'
PLAN = ROOT / 'docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md'
ADV = ROOT / 'docs/superpowers/plans/2026-08-11-mongodb-advanced-capabilities-expansion-plan.md'
checks = []
def check(name, condition, detail=''):
checks.append((name, bool(condition), detail))
texts = {p.name: p.read_text(encoding='utf-8') for p in (DESIGN, PLAN, ADV)}
design = texts[DESIGN.name]
plan = texts[PLAN.name]
adv = texts[ADV.name]
check('design exists', DESIGN.exists())
check('stable plan exists', PLAN.exists())
check('advanced plan exists', ADV.exists())
check('design purpose', 'MongoDB 문서 영속성 플랫폼 설계서' in design)
check('domain ownership', '도메인이 `@Document`, Repository' in design)
check('no generic repository design', '범용 `CommonMongoRepository<T, ID>`를 만들지 않는다' in design)
check('stable api strict', 'Stable API V1' in design and 'apiStrict=true' in design)
check('local replica set', 'Single-node Replica Set' in design)
check('standalone smoke only', 'Standalone은 smoke test' in design)
check('bson manifest', 'BSON 표현 Manifest' in design)
check('transaction retry separation', 'Transaction 본문 Retry와 Commit Retry를 분리' in design)
check('change stream at least once', 'at-least-once projector' in design)
check('ttl cleanup only', 'TTL은 물리 cleanup' in design)
check('gridfs compatibility only', 'GridFS는 compatibility adapter' in design)
check('driver native observability', 'Driver native ObservabilitySettings' in design)
for label, text, expected in [('stable', plan, 50), ('advanced', adv, 15)]:
nums = [int(x) for x in re.findall(r'^### Task (\d+):', text, re.M)]
check(f'{label} task count', len(nums) == expected, f'{len(nums)}')
check(f'{label} task sequence', nums == list(range(1, expected + 1)), str(nums[:3]) + '...' + str(nums[-3:]))
sections = re.split(r'(?=^### Task \d+:)', text, flags=re.M)[1:]
for idx, section in enumerate(sections, 1):
for marker in ['**Files:**', '**Interfaces:**', '**Implementation requirements:**',
'**Step 1:', '**Step 2:', '**Step 3:', '**Step 4:', '**Step 5:']:
check(f'{label} task {idx} has {marker}', marker in section)
check(f'{label} task {idx} has commit', 'git commit -m' in section)
check(f'{label} task {idx} has expected result', 'Expected:' in section)
for name, text in texts.items():
check(f'{name} code fences balanced', text.count('```') % 2 == 0, str(text.count('```')))
check(f'{name} no TODO markers', not re.search(r'\b(TODO|TBD|FIXME)\b', text))
check(f'{name} no wildcard create paths', not re.search(r'- Create: `[^`]*[\*?][^`]*`', text))
for label, text in [('stable', plan), ('advanced', adv)]:
created = re.findall(r'- Create: `([^`]+)`', text)
duplicates = sorted({p for p in created if created.count(p) > 1})
check(f'{label} no duplicate create paths', not duplicates, ', '.join(duplicates))
stable_created = set(re.findall(r'- Create: `([^`]+)`', plan))
advanced_created = set(re.findall(r'- Create: `([^`]+)`', adv))
check('stable and advanced create paths do not collide', not (stable_created & advanced_created),
', '.join(sorted(stable_created & advanced_created)))
check('no real generic repository declaration', not re.search(r'public\s+interface\s+(Common|Generic)MongoRepository', design + plan + adv))
check('no public arbitrary run command', not re.search(r'public\s+[^\n]+\s+runCommand\s*\(', design + plan + adv))
check('stable starter excludes advanced', 'no advanced module' in plan.lower() and 'Stable Starter' in adv)
check('unknown commit body retry forbidden', 'UnknownTransactionCommitResult' in plan and '업무 본문을 재실행하지 않는다' in plan)
check('mongo seven and eight matrix', 'MongoDB 7.0' in plan and 'MongoDB 8.0' in plan)
check('advanced actual topology gate', 'actual topology' in adv.lower() or '실제 topology' in adv)
failed = [c for c in checks if not c[1]]
for name, ok, detail in checks:
print(('PASS' if ok else 'FAIL') + ' | ' + name + ((' | ' + detail) if detail else ''))
print(f'SUMMARY | total={len(checks)} pass={len(checks)-len(failed)} fail={len(failed)}')
sys.exit(1 if failed else 0)
@@ -0,0 +1,5 @@
20a39913d84b179fb407811af4ee23bbcad8eebadff893add530cab97fa90878 ./README.md
b84784d8321ea826af7c9788693008fccd4f2bf437816d2d47a515caac098f04 ./VALIDATION.md
176b0c86e11e6d4d8440285dd723e5555616ff9a69a7a9419250c2ce2b67f565 ./docs/superpowers/plans/2026-08-10-notification-platform-implementation-plan.md
fe7b57c6573ef7305033fe9402333d3c10f7a03ccf421dce5f353e2616ac8677 ./docs/superpowers/specs/2026-08-10-notification-platform-design.md
2109766403adf001bb1dcbfe8679d362557f71433532336ff72b90976f3cfe10 ./validate_notification_docs.py
@@ -0,0 +1,60 @@
# Notification Delivery Platform Superpowers Package
이 패키지는 Backend Skeleton의 `notification` 기술 모듈을 구현하기 위한 설계서와 TDD 구현 계획서다.
## 파일
```text
docs/superpowers/specs/2026-08-10-notification-platform-design.md
docs/superpowers/plans/2026-08-10-notification-platform-implementation-plan.md
VALIDATION.md
validate_notification_docs.py
MANIFEST.sha256
```
## 핵심 계약
```text
NotificationRequest
→ RecipientDelivery
→ DeliveryAttempt
→ ProviderEvent append-only ledger
→ channel-specific projection
→ SubmissionOutcome / DeliveryOutcome / EvidenceLevel
```
- `submit()` 성공은 durable acceptance만 뜻한다.
- Provider acceptance와 delivery/read evidence를 분리한다.
- 결과를 확정할 수 없으면 `AMBIGUOUS`로 보존한다.
- ambiguous attempt의 자동 retry와 cross-channel fallback은 기본 차단한다.
- FCM은 FID를 primary target으로, registration token을 legacy compatibility로 둔다.
- Callback은 signature 검증 후 원장에 append하고 중복·역순·누락을 처리한다.
- Contact Point는 AES-256-GCM 암호화와 HMAC-SHA-256 equality fingerprint를 사용한다.
## 구현 기준 가정
실제 Backend Skeleton 저장소가 입력되지 않았기 때문에 다음 가정을 문서에 명시했다.
```text
Java 21
Gradle Kotlin DSL
Spring Framework 6.2 common line + Spring 7.0 compatibility job
PostgreSQL 16
JPA + Flyway
root package: io.backend.skeleton.notification
module root: modules/notification
```
실제 저장소에서는 dependency catalog와 package/file path를 기존 규칙에 맞게 매핑하되 공개 계약과 신뢰성 의미론은 유지한다.
## 실행
1. 설계서를 검토한다.
2. 계획서 Task 1부터 순서대로 실행한다.
3. 각 Task에서 failing test → red 확인 → 최소 구현 → green 확인 → commit 순서를 지킨다.
4. Task마다 specification review와 code-quality review를 분리한다.
5. 마지막 release gate에서 contract, chaos, security, performance, compatibility job을 모두 실행한다.
## 검증 범위
`VALIDATION.md`는 문서 구조, Task 연속성, 파일 경로 중복, 미확정 표현, 핵심 계약, ZIP CRC를 검증한 결과다. 실제 Gradle compile과 Provider integration은 Backend Skeleton 저장소에서 계획서의 명령을 실행해야 한다.
@@ -0,0 +1,590 @@
# Notification Superpowers 문서 정적 검증
- 결과: **PASS**
- 실행 검사: **567개**
- 통과: **567개**
- 실패: **0개**
- 설계서: **4,770행**
- 구현 계획서: **5,325행**
- 구현 Task: **50개**
- Create 경로: **322개**
- 설계 SHA-256: `fe7b57c6573ef7305033fe9402333d3c10f7a03ccf421dce5f353e2616ac8677`
- 계획 SHA-256: `176b0c86e11e6d4d8440285dd723e5555616ff9a69a7a9419250c2ce2b67f565`
## 검사 결과
| 검사 | 결과 | 상세 |
|---|---:|---|
| 설계서 존재 | PASS | /mnt/data/notification-platform-design.md |
| 구현 계획서 존재 | PASS | /mnt/data/notification-platform-implementation-plan.md |
| 설계서 최소 상세도 | PASS | 4,770 lines |
| 계획서 최소 상세도 | PASS | 5,325 lines |
| 설계서 코드 fence 균형 | PASS | 482 |
| 계획서 코드 fence 균형 | PASS | 308 |
| 설계 핵심 계약: NotificationRequest | PASS | |
| 설계 핵심 계약: RecipientDelivery | PASS | |
| 설계 핵심 계약: DeliveryAttempt | PASS | |
| 설계 핵심 계약: ProviderEvent | PASS | |
| 설계 핵심 계약: EvidenceLevel | PASS | |
| 설계 핵심 계약: SubmissionOutcome | PASS | |
| 설계 핵심 계약: DeliveryOutcome | PASS | |
| 설계 핵심 계약: AMBIGUOUS | PASS | |
| 설계 핵심 계약: append-only | PASS | |
| 설계 핵심 계약: FCM_FID | PASS | |
| 설계 핵심 계약: FCM_REGISTRATION_TOKEN_LEGACY | PASS | |
| 설계 핵심 계약: AES-256-GCM | PASS | |
| 설계 핵심 계약: HMAC-SHA-256 | PASS | |
| 설계 핵심 계약: FOR UPDATE SKIP LOCKED | PASS | |
| 설계 핵심 계약: SMTP Adapter | PASS | |
| 설계 핵심 계약: Amazon SES Adapter | PASS | |
| 설계 핵심 계약: Twilio Adapter | PASS | |
| 설계 핵심 계약: FCM Adapter | PASS | |
| 설계 핵심 계약: APNs Adapter | PASS | |
| 설계 핵심 계약: Web Push Adapter | PASS | |
| 설계 핵심 계약: In-App Inbox | PASS | |
| 설계 핵심 계약: Reconciliation | PASS | |
| 설계 핵심 계약: N4 Admin Plane | PASS | |
| 설계 핵심 계약: 비지원 범위 | PASS | |
| 설계 핵심 계약: 완료 정의 | PASS | |
| exactlyOnce 단순 옵션 금지 명시 | PASS | |
| 금지 보장 부재: guaranteedDelivery=true | PASS | |
| 금지 보장 부재: APNs HTTP 200 = DELIVERED | PASS | |
| 금지 보장 부재: FCM send success = DEVICE_DELIVERED | PASS | |
| Task 1~50 연속성 | PASS | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50] |
| Task chunk 수 | PASS | 50 |
| Task 1 필수 항목: **Files:** | PASS | |
| Task 1 필수 항목: **Interfaces:** | PASS | |
| Task 1 필수 항목: **Implementation requirements:** | PASS | |
| Task 1 필수 항목: **Step 1: | PASS | |
| Task 1 필수 항목: **Step 2: | PASS | |
| Task 1 필수 항목: **Step 3: | PASS | |
| Task 1 필수 항목: **Step 4: | PASS | |
| Task 1 필수 항목: **Step 5: | PASS | |
| Task 1 필수 항목: Expected: | PASS | |
| Task 1 필수 항목: git commit -m | PASS | |
| Task 2 필수 항목: **Files:** | PASS | |
| Task 2 필수 항목: **Interfaces:** | PASS | |
| Task 2 필수 항목: **Implementation requirements:** | PASS | |
| Task 2 필수 항목: **Step 1: | PASS | |
| Task 2 필수 항목: **Step 2: | PASS | |
| Task 2 필수 항목: **Step 3: | PASS | |
| Task 2 필수 항목: **Step 4: | PASS | |
| Task 2 필수 항목: **Step 5: | PASS | |
| Task 2 필수 항목: Expected: | PASS | |
| Task 2 필수 항목: git commit -m | PASS | |
| Task 3 필수 항목: **Files:** | PASS | |
| Task 3 필수 항목: **Interfaces:** | PASS | |
| Task 3 필수 항목: **Implementation requirements:** | PASS | |
| Task 3 필수 항목: **Step 1: | PASS | |
| Task 3 필수 항목: **Step 2: | PASS | |
| Task 3 필수 항목: **Step 3: | PASS | |
| Task 3 필수 항목: **Step 4: | PASS | |
| Task 3 필수 항목: **Step 5: | PASS | |
| Task 3 필수 항목: Expected: | PASS | |
| Task 3 필수 항목: git commit -m | PASS | |
| Task 4 필수 항목: **Files:** | PASS | |
| Task 4 필수 항목: **Interfaces:** | PASS | |
| Task 4 필수 항목: **Implementation requirements:** | PASS | |
| Task 4 필수 항목: **Step 1: | PASS | |
| Task 4 필수 항목: **Step 2: | PASS | |
| Task 4 필수 항목: **Step 3: | PASS | |
| Task 4 필수 항목: **Step 4: | PASS | |
| Task 4 필수 항목: **Step 5: | PASS | |
| Task 4 필수 항목: Expected: | PASS | |
| Task 4 필수 항목: git commit -m | PASS | |
| Task 5 필수 항목: **Files:** | PASS | |
| Task 5 필수 항목: **Interfaces:** | PASS | |
| Task 5 필수 항목: **Implementation requirements:** | PASS | |
| Task 5 필수 항목: **Step 1: | PASS | |
| Task 5 필수 항목: **Step 2: | PASS | |
| Task 5 필수 항목: **Step 3: | PASS | |
| Task 5 필수 항목: **Step 4: | PASS | |
| Task 5 필수 항목: **Step 5: | PASS | |
| Task 5 필수 항목: Expected: | PASS | |
| Task 5 필수 항목: git commit -m | PASS | |
| Task 6 필수 항목: **Files:** | PASS | |
| Task 6 필수 항목: **Interfaces:** | PASS | |
| Task 6 필수 항목: **Implementation requirements:** | PASS | |
| Task 6 필수 항목: **Step 1: | PASS | |
| Task 6 필수 항목: **Step 2: | PASS | |
| Task 6 필수 항목: **Step 3: | PASS | |
| Task 6 필수 항목: **Step 4: | PASS | |
| Task 6 필수 항목: **Step 5: | PASS | |
| Task 6 필수 항목: Expected: | PASS | |
| Task 6 필수 항목: git commit -m | PASS | |
| Task 7 필수 항목: **Files:** | PASS | |
| Task 7 필수 항목: **Interfaces:** | PASS | |
| Task 7 필수 항목: **Implementation requirements:** | PASS | |
| Task 7 필수 항목: **Step 1: | PASS | |
| Task 7 필수 항목: **Step 2: | PASS | |
| Task 7 필수 항목: **Step 3: | PASS | |
| Task 7 필수 항목: **Step 4: | PASS | |
| Task 7 필수 항목: **Step 5: | PASS | |
| Task 7 필수 항목: Expected: | PASS | |
| Task 7 필수 항목: git commit -m | PASS | |
| Task 8 필수 항목: **Files:** | PASS | |
| Task 8 필수 항목: **Interfaces:** | PASS | |
| Task 8 필수 항목: **Implementation requirements:** | PASS | |
| Task 8 필수 항목: **Step 1: | PASS | |
| Task 8 필수 항목: **Step 2: | PASS | |
| Task 8 필수 항목: **Step 3: | PASS | |
| Task 8 필수 항목: **Step 4: | PASS | |
| Task 8 필수 항목: **Step 5: | PASS | |
| Task 8 필수 항목: Expected: | PASS | |
| Task 8 필수 항목: git commit -m | PASS | |
| Task 9 필수 항목: **Files:** | PASS | |
| Task 9 필수 항목: **Interfaces:** | PASS | |
| Task 9 필수 항목: **Implementation requirements:** | PASS | |
| Task 9 필수 항목: **Step 1: | PASS | |
| Task 9 필수 항목: **Step 2: | PASS | |
| Task 9 필수 항목: **Step 3: | PASS | |
| Task 9 필수 항목: **Step 4: | PASS | |
| Task 9 필수 항목: **Step 5: | PASS | |
| Task 9 필수 항목: Expected: | PASS | |
| Task 9 필수 항목: git commit -m | PASS | |
| Task 10 필수 항목: **Files:** | PASS | |
| Task 10 필수 항목: **Interfaces:** | PASS | |
| Task 10 필수 항목: **Implementation requirements:** | PASS | |
| Task 10 필수 항목: **Step 1: | PASS | |
| Task 10 필수 항목: **Step 2: | PASS | |
| Task 10 필수 항목: **Step 3: | PASS | |
| Task 10 필수 항목: **Step 4: | PASS | |
| Task 10 필수 항목: **Step 5: | PASS | |
| Task 10 필수 항목: Expected: | PASS | |
| Task 10 필수 항목: git commit -m | PASS | |
| Task 11 필수 항목: **Files:** | PASS | |
| Task 11 필수 항목: **Interfaces:** | PASS | |
| Task 11 필수 항목: **Implementation requirements:** | PASS | |
| Task 11 필수 항목: **Step 1: | PASS | |
| Task 11 필수 항목: **Step 2: | PASS | |
| Task 11 필수 항목: **Step 3: | PASS | |
| Task 11 필수 항목: **Step 4: | PASS | |
| Task 11 필수 항목: **Step 5: | PASS | |
| Task 11 필수 항목: Expected: | PASS | |
| Task 11 필수 항목: git commit -m | PASS | |
| Task 12 필수 항목: **Files:** | PASS | |
| Task 12 필수 항목: **Interfaces:** | PASS | |
| Task 12 필수 항목: **Implementation requirements:** | PASS | |
| Task 12 필수 항목: **Step 1: | PASS | |
| Task 12 필수 항목: **Step 2: | PASS | |
| Task 12 필수 항목: **Step 3: | PASS | |
| Task 12 필수 항목: **Step 4: | PASS | |
| Task 12 필수 항목: **Step 5: | PASS | |
| Task 12 필수 항목: Expected: | PASS | |
| Task 12 필수 항목: git commit -m | PASS | |
| Task 13 필수 항목: **Files:** | PASS | |
| Task 13 필수 항목: **Interfaces:** | PASS | |
| Task 13 필수 항목: **Implementation requirements:** | PASS | |
| Task 13 필수 항목: **Step 1: | PASS | |
| Task 13 필수 항목: **Step 2: | PASS | |
| Task 13 필수 항목: **Step 3: | PASS | |
| Task 13 필수 항목: **Step 4: | PASS | |
| Task 13 필수 항목: **Step 5: | PASS | |
| Task 13 필수 항목: Expected: | PASS | |
| Task 13 필수 항목: git commit -m | PASS | |
| Task 14 필수 항목: **Files:** | PASS | |
| Task 14 필수 항목: **Interfaces:** | PASS | |
| Task 14 필수 항목: **Implementation requirements:** | PASS | |
| Task 14 필수 항목: **Step 1: | PASS | |
| Task 14 필수 항목: **Step 2: | PASS | |
| Task 14 필수 항목: **Step 3: | PASS | |
| Task 14 필수 항목: **Step 4: | PASS | |
| Task 14 필수 항목: **Step 5: | PASS | |
| Task 14 필수 항목: Expected: | PASS | |
| Task 14 필수 항목: git commit -m | PASS | |
| Task 15 필수 항목: **Files:** | PASS | |
| Task 15 필수 항목: **Interfaces:** | PASS | |
| Task 15 필수 항목: **Implementation requirements:** | PASS | |
| Task 15 필수 항목: **Step 1: | PASS | |
| Task 15 필수 항목: **Step 2: | PASS | |
| Task 15 필수 항목: **Step 3: | PASS | |
| Task 15 필수 항목: **Step 4: | PASS | |
| Task 15 필수 항목: **Step 5: | PASS | |
| Task 15 필수 항목: Expected: | PASS | |
| Task 15 필수 항목: git commit -m | PASS | |
| Task 16 필수 항목: **Files:** | PASS | |
| Task 16 필수 항목: **Interfaces:** | PASS | |
| Task 16 필수 항목: **Implementation requirements:** | PASS | |
| Task 16 필수 항목: **Step 1: | PASS | |
| Task 16 필수 항목: **Step 2: | PASS | |
| Task 16 필수 항목: **Step 3: | PASS | |
| Task 16 필수 항목: **Step 4: | PASS | |
| Task 16 필수 항목: **Step 5: | PASS | |
| Task 16 필수 항목: Expected: | PASS | |
| Task 16 필수 항목: git commit -m | PASS | |
| Task 17 필수 항목: **Files:** | PASS | |
| Task 17 필수 항목: **Interfaces:** | PASS | |
| Task 17 필수 항목: **Implementation requirements:** | PASS | |
| Task 17 필수 항목: **Step 1: | PASS | |
| Task 17 필수 항목: **Step 2: | PASS | |
| Task 17 필수 항목: **Step 3: | PASS | |
| Task 17 필수 항목: **Step 4: | PASS | |
| Task 17 필수 항목: **Step 5: | PASS | |
| Task 17 필수 항목: Expected: | PASS | |
| Task 17 필수 항목: git commit -m | PASS | |
| Task 18 필수 항목: **Files:** | PASS | |
| Task 18 필수 항목: **Interfaces:** | PASS | |
| Task 18 필수 항목: **Implementation requirements:** | PASS | |
| Task 18 필수 항목: **Step 1: | PASS | |
| Task 18 필수 항목: **Step 2: | PASS | |
| Task 18 필수 항목: **Step 3: | PASS | |
| Task 18 필수 항목: **Step 4: | PASS | |
| Task 18 필수 항목: **Step 5: | PASS | |
| Task 18 필수 항목: Expected: | PASS | |
| Task 18 필수 항목: git commit -m | PASS | |
| Task 19 필수 항목: **Files:** | PASS | |
| Task 19 필수 항목: **Interfaces:** | PASS | |
| Task 19 필수 항목: **Implementation requirements:** | PASS | |
| Task 19 필수 항목: **Step 1: | PASS | |
| Task 19 필수 항목: **Step 2: | PASS | |
| Task 19 필수 항목: **Step 3: | PASS | |
| Task 19 필수 항목: **Step 4: | PASS | |
| Task 19 필수 항목: **Step 5: | PASS | |
| Task 19 필수 항목: Expected: | PASS | |
| Task 19 필수 항목: git commit -m | PASS | |
| Task 20 필수 항목: **Files:** | PASS | |
| Task 20 필수 항목: **Interfaces:** | PASS | |
| Task 20 필수 항목: **Implementation requirements:** | PASS | |
| Task 20 필수 항목: **Step 1: | PASS | |
| Task 20 필수 항목: **Step 2: | PASS | |
| Task 20 필수 항목: **Step 3: | PASS | |
| Task 20 필수 항목: **Step 4: | PASS | |
| Task 20 필수 항목: **Step 5: | PASS | |
| Task 20 필수 항목: Expected: | PASS | |
| Task 20 필수 항목: git commit -m | PASS | |
| Task 21 필수 항목: **Files:** | PASS | |
| Task 21 필수 항목: **Interfaces:** | PASS | |
| Task 21 필수 항목: **Implementation requirements:** | PASS | |
| Task 21 필수 항목: **Step 1: | PASS | |
| Task 21 필수 항목: **Step 2: | PASS | |
| Task 21 필수 항목: **Step 3: | PASS | |
| Task 21 필수 항목: **Step 4: | PASS | |
| Task 21 필수 항목: **Step 5: | PASS | |
| Task 21 필수 항목: Expected: | PASS | |
| Task 21 필수 항목: git commit -m | PASS | |
| Task 22 필수 항목: **Files:** | PASS | |
| Task 22 필수 항목: **Interfaces:** | PASS | |
| Task 22 필수 항목: **Implementation requirements:** | PASS | |
| Task 22 필수 항목: **Step 1: | PASS | |
| Task 22 필수 항목: **Step 2: | PASS | |
| Task 22 필수 항목: **Step 3: | PASS | |
| Task 22 필수 항목: **Step 4: | PASS | |
| Task 22 필수 항목: **Step 5: | PASS | |
| Task 22 필수 항목: Expected: | PASS | |
| Task 22 필수 항목: git commit -m | PASS | |
| Task 23 필수 항목: **Files:** | PASS | |
| Task 23 필수 항목: **Interfaces:** | PASS | |
| Task 23 필수 항목: **Implementation requirements:** | PASS | |
| Task 23 필수 항목: **Step 1: | PASS | |
| Task 23 필수 항목: **Step 2: | PASS | |
| Task 23 필수 항목: **Step 3: | PASS | |
| Task 23 필수 항목: **Step 4: | PASS | |
| Task 23 필수 항목: **Step 5: | PASS | |
| Task 23 필수 항목: Expected: | PASS | |
| Task 23 필수 항목: git commit -m | PASS | |
| Task 24 필수 항목: **Files:** | PASS | |
| Task 24 필수 항목: **Interfaces:** | PASS | |
| Task 24 필수 항목: **Implementation requirements:** | PASS | |
| Task 24 필수 항목: **Step 1: | PASS | |
| Task 24 필수 항목: **Step 2: | PASS | |
| Task 24 필수 항목: **Step 3: | PASS | |
| Task 24 필수 항목: **Step 4: | PASS | |
| Task 24 필수 항목: **Step 5: | PASS | |
| Task 24 필수 항목: Expected: | PASS | |
| Task 24 필수 항목: git commit -m | PASS | |
| Task 25 필수 항목: **Files:** | PASS | |
| Task 25 필수 항목: **Interfaces:** | PASS | |
| Task 25 필수 항목: **Implementation requirements:** | PASS | |
| Task 25 필수 항목: **Step 1: | PASS | |
| Task 25 필수 항목: **Step 2: | PASS | |
| Task 25 필수 항목: **Step 3: | PASS | |
| Task 25 필수 항목: **Step 4: | PASS | |
| Task 25 필수 항목: **Step 5: | PASS | |
| Task 25 필수 항목: Expected: | PASS | |
| Task 25 필수 항목: git commit -m | PASS | |
| Task 26 필수 항목: **Files:** | PASS | |
| Task 26 필수 항목: **Interfaces:** | PASS | |
| Task 26 필수 항목: **Implementation requirements:** | PASS | |
| Task 26 필수 항목: **Step 1: | PASS | |
| Task 26 필수 항목: **Step 2: | PASS | |
| Task 26 필수 항목: **Step 3: | PASS | |
| Task 26 필수 항목: **Step 4: | PASS | |
| Task 26 필수 항목: **Step 5: | PASS | |
| Task 26 필수 항목: Expected: | PASS | |
| Task 26 필수 항목: git commit -m | PASS | |
| Task 27 필수 항목: **Files:** | PASS | |
| Task 27 필수 항목: **Interfaces:** | PASS | |
| Task 27 필수 항목: **Implementation requirements:** | PASS | |
| Task 27 필수 항목: **Step 1: | PASS | |
| Task 27 필수 항목: **Step 2: | PASS | |
| Task 27 필수 항목: **Step 3: | PASS | |
| Task 27 필수 항목: **Step 4: | PASS | |
| Task 27 필수 항목: **Step 5: | PASS | |
| Task 27 필수 항목: Expected: | PASS | |
| Task 27 필수 항목: git commit -m | PASS | |
| Task 28 필수 항목: **Files:** | PASS | |
| Task 28 필수 항목: **Interfaces:** | PASS | |
| Task 28 필수 항목: **Implementation requirements:** | PASS | |
| Task 28 필수 항목: **Step 1: | PASS | |
| Task 28 필수 항목: **Step 2: | PASS | |
| Task 28 필수 항목: **Step 3: | PASS | |
| Task 28 필수 항목: **Step 4: | PASS | |
| Task 28 필수 항목: **Step 5: | PASS | |
| Task 28 필수 항목: Expected: | PASS | |
| Task 28 필수 항목: git commit -m | PASS | |
| Task 29 필수 항목: **Files:** | PASS | |
| Task 29 필수 항목: **Interfaces:** | PASS | |
| Task 29 필수 항목: **Implementation requirements:** | PASS | |
| Task 29 필수 항목: **Step 1: | PASS | |
| Task 29 필수 항목: **Step 2: | PASS | |
| Task 29 필수 항목: **Step 3: | PASS | |
| Task 29 필수 항목: **Step 4: | PASS | |
| Task 29 필수 항목: **Step 5: | PASS | |
| Task 29 필수 항목: Expected: | PASS | |
| Task 29 필수 항목: git commit -m | PASS | |
| Task 30 필수 항목: **Files:** | PASS | |
| Task 30 필수 항목: **Interfaces:** | PASS | |
| Task 30 필수 항목: **Implementation requirements:** | PASS | |
| Task 30 필수 항목: **Step 1: | PASS | |
| Task 30 필수 항목: **Step 2: | PASS | |
| Task 30 필수 항목: **Step 3: | PASS | |
| Task 30 필수 항목: **Step 4: | PASS | |
| Task 30 필수 항목: **Step 5: | PASS | |
| Task 30 필수 항목: Expected: | PASS | |
| Task 30 필수 항목: git commit -m | PASS | |
| Task 31 필수 항목: **Files:** | PASS | |
| Task 31 필수 항목: **Interfaces:** | PASS | |
| Task 31 필수 항목: **Implementation requirements:** | PASS | |
| Task 31 필수 항목: **Step 1: | PASS | |
| Task 31 필수 항목: **Step 2: | PASS | |
| Task 31 필수 항목: **Step 3: | PASS | |
| Task 31 필수 항목: **Step 4: | PASS | |
| Task 31 필수 항목: **Step 5: | PASS | |
| Task 31 필수 항목: Expected: | PASS | |
| Task 31 필수 항목: git commit -m | PASS | |
| Task 32 필수 항목: **Files:** | PASS | |
| Task 32 필수 항목: **Interfaces:** | PASS | |
| Task 32 필수 항목: **Implementation requirements:** | PASS | |
| Task 32 필수 항목: **Step 1: | PASS | |
| Task 32 필수 항목: **Step 2: | PASS | |
| Task 32 필수 항목: **Step 3: | PASS | |
| Task 32 필수 항목: **Step 4: | PASS | |
| Task 32 필수 항목: **Step 5: | PASS | |
| Task 32 필수 항목: Expected: | PASS | |
| Task 32 필수 항목: git commit -m | PASS | |
| Task 33 필수 항목: **Files:** | PASS | |
| Task 33 필수 항목: **Interfaces:** | PASS | |
| Task 33 필수 항목: **Implementation requirements:** | PASS | |
| Task 33 필수 항목: **Step 1: | PASS | |
| Task 33 필수 항목: **Step 2: | PASS | |
| Task 33 필수 항목: **Step 3: | PASS | |
| Task 33 필수 항목: **Step 4: | PASS | |
| Task 33 필수 항목: **Step 5: | PASS | |
| Task 33 필수 항목: Expected: | PASS | |
| Task 33 필수 항목: git commit -m | PASS | |
| Task 34 필수 항목: **Files:** | PASS | |
| Task 34 필수 항목: **Interfaces:** | PASS | |
| Task 34 필수 항목: **Implementation requirements:** | PASS | |
| Task 34 필수 항목: **Step 1: | PASS | |
| Task 34 필수 항목: **Step 2: | PASS | |
| Task 34 필수 항목: **Step 3: | PASS | |
| Task 34 필수 항목: **Step 4: | PASS | |
| Task 34 필수 항목: **Step 5: | PASS | |
| Task 34 필수 항목: Expected: | PASS | |
| Task 34 필수 항목: git commit -m | PASS | |
| Task 35 필수 항목: **Files:** | PASS | |
| Task 35 필수 항목: **Interfaces:** | PASS | |
| Task 35 필수 항목: **Implementation requirements:** | PASS | |
| Task 35 필수 항목: **Step 1: | PASS | |
| Task 35 필수 항목: **Step 2: | PASS | |
| Task 35 필수 항목: **Step 3: | PASS | |
| Task 35 필수 항목: **Step 4: | PASS | |
| Task 35 필수 항목: **Step 5: | PASS | |
| Task 35 필수 항목: Expected: | PASS | |
| Task 35 필수 항목: git commit -m | PASS | |
| Task 36 필수 항목: **Files:** | PASS | |
| Task 36 필수 항목: **Interfaces:** | PASS | |
| Task 36 필수 항목: **Implementation requirements:** | PASS | |
| Task 36 필수 항목: **Step 1: | PASS | |
| Task 36 필수 항목: **Step 2: | PASS | |
| Task 36 필수 항목: **Step 3: | PASS | |
| Task 36 필수 항목: **Step 4: | PASS | |
| Task 36 필수 항목: **Step 5: | PASS | |
| Task 36 필수 항목: Expected: | PASS | |
| Task 36 필수 항목: git commit -m | PASS | |
| Task 37 필수 항목: **Files:** | PASS | |
| Task 37 필수 항목: **Interfaces:** | PASS | |
| Task 37 필수 항목: **Implementation requirements:** | PASS | |
| Task 37 필수 항목: **Step 1: | PASS | |
| Task 37 필수 항목: **Step 2: | PASS | |
| Task 37 필수 항목: **Step 3: | PASS | |
| Task 37 필수 항목: **Step 4: | PASS | |
| Task 37 필수 항목: **Step 5: | PASS | |
| Task 37 필수 항목: Expected: | PASS | |
| Task 37 필수 항목: git commit -m | PASS | |
| Task 38 필수 항목: **Files:** | PASS | |
| Task 38 필수 항목: **Interfaces:** | PASS | |
| Task 38 필수 항목: **Implementation requirements:** | PASS | |
| Task 38 필수 항목: **Step 1: | PASS | |
| Task 38 필수 항목: **Step 2: | PASS | |
| Task 38 필수 항목: **Step 3: | PASS | |
| Task 38 필수 항목: **Step 4: | PASS | |
| Task 38 필수 항목: **Step 5: | PASS | |
| Task 38 필수 항목: Expected: | PASS | |
| Task 38 필수 항목: git commit -m | PASS | |
| Task 39 필수 항목: **Files:** | PASS | |
| Task 39 필수 항목: **Interfaces:** | PASS | |
| Task 39 필수 항목: **Implementation requirements:** | PASS | |
| Task 39 필수 항목: **Step 1: | PASS | |
| Task 39 필수 항목: **Step 2: | PASS | |
| Task 39 필수 항목: **Step 3: | PASS | |
| Task 39 필수 항목: **Step 4: | PASS | |
| Task 39 필수 항목: **Step 5: | PASS | |
| Task 39 필수 항목: Expected: | PASS | |
| Task 39 필수 항목: git commit -m | PASS | |
| Task 40 필수 항목: **Files:** | PASS | |
| Task 40 필수 항목: **Interfaces:** | PASS | |
| Task 40 필수 항목: **Implementation requirements:** | PASS | |
| Task 40 필수 항목: **Step 1: | PASS | |
| Task 40 필수 항목: **Step 2: | PASS | |
| Task 40 필수 항목: **Step 3: | PASS | |
| Task 40 필수 항목: **Step 4: | PASS | |
| Task 40 필수 항목: **Step 5: | PASS | |
| Task 40 필수 항목: Expected: | PASS | |
| Task 40 필수 항목: git commit -m | PASS | |
| Task 41 필수 항목: **Files:** | PASS | |
| Task 41 필수 항목: **Interfaces:** | PASS | |
| Task 41 필수 항목: **Implementation requirements:** | PASS | |
| Task 41 필수 항목: **Step 1: | PASS | |
| Task 41 필수 항목: **Step 2: | PASS | |
| Task 41 필수 항목: **Step 3: | PASS | |
| Task 41 필수 항목: **Step 4: | PASS | |
| Task 41 필수 항목: **Step 5: | PASS | |
| Task 41 필수 항목: Expected: | PASS | |
| Task 41 필수 항목: git commit -m | PASS | |
| Task 42 필수 항목: **Files:** | PASS | |
| Task 42 필수 항목: **Interfaces:** | PASS | |
| Task 42 필수 항목: **Implementation requirements:** | PASS | |
| Task 42 필수 항목: **Step 1: | PASS | |
| Task 42 필수 항목: **Step 2: | PASS | |
| Task 42 필수 항목: **Step 3: | PASS | |
| Task 42 필수 항목: **Step 4: | PASS | |
| Task 42 필수 항목: **Step 5: | PASS | |
| Task 42 필수 항목: Expected: | PASS | |
| Task 42 필수 항목: git commit -m | PASS | |
| Task 43 필수 항목: **Files:** | PASS | |
| Task 43 필수 항목: **Interfaces:** | PASS | |
| Task 43 필수 항목: **Implementation requirements:** | PASS | |
| Task 43 필수 항목: **Step 1: | PASS | |
| Task 43 필수 항목: **Step 2: | PASS | |
| Task 43 필수 항목: **Step 3: | PASS | |
| Task 43 필수 항목: **Step 4: | PASS | |
| Task 43 필수 항목: **Step 5: | PASS | |
| Task 43 필수 항목: Expected: | PASS | |
| Task 43 필수 항목: git commit -m | PASS | |
| Task 44 필수 항목: **Files:** | PASS | |
| Task 44 필수 항목: **Interfaces:** | PASS | |
| Task 44 필수 항목: **Implementation requirements:** | PASS | |
| Task 44 필수 항목: **Step 1: | PASS | |
| Task 44 필수 항목: **Step 2: | PASS | |
| Task 44 필수 항목: **Step 3: | PASS | |
| Task 44 필수 항목: **Step 4: | PASS | |
| Task 44 필수 항목: **Step 5: | PASS | |
| Task 44 필수 항목: Expected: | PASS | |
| Task 44 필수 항목: git commit -m | PASS | |
| Task 45 필수 항목: **Files:** | PASS | |
| Task 45 필수 항목: **Interfaces:** | PASS | |
| Task 45 필수 항목: **Implementation requirements:** | PASS | |
| Task 45 필수 항목: **Step 1: | PASS | |
| Task 45 필수 항목: **Step 2: | PASS | |
| Task 45 필수 항목: **Step 3: | PASS | |
| Task 45 필수 항목: **Step 4: | PASS | |
| Task 45 필수 항목: **Step 5: | PASS | |
| Task 45 필수 항목: Expected: | PASS | |
| Task 45 필수 항목: git commit -m | PASS | |
| Task 46 필수 항목: **Files:** | PASS | |
| Task 46 필수 항목: **Interfaces:** | PASS | |
| Task 46 필수 항목: **Implementation requirements:** | PASS | |
| Task 46 필수 항목: **Step 1: | PASS | |
| Task 46 필수 항목: **Step 2: | PASS | |
| Task 46 필수 항목: **Step 3: | PASS | |
| Task 46 필수 항목: **Step 4: | PASS | |
| Task 46 필수 항목: **Step 5: | PASS | |
| Task 46 필수 항목: Expected: | PASS | |
| Task 46 필수 항목: git commit -m | PASS | |
| Task 47 필수 항목: **Files:** | PASS | |
| Task 47 필수 항목: **Interfaces:** | PASS | |
| Task 47 필수 항목: **Implementation requirements:** | PASS | |
| Task 47 필수 항목: **Step 1: | PASS | |
| Task 47 필수 항목: **Step 2: | PASS | |
| Task 47 필수 항목: **Step 3: | PASS | |
| Task 47 필수 항목: **Step 4: | PASS | |
| Task 47 필수 항목: **Step 5: | PASS | |
| Task 47 필수 항목: Expected: | PASS | |
| Task 47 필수 항목: git commit -m | PASS | |
| Task 48 필수 항목: **Files:** | PASS | |
| Task 48 필수 항목: **Interfaces:** | PASS | |
| Task 48 필수 항목: **Implementation requirements:** | PASS | |
| Task 48 필수 항목: **Step 1: | PASS | |
| Task 48 필수 항목: **Step 2: | PASS | |
| Task 48 필수 항목: **Step 3: | PASS | |
| Task 48 필수 항목: **Step 4: | PASS | |
| Task 48 필수 항목: **Step 5: | PASS | |
| Task 48 필수 항목: Expected: | PASS | |
| Task 48 필수 항목: git commit -m | PASS | |
| Task 49 필수 항목: **Files:** | PASS | |
| Task 49 필수 항목: **Interfaces:** | PASS | |
| Task 49 필수 항목: **Implementation requirements:** | PASS | |
| Task 49 필수 항목: **Step 1: | PASS | |
| Task 49 필수 항목: **Step 2: | PASS | |
| Task 49 필수 항목: **Step 3: | PASS | |
| Task 49 필수 항목: **Step 4: | PASS | |
| Task 49 필수 항목: **Step 5: | PASS | |
| Task 49 필수 항목: Expected: | PASS | |
| Task 49 필수 항목: git commit -m | PASS | |
| Task 50 필수 항목: **Files:** | PASS | |
| Task 50 필수 항목: **Interfaces:** | PASS | |
| Task 50 필수 항목: **Implementation requirements:** | PASS | |
| Task 50 필수 항목: **Step 1: | PASS | |
| Task 50 필수 항목: **Step 2: | PASS | |
| Task 50 필수 항목: **Step 3: | PASS | |
| Task 50 필수 항목: **Step 4: | PASS | |
| Task 50 필수 항목: **Step 5: | PASS | |
| Task 50 필수 항목: Expected: | PASS | |
| Task 50 필수 항목: git commit -m | PASS | |
| Create 경로 중복 없음 | PASS | |
| Create 경로 충분성 | PASS | 322 |
| 미확정 표현 없음: TODO | PASS | 0 |
| 미확정 표현 없음: TBD | PASS | 0 |
| 미확정 표현 없음: FIXME | PASS | 0 |
| 미확정 표현 없음: fill in details | PASS | 0 |
| 미확정 표현 없음: implement later | PASS | 0 |
| 미확정 표현 없음: concrete assertion below | PASS | 0 |
| 미확정 표현 없음: 유사 작업 참조 | PASS | 0 |
| 계획 핵심 회귀 테스트: providerAcceptanceIsNotDelivery | PASS | |
| 계획 핵심 회귀 테스트: concurrentSameRequestReturnsOneNotificationId | PASS | |
| 계획 핵심 회귀 테스트: providerAcceptsThenResponseIsLostRecordsAmbiguousAndBlocksFallback | PASS | |
| 계획 핵심 회귀 테스트: deliveredBeforeSentNeverDowngrades | PASS | |
| 계획 핵심 회귀 테스트: fcmInstallationAndLegacyTokenAreDifferentTypes | PASS | |
| 계획 핵심 회귀 테스트: http200IsProviderAcceptedNotDelivered | PASS | |
| 계획 핵심 회귀 테스트: ttlHeaderIsRequiredAndAcceptanceIsNotDelivery | PASS | |
| 계획 핵심 회귀 테스트: websocketFailureDoesNotRollbackInboxItem | PASS | |
| 계획 핵심 회귀 테스트: metricTagsNeverContainHighCardinalityIdentifiers | PASS | |
| 계획 핵심 회귀 테스트: acceptedThenResponseLossIsAmbiguousForEveryApplicableAdapter | PASS | |
| 계획 핵심 회귀 테스트: notificationPerformanceTest | PASS | |
| 설계·계획 날짜 일치 | PASS | |
| Java 21 가정 명시 | PASS | |
| 실제 저장소 부재 가정 명시 | PASS | |
| Provider SDK 공개 금지 | PASS | |
| Core async CompletionStage | PASS | |
| FCM FID 우선 | PASS | |
| Ambiguous fallback 금지 | PASS | |
| ProviderEvent 원장 | PASS | |
| ZIP CRC 무결성 | PASS | None |
| ZIP 필수 파일 | PASS | [] |
## 검증 범위
- 이 검증은 Markdown 설계서와 구현 계획서의 구조·정합성·필수 계약·경로 중복·미확정 표현·패키지 CRC를 검사한다.
- 실제 Backend Skeleton 저장소가 입력되지 않았으므로 Gradle compile, Provider sandbox, PostgreSQL integration, chaos, performance test 실행 결과는 포함하지 않는다.
- 구현 시에는 계획의 각 Task가 지정한 red-green TDD 명령을 실제 저장소에서 실행해야 한다.
@@ -0,0 +1,188 @@
#!/usr/bin/env python3
from __future__ import annotations
from collections import Counter
from pathlib import Path
import hashlib
import re
import sys
import zipfile
ROOT = Path('/mnt/data')
DESIGN = ROOT / 'notification-platform-design.md'
PLAN = ROOT / 'notification-platform-implementation-plan.md'
REPORT = ROOT / 'notification-superpowers-validation.md'
PACKAGE = ROOT / 'notification-superpowers-package.zip'
checks: list[tuple[str, bool, str]] = []
def check(name: str, condition: bool, detail: str = '') -> None:
checks.append((name, bool(condition), detail))
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open('rb') as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
digest.update(chunk)
return digest.hexdigest()
def main() -> int:
check('설계서 존재', DESIGN.is_file(), str(DESIGN))
check('구현 계획서 존재', PLAN.is_file(), str(PLAN))
if not DESIGN.is_file() or not PLAN.is_file():
return write_report()
design = DESIGN.read_text(encoding='utf-8')
plan = PLAN.read_text(encoding='utf-8')
design_lines = design.count('\n') + 1
plan_lines = plan.count('\n') + 1
check('설계서 최소 상세도', design_lines >= 3000, f'{design_lines:,} lines')
check('계획서 최소 상세도', plan_lines >= 4000, f'{plan_lines:,} lines')
check('설계서 코드 fence 균형', design.count('```') % 2 == 0, str(design.count('```')))
check('계획서 코드 fence 균형', plan.count('```') % 2 == 0, str(plan.count('```')))
required_design_terms = [
'NotificationRequest', 'RecipientDelivery', 'DeliveryAttempt',
'ProviderEvent', 'EvidenceLevel', 'SubmissionOutcome', 'DeliveryOutcome',
'AMBIGUOUS', 'append-only', 'FCM_FID', 'FCM_REGISTRATION_TOKEN_LEGACY',
'AES-256-GCM', 'HMAC-SHA-256', 'FOR UPDATE SKIP LOCKED',
'SMTP Adapter', 'Amazon SES Adapter', 'Twilio Adapter', 'FCM Adapter',
'APNs Adapter', 'Web Push Adapter', 'In-App Inbox', 'Reconciliation',
'N4 Admin Plane', '비지원 범위', '완료 정의'
]
for term in required_design_terms:
check(f'설계 핵심 계약: {term}', term in design)
check('exactlyOnce 단순 옵션 금지 명시',
'`exactlyOnce=true` 같은 단순 옵션을 두는 것은 잘못된 추상화' in design)
forbidden_design_claims = [
'guaranteedDelivery=true',
'APNs HTTP 200 = DELIVERED',
'FCM send success = DEVICE_DELIVERED',
]
for phrase in forbidden_design_claims:
check(f'금지 보장 부재: {phrase}', phrase not in design)
tasks = [int(x) for x in re.findall(r'^### Task (\d+):', plan, re.MULTILINE)]
check('Task 1~50 연속성', tasks == list(range(1, 51)), str(tasks))
task_chunks = re.split(r'(?=^### Task \d+:)', plan, flags=re.MULTILINE)[1:]
check('Task chunk 수', len(task_chunks) == 50, str(len(task_chunks)))
required_task_sections = [
'**Files:**', '**Interfaces:**', '**Implementation requirements:**',
'**Step 1:', '**Step 2:', '**Step 3:', '**Step 4:', '**Step 5:',
'Expected:', 'git commit -m'
]
for index, chunk in enumerate(task_chunks, 1):
for marker in required_task_sections:
check(f'Task {index} 필수 항목: {marker}', marker in chunk)
creates = re.findall(r'^- Create: `([^`]+)`', plan, re.MULTILINE)
duplicate_creates = sorted(path for path, count in Counter(creates).items() if count > 1)
check('Create 경로 중복 없음', not duplicate_creates, ', '.join(duplicate_creates))
check('Create 경로 충분성', len(creates) >= 200, str(len(creates)))
red_flags = {
'TODO': r'\bTODO\b',
'TBD': r'\bTBD\b',
'FIXME': r'\bFIXME\b',
'fill in details': r'fill in details',
'implement later': r'implement later',
'concrete assertion below': r'concrete assertion below',
'유사 작업 참조': r'Similar to Task',
}
for label, pattern in red_flags.items():
matches = re.findall(pattern, plan, re.IGNORECASE)
check(f'미확정 표현 없음: {label}', not matches, str(len(matches)))
key_plan_terms = [
'providerAcceptanceIsNotDelivery',
'concurrentSameRequestReturnsOneNotificationId',
'providerAcceptsThenResponseIsLostRecordsAmbiguousAndBlocksFallback',
'deliveredBeforeSentNeverDowngrades',
'fcmInstallationAndLegacyTokenAreDifferentTypes',
'http200IsProviderAcceptedNotDelivered',
'ttlHeaderIsRequiredAndAcceptanceIsNotDelivery',
'websocketFailureDoesNotRollbackInboxItem',
'metricTagsNeverContainHighCardinalityIdentifiers',
'acceptedThenResponseLossIsAmbiguousForEveryApplicableAdapter',
'notificationPerformanceTest',
]
for term in key_plan_terms:
check(f'계획 핵심 회귀 테스트: {term}', term in plan)
check('설계·계획 날짜 일치', '2026-08-10' in design and '2026-08-10' in plan)
check('Java 21 가정 명시', 'Java 21' in design and 'Java 21' in plan)
check('실제 저장소 부재 가정 명시', '실제 저장소가 제공되지 않아' in design)
check('Provider SDK 공개 금지', 'Provider SDK' in design and 'raw SDK client' in plan)
check('Core async CompletionStage', 'CompletionStage' in design and 'CompletionStage' in plan)
check('FCM FID 우선', 'FID 우선' in design and 'FCM primary target은 FID' in plan)
check('Ambiguous fallback 금지', 'ambiguousAttemptExists = true' in design and '`AMBIGUOUS` attempt가 있는 recipient' in plan)
check('ProviderEvent 원장', 'append-only ledger' in plan and 'ProviderEvent 원장' in design)
if PACKAGE.is_file():
try:
with zipfile.ZipFile(PACKAGE) as archive:
bad = archive.testzip()
names = set(archive.namelist())
required = {
'notification-superpowers-package/docs/superpowers/specs/2026-08-10-notification-platform-design.md',
'notification-superpowers-package/docs/superpowers/plans/2026-08-10-notification-platform-implementation-plan.md',
'notification-superpowers-package/README.md',
'notification-superpowers-package/VALIDATION.md',
'notification-superpowers-package/validate_notification_docs.py',
'notification-superpowers-package/MANIFEST.sha256',
}
check('ZIP CRC 무결성', bad is None, str(bad))
check('ZIP 필수 파일', required.issubset(names), str(sorted(required - names)))
except zipfile.BadZipFile as exc:
check('ZIP 열기', False, str(exc))
else:
check('ZIP 패키지 존재', False, str(PACKAGE))
return write_report(design_lines, plan_lines, len(creates))
def write_report(design_lines: int = 0, plan_lines: int = 0, create_count: int = 0) -> int:
passed = sum(1 for _, ok, _ in checks if ok)
failed = [(name, detail) for name, ok, detail in checks if not ok]
status = 'PASS' if not failed else 'FAIL'
rows = [
'# Notification Superpowers 문서 정적 검증', '',
f'- 결과: **{status}**',
f'- 실행 검사: **{len(checks)}개**',
f'- 통과: **{passed}개**',
f'- 실패: **{len(failed)}개**',
f'- 설계서: **{design_lines:,}행**',
f'- 구현 계획서: **{plan_lines:,}행**',
f'- 구현 Task: **50개**',
f'- Create 경로: **{create_count:,}개**',
f'- 설계 SHA-256: `{sha256(DESIGN) if DESIGN.exists() else "missing"}`',
f'- 계획 SHA-256: `{sha256(PLAN) if PLAN.exists() else "missing"}`',
'', '## 검사 결과', '',
'| 검사 | 결과 | 상세 |', '|---|---:|---|'
]
for name, ok, detail in checks:
safe = detail.replace('|', '\\|').replace('\n', ' ')[:500]
rows.append(f'| {name} | {"PASS" if ok else "FAIL"} | {safe} |')
rows.extend(['', '## 검증 범위', '',
'- 이 검증은 Markdown 설계서와 구현 계획서의 구조·정합성·필수 계약·경로 중복·미확정 표현·패키지 CRC를 검사한다.',
'- 실제 Backend Skeleton 저장소가 입력되지 않았으므로 Gradle compile, Provider sandbox, PostgreSQL integration, chaos, performance test 실행 결과는 포함하지 않는다.',
'- 구현 시에는 계획의 각 Task가 지정한 red-green TDD 명령을 실제 저장소에서 실행해야 한다.',
])
if failed:
rows.extend(['', '## 실패 항목', ''])
rows.extend(f'- **{name}**: {detail}' for name, detail in failed)
REPORT.write_text('\n'.join(rows) + '\n', encoding='utf-8')
print(f'{status}: {passed}/{len(checks)} checks passed')
if failed:
for name, detail in failed:
print(f'FAIL: {name}: {detail}', file=sys.stderr)
return 1
return 0
if __name__ == '__main__':
raise SystemExit(main())