feat: 설계 문서 추가
This commit is contained in:
@@ -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
|
||||
@@ -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가 해당 실행 검증을 추가하도록 구성되어 있다.
|
||||
@@ -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가 계획에 존재한다
|
||||
+5056
File diff suppressed because it is too large
Load Diff
+2608
File diff suppressed because it is too large
Load Diff
+164
@@ -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}")
|
||||
Reference in New Issue
Block a user