chore: initialize from backend template 0a6dd0e

This commit is contained in:
DongHyeonka
2026-08-13 20:31:02 +09:00
commit e64e701fe5
3223 changed files with 388401 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
# HTTP Client Superpowers 설계 패키지
이 패키지는 `Java/Spring 외부 HTTP Client 플랫폼 설계 심층 리서치`를 기반으로 작성한 설계서와 구현 계획서다.
## 파일
- `docs/superpowers/specs/2026-08-08-httpclient-platform-design.md`
- `docs/superpowers/plans/2026-08-08-httpclient-platform-implementation-plan.md`
- `VALIDATION.md`
- `validate_httpclient_docs.py`
## 구현 기준
- Java 21
- Gradle Kotlin DSL
- 공통 API는 Spring Framework 6.2 기준
- Spring Framework 7.0 호환성 검증
- Apache HttpClient 5 + RestClient
- JDK HttpClient + RestClient
- Reactor Netty + WebClient
- Jetty HTTP/3 Experimental
실제 Backend Skeleton 저장소가 제공되지 않았으므로 package 경로와 Gradle 구조는 설계서의 명시적 구현 가정이다. 구현 전 저장소의 기존 convention과 root package에 맞춰 경로만 조정하고 공개 계약과 정책 의미론은 유지한다.
@@ -0,0 +1,31 @@
# HTTP Client Superpowers 문서 검증
**검증 결과:** PASS
## 검증 항목
- 설계서 존재 및 최소 구조: PASS
- 구현 계획서 존재 및 최소 구조: PASS
- Task 번호 연속성: PASS
- Task별 Files·Interfaces·Step 1~5·Expected·Commit: PASS
- Markdown code fence 균형: PASS
- Placeholder scan: PASS
- 중복 Create 경로: PASS
- 핵심 설계 범위: PASS
- 핵심 구현 범위: PASS
## 통계
- explicitly forbidden signature documented: ApacheHttpClient nativeApacheClient()
- explicitly forbidden signature documented: HttpClient nativeJdkClient()
- explicitly forbidden signature documented: WebClient.Builder mutableBuilder()
- explicitly forbidden signature documented: RestClient.Builder mutableBuilder()
- design lines=1956, bytes=64493
- plan lines=3635, bytes=158401
- tasks=38, create_paths=306
## 결론
- 설계 결정과 구현 작업의 정적 추적성이 확인됐다.
- 실제 저장소가 제공되지 않았으므로 Gradle compile, integration, fault, security, performance test는 아직 실행되지 않았다.
- 계획의 Java 21, Gradle Kotlin DSL, root package는 명시된 구현 가정이다.
@@ -0,0 +1,148 @@
from pathlib import Path
import re
import sys
import zipfile
base = Path('/mnt/data')
design_path = base / 'httpclient-platform-design.md'
plan_path = base / 'httpclient-platform-implementation-plan.md'
errors = []
notes = []
def read(p):
if not p.exists():
errors.append(f'missing file: {p}')
return ''
return p.read_text(encoding='utf-8')
design = read(design_path)
plan = read(plan_path)
# Basic size and structure
if len(design.splitlines()) < 1200:
errors.append(f'design unexpectedly short: {len(design.splitlines())} lines')
if len(plan.splitlines()) < 2500:
errors.append(f'plan unexpectedly short: {len(plan.splitlines())} lines')
# Task continuity and task internals
matches = list(re.finditer(r'^### Task (\d+): (.+)$', plan, flags=re.M))
nums = [int(m.group(1)) for m in matches]
expected = list(range(1, (max(nums) if nums else 0) + 1))
if nums != expected:
errors.append(f'task numbers not continuous: {nums[:5]}...{nums[-5:] if nums else []}')
for i, m in enumerate(matches):
start = m.start()
end = matches[i+1].start() if i+1 < len(matches) else plan.find('\n## 3. Plan Self-Review Checklist', start)
if end == -1:
end = len(plan)
block = plan[start:end]
n = m.group(1)
for token in ['**Files:**', '**Interfaces:**', '**Step 1:', '**Step 2:', '**Step 3:', '**Step 4:', '**Step 5:']:
if token not in block:
errors.append(f'Task {n} missing {token}')
if 'git commit -m ' not in block:
errors.append(f'Task {n} missing commit command')
if 'Expected:' not in block:
errors.append(f'Task {n} missing expected result')
# Markdown fence balance
for name, text in [('design', design), ('plan', plan)]:
count = len(re.findall(r'^```', text, flags=re.M))
if count % 2:
errors.append(f'{name} has unbalanced code fences: {count}')
# Placeholder scan
patterns = {
'TBD': r'\bTBD\b',
'TODO': r'\bTODO\b',
'implement later': r'implement later',
'fill in': r'fill in',
'similar to task': r'similar to Task',
'placeholder': r'placeholder',
}
for name, text in [('design', design), ('plan', plan)]:
for label, pat in patterns.items():
if re.search(pat, text, flags=re.I):
errors.append(f'{name} contains placeholder pattern: {label}')
# Duplicate create path scan
create_paths = re.findall(r'^- Create: `([^`]+)`', plan, flags=re.M)
dupes = sorted({p for p in create_paths if create_paths.count(p) > 1})
if dupes:
errors.append(f'duplicate Create paths: {dupes}')
# Required design coverage
required_design_terms = [
'H1 Typed Service Client', 'H2 Generic Exchange', 'H3 Dynamic Target',
'ExecutionEvidence', 'BodyReplayability', 'OperationIdempotency',
'Named Client Profile', 'Apache HttpClient 5', 'Reactor Netty',
'Retry Coordinator', 'Circuit Breaker', 'Rate Limiter', 'Bulkhead',
'OAuth2', 'TLS', 'SSRF', 'Streaming', 'SSE', 'HTTP/3',
'Spring Framework 6.2', 'Spring 7', 'RestTemplate'
]
for term in required_design_terms:
if term not in design:
errors.append(f'design missing term: {term}')
required_plan_terms = [
'httpclient-core-api', 'httpclient-transport-apache', 'httpclient-transport-jdk',
'httpclient-transport-reactor-netty', 'httpclient-dynamic-target',
'httpclient-spring-boot-starter', 'HttpAmbiguousExecutionException',
'first response byte', 'DNS/IP Pinning', 'SingleFlightTokenLoader',
'httpClientStableContractTest', 'spring62CompatibilityTest',
'spring70CompatibilityTest'
]
for term in required_plan_terms:
if term not in plan:
errors.append(f'plan missing term: {term}')
# Core API should not deliberately expose native clients in design signatures.
for forbidden_signature in [
'ApacheHttpClient nativeApacheClient()',
'HttpClient nativeJdkClient()',
'WebClient.Builder mutableBuilder()',
'RestClient.Builder mutableBuilder()'
]:
# These appear in an explicit "do not provide" code block. Note rather than fail.
if forbidden_signature in design:
notes.append(f'explicitly forbidden signature documented: {forbidden_signature}')
# Record task count and file counts
notes.append(f'design lines={len(design.splitlines())}, bytes={len(design.encode())}')
notes.append(f'plan lines={len(plan.splitlines())}, bytes={len(plan.encode())}')
notes.append(f'tasks={len(nums)}, create_paths={len(create_paths)}')
report = base / 'httpclient-superpowers-validation.md'
status = 'PASS' if not errors else 'FAIL'
report_text = [
'# HTTP Client Superpowers 문서 검증', '',
f'**검증 결과:** {status}', '',
'## 검증 항목', '',
f'- 설계서 존재 및 최소 구조: {"PASS" if design else "FAIL"}',
f'- 구현 계획서 존재 및 최소 구조: {"PASS" if plan else "FAIL"}',
f'- Task 번호 연속성: {"PASS" if nums == expected else "FAIL"}',
f'- Task별 Files·Interfaces·Step 1~5·Expected·Commit: {"PASS" if not any("Task " in e for e in errors) else "FAIL"}',
f'- Markdown code fence 균형: {"PASS" if not any("code fences" in e for e in errors) else "FAIL"}',
f'- Placeholder scan: {"PASS" if not any("placeholder" in e for e in errors) else "FAIL"}',
f'- 중복 Create 경로: {"PASS" if not dupes else "FAIL"}',
f'- 핵심 설계 범위: {"PASS" if not any("design missing" in e for e in errors) else "FAIL"}',
f'- 핵심 구현 범위: {"PASS" if not any("plan missing" in e for e in errors) else "FAIL"}',
'', '## 통계', ''
]
report_text += [f'- {note}' for note in notes]
if errors:
report_text += ['', '## 오류', ''] + [f'- {e}' for e in errors]
else:
report_text += ['', '## 결론', '',
'- 설계 결정과 구현 작업의 정적 추적성이 확인됐다.',
'- 실제 저장소가 제공되지 않았으므로 Gradle compile, integration, fault, security, performance test는 아직 실행되지 않았다.',
'- 계획의 Java 21, Gradle Kotlin DSL, root package는 명시된 구현 가정이다.']
report.write_text('\n'.join(report_text) + '\n', encoding='utf-8')
print(status)
for note in notes:
print(note)
for e in errors:
print('ERROR:', e)
sys.exit(0 if not errors else 1)