/apt-spSemanticPyramid — recursive Span decomposition.
/apt-sp Claude Code CLI 또는 연결된 에이전트에서 호출합니다.
> **SP = 1개의 세계.** Span들이 레이어(L1→Ln)로 배치. > D(S)로 재귀 분해. 모든 leaf가 AtomicSpan이 될 때까지 반복. > 코드 없음. Contract 없음. 분해와 탐색만.
> `apt-gate-check.sh`가 자동 실행. SA Gate 미통과 시 `permissionDecision: deny`. > `$PROJECT`는 apt-progress.md의 `## Anchor:` 에서 읽는다. > BLOCKED 시: `/apt-sa` → `/taliban` → SA Gate 통과 → `/apt-sp` 재호출. ---
### Rule 1: SpanPlanningNature — Span은 의미 단위 Span은 코드 아티팩트가 아닌 **의미(semantic) 단위**다. - GOOD: "사용자 인증" — 의미 단위 - BAD: "auth.py" — 코드 아티팩트 - BAD: "AuthService 클래스 구현" — 구현 수준 상위 Span = 하위 Span을 만들기 위한 계획. 그 이상도 이하도 아님. ### Rule 2: DAG, Not Tree — N:N 관계 Span은 **반드시 트리일 필요 없다**. 핵심은 레이어(L1, L2, ..., Ln)가 있다는 것. 하나의 Span이 여러 부모를 가질 수 있다 (DAG). INFORMED_BY로 외부 지식(디자인 패턴, 기술, 도메인 개념)이 연결된다. ### Rule 3: A3 — 같은 레이어의 형제는 완전 독립 같은 레이어의 형제 Span 사이에 의존성 금지. 의존이 발생하면 → 분해 오류. 상위로 올려서 재분해. **DP의 independent subproblems = APT의 sibling independence.** ### Rule 4: TerminationGuarantee — 유한 단계에서 반드시 AtomicSpan 도달 모든 분해 경로는 유한 단계에서 AtomicSpan에 도달해야 한다. ---
```
D(S) = {S₁, S₂, ..., Sₙ} where n ≥ 2 (BranchingInvariant)
반복:
for each leaf S in SP:
if C(S) = true: → mark as AtomicSpan (더 이상 분해 안 함)
else: → D(S) 적용 (하위 Span 생성)
until ALL leaves are AtomicSpan
```
**DP 대응**: Span DAG = dependency DAG. DECOMPOSES_TO = subproblem 분해.
AtomicSpan = base case. Contract = subproblem 간 합의된 interface.
--- **모든 5개를 만족해야 AtomicSpan.** | # | Sym | Criterion | Auto/Human | 실패 시 | |:-:|:---:|-----------|:----------:|---------| | 1 | ν | Complexity ≤ 500줄 | auto | Split | | 2 | τ | Type expressibility — 구체적 I/O 타입 | auto | Split by type boundary | | 3 | ι | Test feasibility — 구체적 assertion 작성 가능 | auto | 예시로 명확화 | | 4 | δ | Decomposition diseconomy — 100줄 미만이면 합병 | auto | Merge upward | | 5 | σ | Semantic completeness — 의미 완결 | human | Narrow scope | ### δ (Decomposition Diseconomy) 상세 - **Sweet spot: 200~500줄** - 100줄 미만: 상위 Span과 합병 (독립 의미 단위로 존재할 이유 없음) - 500줄 초과: 더 분해 필요 (바이브코딩 최적 단위 초과) ### LeafSpan ≠ AtomicSpan - **LeafSpan**: 현재 하위 DECOMPOSES_TO가 없는 Span. **상태**일 뿐. - **AtomicSpan**: C(S) 5-predicate를 만족한다고 **판정**된 Span. - LeafSpan이 모두 AtomicSpan은 아니다. C(S) 확인 필수. ### L*_ 프리픽스는 레거시 식
각 Span에 INFORMED_BY ≥ 5 확보:
```cypher
MATCH (s:AptSpan {name: $SPAN})
OPTIONAL MATCH (s)-[:INFORMED_BY]->(k)
WITH s, count(k) as links
WHERE links < 5
RETURN s.name, links, "NEED MORE INFORMED_BY" as action
```
부족하면 → 프로메테우스 리서치 → KG에 KnowledgeNode 추가 → INFORMED_BY 연결.
--- ### Step 1: Root Span 확인
```cypher
MATCH (sa:SemanticAnchor {name: $PROJECT})-[:HAS_ROOT]->(root)
RETURN root.name, root.description, root.status
```
### Step 2: L1 분해 — 관심사(concern) 기준
Root를 **관심사 단위**로 분해. 파일이나 기술이 아닌 의미.
```
"이 문제를 어떤 독립적 subproblem들로 나눌 수 있는가?" (DP 사고)
```
### Step 3: 재귀 — 각 leaf에 C(S) 확인
```
for each leaf:
check C(S) 5-predicate
if ALL pass → mark AtomicSpan
if ANY fail → D(S) 적용 → 하위 Span 생성
repeat
```
### Step 4: Crystallization Frontier 도달 확인
```cypher
-- 모든 leaf가 AtomicSpan인지 확인
MATCH (sa:SemanticAnchor {name: $PROJECT})-[:HAS_ROOT]->(root)
MATCH (root)-[:DECOMPOSES_TO*1..10]->(leaf)
WHERE NOT (leaf)-[:DECOMPOSES_TO]->()
RETURN leaf.name, leaf.is_atomic,
CASE WHEN leaf.is_atomic = true THEN '✓ READY' ELSE '✗ NEED DECOMPOSITION' END as status
```
### 보존 (ST에 전달) - 전체 AtomicSpan 목록 + description - Span 간 DEPENDS_ON 관계 - INFORMED_BY 링크 - apt-progress.md 현재 상태 ### 제거 - 분해 탐색 히스토리 - C(S) 중간 결과 - RefinementGate 로그 ---
| 금지 | 이유 | 대안 | |------|------|------| | 코드 아티팩트로 Span 이름 | SpanPlanningNature 위반 | 의미 단위로 이름 | | 개별 Span을 바로 ST로 | 전체 leaf가 Atomic이어야 | 전부 끝날 때까지 SP | | 트리 구조 강제 | Span은 DAG | N:N 허용 | | 100줄 미만 AtomicSpan | δ-diseconomy | 상위와 합병 | | C(S) 확인 없이 AtomicSpan 선언 | LeafSpan ≠ AtomicSpan | 5-predicate 전부 확인 | | INFORMED_BY 없이 분해 | 근거 없는 분해 | KAL로 지식 확보 |