/apt-saSemanticAnchor — project identity and context bootstrap.
/apt-sa Claude Code CLI 또는 연결된 에이전트에서 호출합니다.
SA answers ONE question: **"이 작업이 정확히 무엇이며, KG에서 어디에 위치하는가?"** SA는 단순한 이름 붙이기가 아니다. Anthropic의 Initializer Agent 패턴과 동일하게, 후속 Phase가 즉시 이어받을 수 있는 **실행 가능한 환경을 부트스트랩**한다. SA가 없으면 모든 후속 Phase(SP, ST, SCW)의 기반이 없다 (D9 GenerativeFlowOrdering).
### Inform 축 (Harness Engineering) 하네스 엔지니어링에서 Inform(정보 제공)은 에이전트에게 컨텍스트를 주입하는 축이다. SA가 정확히 이 역할을 수행한다: - **프로젝트 정체성**: 이 작업이 무엇인지 (이름, 도메인, 설명) - **KG 위치**: 기존 지식과의 관계 (INFORMED_BY 링크, 관련 앵커) - **실행 환경**: apt-progress.md, feature-spans.json, Context Budget ### Routing 패턴 (Anthropic Flow Patterns) SA는 Anthropic의 **Routing 패턴**에 대응한다: 1. 입력(사용자 요청)을 분류한다 2. 적절한 앵커/브랜치로 라우팅한다 3. 새 앵커 생성 vs 기존 앵커 재사용 vs 기존 앵커에 브랜치 추가를 결정한다 라우팅 결정이 잘못되면 이후 모든 Phase가 잘못된 컨텍스트에서 진행된다. **KG 탐색이 라우팅의 핵심**이며, 이것이 Step 1이 존재하는 이유다. ---
**절대 새 앵커를 먼저 만들지 말 것.** 이것이 SA의 가장 흔한 실수(E-SA1)이다.
먼저 KG에 이미 관련 앵커가 있는지 확인한다.
### 1-1. 기존 SemanticAnchor 전체 목록
```cypher
-- 모든 활성 앵커 조회
MATCH (sa:SemanticAnchor)
RETURN sa.name, sa.description, sa.domain, sa.status,
sa.context_budget_total, sa.created_at
ORDER BY sa.name
```
결과를 검토하여 현재 작업과 동일하거나 유사한 앵커가 있는지 확인한다.
이름뿐 아니라 description과 domain도 비교해야 한다.
### 1-2. 키워드로 관련 KG 노드 탐색
```cypher
-- 키워드로 관련 작업/개념 탐색
MATCH (n)
WHERE n.name CONTAINS $keyword
OR n.description CONTAINS $keyword
RETURN n.name, labels(n), n.description
LIMIT 20
```
키워드를 여러 개 시도한다. 프로젝트명, 도메인 용어, 핵심 기술 등.
관련 노드가 나오면 해당 앵커로 라우팅할 수 있는지 검토한다.
### 1-3. 기존 앵커의 Span 트리 확인
```cypher
-- 특정 앵커의 하위 구조 확인
MATCH (sa:SemanticAnchor {name: $sa_name})-[:HAS_ROOT]->(root)
OPTIONAL MATCH (root)-[:DECOMPOSES_TO*1..3]->(s) ### 2-A. 새 앵커 생성
KG 탐색 결과 관련 앵커가 없을 때만 실행한다.
```cypher
-- 새 SemanticAnchor 생성 (항상 MERGE로 중복 방지)
MERGE (sa:SemanticAnchor {name: $project_name})
SET sa.description = $description,
sa.domain = $domain,
sa.status = 'active',
sa.context_budget_total = 100000,
sa.context_budget_per_span = 8000,
sa.created_at = datetime(),
sa.updated_at = datetime()
RETURN sa
```
앵커 생성 직후 Root Span을 연결한다:
```cypher
-- Root Span 생성 및 연결
MATCH (sa:SemanticAnchor {name: $project_name, status: 'active'})
MERGE (root:AptSpan {name: 'SPAN_' + $project_name + '_ROOT'})
SET root.description = $root_description,
root.depth = 0,
root.status = 'open',
root.context_budget = 50000,
root.created_at = datetime()
SA는 고립된 정체성 노드가 아니다. **전체 시스템의 인덱스**다.
SA는 생성 시점부터 연결되어야 하고, 나중에 발견한 연결도 **소급 추가**한다.
### 필수 연결
```cypher
// 도메인 지식 연결
MATCH (sa:SemanticAnchor {name: $project})
MERGE (k:KnowledgeNode {name: $knowledge})
MERGE (sa)-[:INFORMED_BY {reason: $why}]->(k)
// 기술 스택 연결
MERGE (lib:KnowledgeNode {name: $library, source: 'library'})
MERGE (sa)-[:USES {reason: $why}]->(lib)
// 다른 프로젝트 연결
MATCH (other:SemanticAnchor {name: $related_project})
MERGE (sa)-[:RELATED_TO {reason: $why}]->(other)
// 인프라 연결
MERGE (infra:KnowledgeNode {name: $service, source: 'infrastructure'})
MERGE (sa)-[:DEPLOYED_ON]->(infra)
```
### 연결 종류
| Relation | 대상 | 예시 |
|----------|------|------|
| INFORMED_BY | 도메인 지식, 디자인 패턴 | SA → "ECS Pattern", "REST API Design" |
| USES | 라이브러리, 프레임워크, 외부 API | SA → "Three.js", "FastAP Context Rot(토큰 증가 시 n^2으로 주의력 분산) 방지를 위해 컨텍스트를 단계적으로 로드한다.
**절대 KG 전체를 한번에 로드하지 않는다** (E-SA2).
### L1: 메타데이터 (토큰 예산 ~2K)
프로젝트 정체성과 최상위 Span 이름/설명만 로드한다. KG 전체를 읽지 않는다.
```cypher
-- L1: SA + 직계 자식 메타데이터만
MATCH (sa:SemanticAnchor {name: $sa_name})-[:HAS_ROOT]->(root)
OPTIONAL MATCH (root)-[:DECOMPOSES_TO]->(l1)
RETURN sa.name, sa.description, sa.domain, sa.status,
root.name AS root_span,
l1.name AS l1_span, l1.description AS l1_desc, l1.depth
ORDER BY l1.name
```
**포함 항목:** SA name, description, domain, status, Root Span, L1 Span 이름/설명
**제외 항목:** Span 트리 구조, Contract, 소스코드, 테스트 결과
### L2: 구조 (토큰 예산 ~5K)
선택된 브랜치의 Span 트리와 Contract 존재 여부를 로드한다. **작업 대상 브랜치만** 로드한다.
```cypher
-- L2: 선택된 브랜치의 Span 트리 + Contract 존재 여부
MATCH (root:AptSpan {name: $branch})-[:DECOMPOSES_TO*1..5] SA에서 전체 프로젝트의 depth별 토큰 예산을 할당한다.
이 예산은 인지과학이 아닌 **공학적 휴리스틱**(C26)이다.
### Depth별 토큰 예산
| Span Depth | 토큰 예산 | 근거 |
|:----------:|:---------:|------|
| 0 (Root) | 50,000 | 프로젝트 전체 개요. L1 메타 + L2 구조 |
| 1 | 50,000 | 주요 모듈. 넉넉한 컨텍스트 필요 |
| 2 | 20,000 | 서브모듈. 범위가 좁아짐 |
| 3+ | 8,000 | AtomicSpan. 단일 파일 500줄 구현에 적정 |
### 할당 Cypher
```cypher
-- SA에 Context Budget 총량 설정
MATCH (sa:SemanticAnchor {name: $sa_name})
SET sa.context_budget_total = 100000,
sa.context_budget_per_span = 8000
-- Span 생성 시 depth 기반 자동 할당
MERGE (child:AptSpan {name: $name})
SET child.context_budget = CASE
WHEN child.depth = 0 THEN 50000
WHEN child.depth = 1 THEN 50000
WHEN child.depth = 2 THEN 20000
ELSE 8000 END
```
### Budget 초과 시 대응
- depth 0~1에서 50K 초과: L2 로딩 SA 완료 시 후속 Phase를 위한 파일을 생성한다.
### 5-1. apt-progress.md 생성
세션 연속성의 핵심 파일. 모든 Phase에서 참조하고 갱신한다.
```markdown
# APT Progress: {project_name} ---
### Completed Spans
(none yet)
### In Progress
- {current_span}: SA complete, ready for SP
### Blocked
(none)
### KG Stats
- SemanticAnchor: {sa_name}
- L1 Spans: {count}
- INFORMED_BY links: {count}
### Next Steps
1. SP Phase: {first_branch} 분해 시작
2. 각 L1 Span에 INFORMED_BY >= 5 확보
### Session Log
- [{datetime}] SA Phase: anchor {sa_name} created/reused
```
### 5-2. feature-spans.json 생성 (선택)
대규모 프로젝트에서 Span 목록을 구조화된 형태로 관리할 때 사용.
```json
{
"anchor": "{sa_name}",
"domain": "{domain}",
"created_at": "{datetime}",
"spans": [
{
"name": "SPAN_{project}_ROOT",
"description": "...",
"status": "open",
"depth": 0,
"context_budget": 50000
}
]
}
```
### 5-3. 기존 파일이 있는 경우
기존 앵커를 재사용하는 경우(Step 2-B) apt-progress.md가 이미 존재한다.
이 경우 새로 생성하지 않고 SA 결과물을 커밋한다. 이것이 SA의 마지막 단계.
```bash
git add apt-progress.md feature-spans.json
git commit -m "APT SA: Initialize {project_name} anchor
- SemanticAnchor: {sa_name}
- Domain: {domain}
- Context Budget: {total}K total, {per_span}K per span
- L1 Spans: {count}"
```
기존 앵커 재사용 시:
```bash
git add apt-progress.md
git commit -m "APT SA: Resume {project_name} - {branch_description}"
```
--- SA에서 SP로 전환하기 전 **모든 항목**을 확인한다.
### 핸드오프 체크리스트
| # | 체크 항목 | 검증 방법 |
|---|----------|----------|
| 1 | SemanticAnchor가 KG에 존재 | `MATCH (sa:SemanticAnchor {name: $sa}) RETURN sa` |
| 2 | SA status = 'active' | `sa.status = 'active'` 확인 |
| 3 | Root Span이 SA에 연결됨 | `MATCH (sa)-[:HAS_ROOT]->(root) RETURN root` |
| 4 | Progressive Disclosure L1 로드됨 | apt-progress.md에 L1 Span 목록 기재 확인 |
| 5 | Context Budget 할당됨 | `sa.context_budget_total IS NOT NULL` |
| 6 | apt-progress.md 생성됨 | 파일 존재 확인 |
| 7 | 기존 앵커 중복 없음 | Step 1의 KG 탐색으로 유사 앵커 부재 확인 |
| 8 | Git commit 완료 | `apt-progress.md` 커밋됨 |
### 핸드오프 검증 쿼리
```cypher
-- 핸드오프 준비 상태 종합 확인
MATCH (sa:SemanticAnchor {name: $sa_name, status: 'active'})
MATCH (sa)-[:HAS_ROOT]->(root:AptSpan)
WHERE sa.context_budget_total IS NOT NULL
RETURN sa.name, sa.st SA 완료 후 SP로 전환할 때 컨텍스트를 압축한다. ### 보존 (SP에 전달) - 앵커 이름, 설명, 도메인 - L1 Span 목록 (이름 + 설명) - Context Budget 설정 - apt-progress.md 현재 상태 ### 제거 (메모리에서 해제) - KG 탐색 과정 (어떤 쿼리를 실행했는지) - 앵커 후보 비교 로그 - 라우팅 의사결정 과정 - L2/L3 로딩 결과 (SP에서 필요할 때 다시 로드) ### 새 컨텍스트 (SP 시작 시 로드) - SA 결과 압축 요약 (앵커명 + 도메인 + L1 구조 1줄 요약) - 최근 5개 접근 파일 경로 - SP Phase 시작에 필요한 첫 번째 브랜치 정보 ---
SA 관련 무결성을 검증하는 쿼리들. 문제 발생 시 사용한다.
### V-SA1: 고아 Root Span (SA 미연결)
```cypher
MATCH (root:AptSpan)
WHERE root.depth = 0
AND NOT ()-[:HAS_ROOT]->(root)
RETURN 'V_SA1_OrphanRoot' AS validation, root.name
```
### V-SA2: SA 중복 (동일 프로젝트 이름)
```cypher
MATCH (sa:SemanticAnchor)
WITH sa.name AS sa_name, count(sa) AS cnt
WHERE cnt > 1
RETURN 'V_SA2_DuplicateAnchor' AS validation, sa_name, cnt
```
### V-SA3: Root Span이 없는 SA
```cypher
MATCH (sa:SemanticAnchor {status: 'active'})
WHERE NOT (sa)-[:HAS_ROOT]->()
RETURN 'V_SA3_NoRoot' AS validation, sa.name
```
### V-SA4: Context Budget 미할당
```cypher
MATCH (sa:SemanticAnchor {status: 'active'})
WHERE sa.context_budget_total IS NULL
RETURN 'V_SA4_NoBudget' AS validation, sa.name
```
--- SA는 5대 신기 중 **프로메테우스(지식 선행)**와 **롱기누스(KG 접지)**의 시작점.
| Rule | SA에서의 의미 |
|------|-------------|
| HR11 (증거 필수) | SA 생성 시 KG 조회 증거 필수. "KG에 없었다"도 증거 |
| HR14 (Reflection) | SA 완료 후: "이 앵커의 약점은?" 기록 → SP 진입 시 참조 |
```cypher
-- v21: SA 완료 후 Reflection 기록
MATCH (sa:SemanticAnchor {name: $project})
SET sa.v21_reflection = $weakness,
sa.v21_reflectedAt = datetime()
```
--- | 금지 사항 | 이유 | 해당 Phase | |----------|------|-----------| | 코드 작성 | 코드는 SCW(PH5)의 책임 | SCW | | Span 분해 | 분해는 SP(PH3)의 책임 | SP | | Contract 정의 | Contract는 ST(PH4)의 책임 | ST | | KG 탐색 없이 앵커 생성 | 중복 앵커가 가장 흔한 SA 오류(E-SA1) | - | | 전체 KG 한번에 로드 | Context Rot 발생(E-SA2). Progressive Disclosure 위반 | - | | apt-progress.md 없이 SP 진입 | 세션 연속성 단절. 다음 에이전트가 컨텍스트를 잃음 | - | | CREATE 사용 (MERGE 대신) | 중복 노드 생성 위험. 항상 MERGE 사용 | - |