feat: 설계 문서 추가
This commit is contained in:
@@ -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 명령을 실제 저장소에서 실행해야 한다.
|
||||
+5324
File diff suppressed because it is too large
Load Diff
+4770
File diff suppressed because it is too large
Load Diff
@@ -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())
|
||||
Reference in New Issue
Block a user