Skip to content
Apothem
블로그

멀티 하니스 어댑터 설계의 내부

Apothem의 HarnessAdapter 프로토콜과, 하나의 프로필을 열일곱 개의 하니스로 머티리얼라이즈하는 세 가지 어댑터 클래스에 대한 기술적 심층 분석.

Apothem은 하나의 공유 프로필을 열일곱 개의 하니스 네이티브 구성으로 머티리얼라이즈합니다. 열일곱 개의 대상, 열일곱 가지 벤더 형식, 열일곱 개의 설치 위치 — 그리고 운영자와 CLI 둘 다가 의존하는 하나의 균일한 인터페이스. 이 글은 어댑터 계층이 그 계약을 어떻게 하나로 묶어 두는지 설명합니다.

HarnessAdapter 프로토콜

모든 하니스 어댑터는 구조적 HarnessAdapter 프로토콜을 구현합니다. 프로토콜은 CLI와 벤더 특화 세부 사항 사이의 솔기입니다. CLI가 호출하는 연산과 CLI가 읽는 메타데이터를 선언합니다.

  • name — 어댑터의 등록된 식별자.
  • output_path — 머티리얼라이즈된 구성이 디스크에 안착하는 위치.
  • install — 하니스 네이티브 구성을 렌더링하여 기록한다.
  • update — 프로필이 바뀐 뒤 하니스 네이티브 구성을 다시 렌더링한다.
  • uninstall — 공유 프로필은 건드리지 않은 채, 머티리얼라이즈된 구성을 제거한다.
  • is_installed — 하니스의 구성이 있는지 보고한다.
  • verify — 프로필과 디스크상의 구성 사이의 표류를 탐지한다.

계약이 typing.Protocol이므로, 어댑터는 상속이 아니라 형태로 그것을 충족합니다. 런타임에 CLI는 정적 레지스트리에서 모든 어댑터를 해석합니다. 그 레지스트리는 src/apothem/lib/harness_registry_data.py의 HARNESS_REGISTRY 테이블이며, harness_registry.py가 이를 런타임에 권위를 가진다고 선언합니다. 그래서 평범한 체크아웃은 sys.path에 설치된 진입점 메타데이터가 전혀 없어도 모든 어댑터를 로드합니다. pyproject.toml의 [project.entry-points."apothem.harnesses"] 테이블과 파일 시스템 discover_adapters() 스위프는 런타임 해석기가 아니라 규약 패리티 점검입니다. 테스트가 발견된 어댑터 집합이 정적 레지스트리와 동일함을 단언하므로, 규약상 올바르지만 등록되지 않은 어댑터는 조용히 해석되는 대신 커버리지 회귀로 드러납니다. 따라서 하니스를 추가한다는 것은 레지스트리 데이터에 이를 등록하는 것(그리고 패키징 패리티를 위해 진입점 테이블에 이를 반영하는 것)을 뜻합니다 — 둘 중 하나라도 빠뜨리면 패리티 테스트가 분명하게 실패합니다.

어댑터당 하나의 하위 패키지

각 어댑터는 src/apothem/harnesses/<name>/ 아래 자신만의 하위 패키지 안에 살아 있습니다.

  • __init__.py — 프로토콜을 구현하고 형제 액션 모듈에 위임하는 <Name>Adapter 클래스.
  • install.py / uninstall.py / update.py / verify.py — 어댑터 클래스가 임포트하고 호출하는, 액션별 구현.
  • materializer.py — 공유 프로필을 하니스 네이티브 구성 텍스트로 렌더링하는, 선택적 materialize_native_config(profile) -> str.

각 액션을 자신만의 모듈로 나누면 어댑터 클래스가 얇게 유지됩니다. 그것은 프로토콜 메서드를 액션 함수에 배선하는 디스패처입니다. verify.py를 여는 독자는 정확히 그 하나의 하니스에 대한 표류 탐지 로직만을 보며, 헤쳐 나가야 할 설치나 제거의 잡음이 없습니다.

세 가지 어댑터 클래스

열일곱 개의 하니스가 모두 같은 방식으로 구성을 소비하지는 않습니다. Apothem은 프로필이 하니스에 닿기 전에 얼마나 많은 변환이 필요한지에 따라 그것들을 세 클래스로 분류합니다.

클래스 I — 원시 전파

하니스는 Apothem이 이미 작성하는 바로 그 산출물 — 평평한 .md 규칙, 에이전트, 훅 — 을 렌더링 단계 없이 소비합니다. 어댑터는 원시 템플릿을 하니스의 구성 트리로 복사합니다. 아무것도 렌더링되지 않으므로 materializer.py는 없습니다. 프로필의 산출물이 곧 출력입니다.

클래스 II-A — 원시에 더해 벤더 템플릿

하니스는 Apothem의 원시 산출물 AND 일련의 벤더 특화 템플릿 파일(하니스가 요구하는 설정, 매니페스트 또는 스캐폴드 파일)을 소비합니다. 어댑터는 원시 산출물과 벤더 템플릿을 함께 복사합니다. 벤더 템플릿은 어댑터 옆 templates/ 디렉터리 아래에 살아 있습니다.

클래스 II-B — 프로필에서 렌더링

하니스는 공유 프로필의 구조화된 필드로부터 렌더링된 하나의 네이티브 구성 파일을 원합니다. 어댑터의 materializer.py는 프로필을 읽고 하니스 네이티브 텍스트를 내보냅니다 — Hermes의 config.yaml, Open-Claw의 openclaw.json, OpenCode의 opencode.json, 또는 Qwen Code의 settings.json. 클래스 II-B는 형식 조정 작업이 집중되는 곳입니다. 규약 수렴 심층 분석 이 이를 자세히 다룹니다.

claude-code: 전체 표면 어댑터

claude-code 어댑터는 가장 넓은 구성 표면 — 규칙, 에이전트, 훅, 출력 스타일, 설정, 그리고 규약 디렉터리 — 을 지닙니다. 그것은 클래스 II-A에 속합니다. Apothem의 원시 템플릿을 전파하고 규약 디렉터리를 평탄화하며, src/apothem/harnesses/claude_code/templates/ 아래에 벤더 설정 템플릿을 함께 실어 보냅니다. 그것은 materializer.py를 지니지 않습니다 — 프로필에서 네이티브 구성 파일을 렌더링하지 않고, 산출물과 규약 디렉터리를 직접 전파합니다.

의도된 경계가 하나 있습니다. claude-code 어댑터는 CLAUDE.md 안의 센티널로 구분된 블록 하나만 관리하며, 그 블록 바깥의 산문은 보존합니다. 그 파일의 나머지 부분은 운영자가 소유한 영역입니다. Apothem은 주변의 구성 표면(규칙, 훅, 설정)을 머티리얼라이즈하고, 그 블록 바깥의 운영자 산문은 그대로 둡니다. 파일 전체를 다시 쓰는 어댑터라면 apothem install을 실행할 때마다 손으로 작성한 지시가 지워질 것입니다. 이 어댑터는 자신의 관리 블록만 다시 씁니다.

균일성이 중요한 이유

이 프로토콜은 운영자에게 열일곱 개의 도구를 가로지르는 하나의 심성 모형을 줍니다. 대상이 단일 파일을 렌더링하든 디렉터리 트리를 평탄화하든, apothem install, apothem verify, apothem uninstall은 똑같은 방식으로 동작합니다. 벤더 차이는 프로토콜 뒤편, 어댑터 하위 패키지 내부 — 그것들이 있어야 할 자리 — 에 살아 있습니다.

운영자를 위한 참조는 하니스에서, 프로필 모델은 개념에서 읽어 보세요.

On this page