알려진 gap (정직 페이지)
문서는 “운영자가 1인 멀티테넌트 플랫폼을 잡고 갈 수 있도록 하는 사실 의 SSOT” 가 목표. 그러나 일부 항목은 미구현 / 단순화 / 향후 작업. 본 페이지는 그 격차를 명시.
시간축 4-페이지 분리 (2026-05-22, D-docs-updates-1):
- /ops/backlog = 현재 — 실행 큐 (
🆕→📋→🔧→✅→⏸️). 다른 세션의 entry point- /ops/roadmap = 미래 — M1~M5 마일스톤 큰 그림
- /ops/updates = 과거 — Ship Log + Highlights + /api/changes JSON feed
- 본 페이지 = (사실) — “왜 이렇게 됐는지 · 무엇이 함정인지” 의 분석적 사실 기록. entry point 아님
새 함정·미구현을 발견하면: 실행 가능한 항목 = backlog 🆕 / 사실·맥락 기록 = 본 페이지 / ship 됨 = updates.
deploy-SSOT guard = host-local 강제, 서버측 백스톱 없음 (2026-06-05)
D-ops-42 의 pre-push guard (컴포넌트 E) 는 이 Mac mini 의 로컬 git 훅이다. axe guard install 로 frame·hive·blueprint·stream·magnet·index + axe-cli(~/.axe/bin) 에 설치 — git push origin main 직접을 기계적으로 거부하고 axe ship(내부적으로 AXE_SHIP=1 set, axe line ~7606) 만 통과. 강제의 경계가 있다:
| 우회 벡터 | 사실 | 완화 |
|---|---|---|
| 다른 머신/CI 에서 같은 origin 으로 push | 로컬 훅이 없어 차단 안 됨 | 모든 개발 세션이 이 Mac mini 에서 돎 → 실 위협은 로컬 훅이 커버 |
git push --no-verify | git 내재 동작 — 클라이언트에서 pre-push skip, 클라이언트측 차단 불가 | 운영자 단일·악의 아님 전제로 수용 |
| GitHub branch protection (서버측 정답) | 현 plan 불가 — private repo 의 branch protection 은 Pro/Team 필요 (gh api repos/axelabs-ai/<svc>/branches/main/protection → 403 “Upgrade to GitHub Pro”) | plan upgrade 시 require-PR / restrict-push 로 권위 백스톱 추가 |
범위: guard 는 axe-ship 거버넌스 대상 한정 (위 6 서비스 + CLI). artemis/blurgram/vault/axelabs 등은 자체 배포 경로라 의도적으로 guard 밖 (universal 아님).
설치 함정 — tracked core.hooksPath: index 는 core.hooksPath=.githooks (추적 디렉터리, sqlx pre-commit 보유). guard install 이 거기에 pre-push 를 쓰면 untracked 로 노출 → 세션이 git add -A 하면 운영자-로컬 강제 훅이 공유 repo 에 커밋되어 clone/CI 의 정상 push 까지 잘못 차단한다. 2026-06-05 ~/index/.git/info/exclude 로 차단 (비전파·tree 무변·훅 실행엔 무영향). 나머지 6 repo 는 .git/hooks(git 미추적)라 무관. CLI 일반화 = B-axe-guard-hookspath-exclude.
SSH 세션은 GUI login keychain 에 못 쓴다 — vault 비밀 주입의 keychain 함정 (2026-06-09)
이 Mac mini 의 Claude Code 세션은 SSH(loopback) 위에서 돌고 운영자는 Windows 에서 ssh [email protected] (Tailscale) 로 접속한다. macOS 는 SSH 세션이 GUI login keychain 에 쓰는 것을 거부한다 (“User interaction is not allowed”). 그래서 vault 에 비밀을 넣는 keychain 의존 경로 — axe vault unlock (keychain-cache crash), axe secret push/pull/check (_vault_env 가 security find-generic-password -s axe.vault.session 으로 keychain 세션만 읽음 → “vault session not found”) — 이 전부 SSH 에서 실패한다. 현 워크어라운드 = keychain-free raw-bw (운영자가 자기 셸에서 NODE_EXTRA_CA_CERTS + 단독 줄 bw unlock --raw + raw bw create/edit, D-ops-44 / /architecture/secrets). 더 깊은 격차 = _vault_env 가 keychain 세션만 읽고 env-session(BW_SESSION/stdin) 폴백이 없다는 것 — 그래서 SSH 컨텍스트가 axe secret * 의 1급 경로가 못 되고 매번 raw-bw 로 우회해야 한다. 이 함정을 없애는 fix 후보 = axe secret put --stdin-session 또는 _vault_env 의 env-session 폴백 (있으면 SSH 에서도 axe secret push 가 그대로 동작). 비밀 주입과 달리 배포(axe ship/secret pull) 는 keychain 세션이 필요 — SSH 에선 security unlock-keychain headless unlock 후 axe vault unlock 으로 채운다. (전조: 이 keychain partition 격리 자체는 realchoice D-day 함정 #5 에서 customer 측으로 먼저 관측됨 — 본 항목은 운영자 macmini 의 SSH 세션 + 비밀 주입 축.)
OIDC 발행자가 자기 토큰을 401 (issuer ≠ resource server)
2026-06-04. D-axe-idp-1 후속. Blueprint 가 플랫폼 토큰을 발행하면서 자기 MCP 는 그 토큰을 거부 했다. axe login 토큰은 frame·hive·index·cortex·matrix 에선 동작하나 https://axe.axelabs.ai/blueprint/mcp 에선 401 (unknown_kid).
근본 원인: trust-migration 작업이 frame·hive·cortex·index·matrix 5개만 대상으로 잡고 Blueprint 자체 MCP 를 빠뜨렸다 — “issuer 가 곧 resource server” 가 자명해 보여 목록에서 누락. Blueprint MCP (blueprint_mcp/auth_oidc.py) 는 Microsoft Entra access_token 만 검증 (verify_microsoft_access_token) → 플랫폼 토큰의 kid (= Blueprint OIDC 서명키) 가 Microsoft JWKS 에 없어 unknown_kid → 401.
진단 단서 (같은 토큰을 두 서비스에 던져 비교):
blueprint/mcp→401 {"code":"UNAUTHORIZED","message":"unknown_kid: <kid>"}(Microsoft JWKS 에서 못 찾음)frame/mcp→401 {"code":"TOKEN_EXPIRED"}(= iss 분기·서명검증을 통과하고 exp 만 걸림 → frame 경로는 정상)unknown_kidvstoken_expired의 차이가 “이 서비스는 Blueprint issuer 분기가 아예 없다” 를 가리킨다.
Fix: frame auth_blueprint.py + http_server.py iss-dispatch 를 Blueprint MCP 에 미러 (mcp/src/blueprint_mcp/auth_blueprint.py 신규 + BLUEPRINT_ISSUER=https://blueprint.axellc.com compose env). forged-iss/bad-sig → 401 검증 (unverified iss-peek 가 auth 우회 안 함). 설계: /architecture/platform-identity.
교훈: OP 를 세울 때 그 OP 자신의 MCP 도 resource-server 목록의 한 행이다. 발행자라는 사실이 자동 신뢰를 주지 않는다 — 토큰 검증은 issuer 든 아니든 동일하게 명시 배선해야 한다.
잔여 (별개): axe-cli 의 blueprint local 엔드포인트 경로가 틀림 — :3151/mcp → 404 (proxy 는 /blueprint/mcp 서빙). public 은 정상. backlog B-axe-cli-blueprint-local-path.
Cloudflare Universal SSL 1-level
2026-05-26, D-ops-39. 무료 plan 의 *.axelabs.ai Universal SSL cert 가 1단 서브도메인만 cover 하는 함정. zone 안에 2단 hostname (e.g. ssh.axe.axelabs.ai) 을 만들면 edge 가 SAN 미일치 → default cert fallback → 클라이언트 TLS handshake 실패.
| 환경 | 증상 |
|---|---|
| Windows (Schannel) | SEC_E_ILLEGAL_MESSAGE (0x80090326) — cloudflared access login 시 failed to get app info: remote error: tls: handshake failure |
| macOS (LibreSSL/curl) | sslv3 alert handshake failure |
| 브라우저 (Chrome/Edge) | ERR_SSL_VERSION_OR_CIPHER_MISMATCH |
진단 분기점: cloudflared --version 최신 + 시스템 시간 OK + 브라우저/curl/cloudflared 3개 클라이언트 모두 동일 실패 = 서버측 cert 단정 (90%+ confidence). 동일 zone 안에서 1단 host (e.g. axe.axelabs.ai) 는 정상 동작하는 것도 단서.
컨벤션 (D-ops-39): HTTPS 노출되는 모든 hostname = {name}.axelabs.ai 의 1단. flat-hostname (예: ssh-axe) 또는 path under apex (axe.axelabs.ai/frame). 룰 본문 /architecture/domains#함정—universal-ssl-wildcard-의-1-level-한계-d-ops-39.
구체 사례 — ssh.axe.axelabs.ai (강태훈 Windows, 2026-05-25):
- 강태훈이
cloudflared access login https://ssh.axe.axelabs.ai시도 → 위 Windows 증상. - axelabs.ai chat 의 MAX agent 가 클라이언트 4단계 (cloudflared version / 시스템 시간 / TLS inspection / hostname 정확성) 진단 후 서버측 단정.
- 2026-05-26 운영자가
ssh-axe.axelabs.ai신규 등록 (DNS CNAME → 동일 tunneld8efecdd, ingress 규칙 clone, Access appb903d8cdself_hosted_domains 추가). 검증:curl -v https://ssh-axe.axelabs.ai/→ 302 toaxellc.cloudflareaccess.com.
옛 hostname 완전 삭제 (2026-05-26 같은 날 후속): 강태훈이 신규 hostname 으로 1차 재시도했으나 Microsoft SSO 통과 후 callback 이 옛 ssh.axe.axelabs.ai 로 떨어져 동일 cert 오류 재발. 추가 진단:
- Access app 의 primary
domain필드가 여전히ssh.axe.axelabs.ai였음 → post-auth callback / App Launcher 흐름이 primary 를 우선시. 1차 fix:domain을ssh-axe.axelabs.ai로 PUT swap. - 2차 fix (확정): 옛 hostname 흔적 3 layer 모두 제거 — (a) DNS CNAME
ssh.axe.axelabs.ai+*.axe.axelabs.ai와일드카드 삭제, (b) axelabs tunnel (d8efecdd) ingress 규칙 제거, (c) Access appself_hosted_domains/destinations에서 제거. 검증:dig ssh.axe.axelabs.ai→ NXDOMAIN. 클라이언트 측 cache (브라우저 쿠키 +%USERPROFILE%\.cloudflared\token) 정리 후cloudflared access login https://ssh-axe.axelabs.ai재시도 안내.
거부된 대안 ($10/cert/월 ACM, zone delegation, Tailscale 우회, 클라이언트 cert bypass): D-ops-39 본문 참조.
Microsoft 자사 앱 간 consent preauthorization 정책 — az CLI Mail.Send 영구 차단 (2026-05-26)
운영자가 az CLI 로
/me/sendMail호출하려고 시도 →403 ErrorAccessDenied. 명시적 scope (--scope https://graph.microsoft.com/Mail.Send) 요청 시AADSTS65002: Consent between first party application '04b07795-8ddb-461a-bbee-02f9e1bf7b46' and first party resource '00000003-0000-0000-c000-000000000000' must be configured via preauthorization.
az CLI 의 client app id 04b07795 (Microsoft Azure CLI 자체) 가 Microsoft Graph (00000003) 의 Mail.Send scope 받으려면 Microsoft 가 preauthorization 등록해야 함. 자사 앱 간에도 cross-app consent 안 됨. 개별 tenant 의 admin consent 로 우회 불가.
영향: az CLI 경유 mail/chat send 영구 불가. 우회 = (a) /api/admin/broadcast-dm (Blueprint REST, 본 use case 의 정공법), (b) Outlook 수동, (c) custom Azure app 신설 (Mail.Send delegated/application + admin consent + client_id/secret vault 저장, 30분+ setup — 사용 빈도 높을 시 B-axe-mail-send-cli 진행).
왜 운영자가 함정에 들어갔나: Blueprint 가 sendEmail() 함수 + Mail.Send permission 둘 다 보유 — 자연스레 “외부 connector 에서도 호출 가능” 가정. 실제는 graph_* tool 32+ 중 send 계열은 blueprint-graph 내부 MCP 에만 등록, 외부 7cb41f76 connector 는 read-only 격리 (의도). 외부에서 send 가 필요하면 admin REST (broadcast-dm 등) 별도 경로.
D-bp-mcp-calendar-2 send-as 도입 시 8개 함정 (2026-05-26)
Soohun 의 “ai 계정으로 다른 사용자 캘린더 쓸 수 있냐” 질문 → 1시간 안에 7개 별개 차단점 통과. 각각 다음 calendar/admin write feature 도입 시 동일 패턴.
Blueprint Azure App ID 혼동
Blueprint 가 별개의 Azure App 2개 보유:
2b222356-1c36-48e0-96a3-2c5e0ecbf937= Blueprint Next.js app (NextAuth Azure AD provider,getMsalApp()→getAppOnlyClient()가 사용). DB AppSettingazure_ad_client_id.482598f7-540c-462c-9dfd-b957651eb804= Blueprint MCP custom connector (Claude → MCP OAuth용). CLAUDE.md 의 “Client ID” 항목.
내부 API route 가 app-only token 발급할 때 audience = Next.js app (2b222356). Application permission + admin consent 모두 2b222356 에 적용해야 함. MCP app 482598f7 에 consent 하면 무관 → 403 ErrorAccessDenied 계속 반환. 본 세션 1회 잘못 적용 → MSAL token 디코드해서 aud 확인하고서야 발견.
Blueprint User.id ≠ Microsoft Entra oid
Blueprint User.id = Prisma 가 생성한 cuid (e.g. e1c51fa2-0102-43ed-...). NextAuth Azure AD callback 에서 Entra oid 를 별도 컬럼에 저장하지 않음 → /users/{User.id}/events 호출 시 Graph 가 ErrorInvalidUser 404. Graph /users/{key} 는 oid 또는 UPN/email 받음 — UPN 사용으로 우회. Route 가 target.email 을 path 에 인코딩하도록 fix. 향후 D-bp-mcp-calendar-3 이상에서 직접 oid 필요한 endpoint 도입 시 User 에 oid 컬럼 추가 검토.
MSAL acquireTokenByClientCredential 토큰 캐시
@azure/msal-node 의 ConfidentialClientApplication 은 client-credentials flow 토큰을 in-memory 캐시 (default TTL ~1시간). admin consent 직후에도 캐시된 옛 토큰 (consent 이전 발급, roles claim 누락) 이 계속 반환됨 → Graph 403 지속. container 재시작 또는 skipCache: true 옵션으로 강제 refresh. MSAL .default scope 가 “현재 consent 된 모든 perm” 의미라 cache 무효화 트리거 없음 (admin consent 가 MSAL instance 에 signal 안 보냄). 본 세션은 docker restart blueprint-app-green 으로 우회.
Global Admin = soohun.kang 단독 ([email protected] 은 admin 아님)
운영자 가정: “[email protected] 으로 az 로그인 되어 있고 관리자 권한이므로 tenant 작업 가능”. 실제: AXE 테넌트의 Microsoft Entra Global Administrator role = [email protected] 단독. [email protected] 은 일반 사용자. Authorization_RequestDenied 에러 시 userObjectId 필드 디코드 (Entra oid lookup) 로 실제 호출 identity 확인.
참고: Blueprint 자체 admin role (User.role === "admin") 과 Azure AD Global Admin 은 별개. [email protected] 은 Blueprint admin (Blueprint MCP send-as 호출 가능), 그러나 Azure AD admin 작업 (Application permission grant 등) 불가.
az login 무구독 테넌트
M365-only 테넌트는 Azure 구독 0개. az login --tenant <id> 만으로는 active context 가 직전 계정에 머무름 (No subscriptions 메시지 후 무시). --allow-no-subscriptions flag 필수. 확인: az account show 로 active upn 검증.
Claude.app /dev/ptmx fd leak
Claude 데스크탑 앱이 새 채팅/대화 열 때마다 /dev/ptmx open, close 안 함. 누적되면 kern.tty.ptmx_max=511 (macOS 하드 한도) 도달 → forkpty: Device not configured (ENXIO) → 신규 터미널·subprocess PTY 할당 불가. userspace 측 zsh 좀비 (Claude Code subprocess 누적 82 → 정리해도 무효) 가 아니라 Claude.app 본체 fd leak. lsof /dev/ptmx 로 holder 확인 가능.
해결: Claude.app 종료 후 재실행 (fd 자동 release). sudo 로 kern.tty.ptmx_max raise = “Invalid argument” (macOS 가 511 이상 거부). Anthropic 리포트 대상 (B-claudeapp-fd-leak-report backlog).
2026-06-04 재발 확인 (lsof /dev/ptmx: Claude.app PID 1개가 510/511 점유, 여유 0). 이때 신규 PTY 차단뿐 아니라 sudo 자체가 막힌다 — sudoers Defaults use_pty 탓에 sudo: unable to allocate pty: Device not configured. 세션을 안 끊고 root 작업하는 우회법 = osascript -e 'do shell script "…" with administrator privileges' (AuthorizationServices GUI 인증 → pty 불요, sudo 미경유). netheal 데몬(D-matrix-4) 설치를 이 방법으로 완료. 단 GUI 세션 필요 (GRD/콘솔 OK, 순수 SSH 불가). 항구적 회수는 여전히 Claude.app 재시작.
FastMCP tool 의 Image | dict union 반환 어노테이션
FastMCP tool registration 이 return type annotation 을 pydantic 으로 schema generate. mcp.server.fastmcp.utilities.types.Image 가 union (Image | dict) 안에 있으면 PydanticSchemaGenerationError: Unable to generate pydantic-core schema for <class 'Image'> → MCP 서버 startup 실패 → blue/green health check 60s timeout → swap 자동 거부. return annotation 생략 또는 Any 로 우회. 본 세션 get_teams_hosted_content 가 1회 발현 후 commit 3c7ae254 로 fix.
Pre-existing axelabs-docs {#anchor} MDX 3 acorn parse fail
별개 함정 (calendar 와 무관) 이지만 본 세션 docs ship 차단했음. ## Heading {#explicit-anchor} 문법이 Nextra (MDX 3) 의 acorn 파서를 깨뜨림 → Could not parse expression with acorn → npm run build 실패 → axe ship docs 의 docker rebuild 차단. MDX 3 에서 {#...} 명시 anchor 미지원: 헤딩 텍스트를 슬러그와 일치시키도록 변경 (github-slugger 자동 ID) 하고 inbound 링크 그대로 유지. 운영자 별도 commit 으로 동시 fix 됨 (race condition 으로 commit 941196f).
realchoice D-day 첫 실행 — 6 함정 (2026-05-25 발견)
Truvia 측 RE^6 의 D-day 즉시 시작 피벗 후 운영자가
axe onboard realchoice --apply실행. step 1-13 ✅ + step 14 차단. 본 섹션은 분석적 기록 (실행 항목은 B-onboard-d-day-traps-2026-05-25).
| # | 함정 | 본질 | 영구 fix 후보 |
|---|---|---|---|
| 1 | Tailscale short host alias (realchoice-macmini) macOS resolver 미해석 → ssh fail | macOS resolver 는 magicDNS short alias 자동 처리 X. ~/.ssh/config 의 Host alias + HostName FQDN 매핑 필수. axe CLI 가 short alias 로 ssh 호출 | partner/macmini-prep 함정 표 + axe onboard 가 자동으로 ~/.ssh/config 추가 또는 ssh 명령에 -o "Host alias=FQDN" 사용 |
| 2 | SSH non-login shell PATH 에 /usr/local/bin 누락 → docker not found | macOS 의 zsh non-login 은 ~/.zshrc 안 봄. ~/.zshenv 만 봄. Docker Desktop 의 symlink (/usr/local/bin/docker) 가 default PATH 밖. Truvia 측 ~/.zshenv 에 export PATH=/usr/local/bin:/opt/homebrew/bin:$PATH 추가 필요 | partner/macmini-prep 의 §5 Docker 섹션에 ~/.zshenv 명시 (즉시 갱신 필요) |
| 3 | TXT + CNAME 같은 name 공존 시 axe onboard step 5 의 _cf_dns_find(zone, name) 가 type filter 없어 거절 | CloudFlare API 는 type filter 미적용 시 모든 record 반환. axe CLI 코드가 “exists” 로 단정 → conflict 처리. RFC 측면 TXT+CNAME 공존 OK | axe CLI _cf_dns_find(zone, name, record_type) 시그니처 변경 + onboard step 5 가 record_type="CNAME" 명시 |
| 4 | Login keychain locked + SSH non-interactive → security add-generic-password exit 36 (errSecAuthFailed) | macOS GUI session 의 keychain unlock 이 SSH non-interactive session 의 securityd partition 으로 propagate 안 됨 | 함정 5 가 본질적 fix (vault SOT 이전) |
| 5 | macOS keychain partition 격리 — SSH non-interactive 의 securityd 가 GUI session 과 별도 partition | Truvia 측 GUI 로그인 + timeout 10h 연장 했지만 SSH non-interactive session 영향 0. 즉 axe onboard 의 step 12 (secret generation) 가 SSH 으로 호출하면 fundamental 차단. 본 D-day 의 핵심 함정. 즉시 우회 = Truvia 가 본인 SSH session 에서 직접 명령 실행 (operator → customer 권한 위임 패턴) | (a) start-frame.sh 를 customer launchd job 으로 등록 → console session 의 keychain access. (b) secret SOT 를 keychain 에서 vault 로 이전 (D-ops-17 manifest pattern 확장). bw CLI 는 SSH session 에서도 작동 |
| 6 | start-frame.sh wrapper 가 SSH non-interactive 에서 security find -w (value fetch) 시도 → ACL 차단 → FATAL: FRAME_JWT_SECRET empty | 함정 5 의 구체적 발현. wrapper 가 keychain value 꺼내야 frame postgres + frame-mcp-blue 부팅 가능. SSH 으로 호출 시 차단. axe CLI line 2588 의 step 12 fix (-w 제거, metadata-only) 는 step 12 통과시키나 step 14 wrapper 의 fetch 는 동일 차단 | 함정 5 와 동일 영구 fix |
즉시 우회 (본 세션 적용):
- 함정 1:
~/.ssh/config신설 (Host alias + HostName FQDN) - 함정 2: Truvia 측
~/.zshenvPATH 추가 - 함정 3: Microsoft verify 완료 후 TXT 제거 + axe CLI 재실행
- 함정 4-5: Truvia 가 본인 SSH session 에서
security add-generic-password직접 실행 - 함정 6: ⏳ Truvia 회신 대기 (옵션 A = 본인 SSH session 에서
start-frame.sh up -d직접 / 옵션 B =security set-generic-password-partition-listACL 변경)
영구 fix 적용 (2026-05-26, B-customer-deploy-generalization Phase 1 완료):
- 함정 4·5·6 → vault SoT 이전. axe CLI 의
_deploy_service_customer+ 새 wrapper 3종 (start-{frame,blueprint,hive}.sh) 이 customer 측bw get password으로 secret fetch. SSH non-interactive 의 keychain partition ACL 회피 (bw 는 파일 기반~/.bw-session으로 BW_SESSION 획득 → securityd 무관). customer 측 1회 부트스트랩 =brew install bitwarden-cli+bw-bootstrap.sh https://<vault-url> <email>(interactive). 본 변경 후 함정 4·5·6 = 다음 customer 에서 재발 0. - 함정 10 (env_file
$literal) →escape_dollar: trueflag 자동 처리. INGEST_MANIFEST_TEMPLATE 의 OAuth secrets 에 빌트인. - 함정 7 (docker-compose default path hardcode) →
image_override슬롯으로 customers.yaml 에서 override 가능 (R4). compose 본체 변수화는 service repo 측 별도 PR. - 함정 8 (axe onboard step 14 non-idempotent) → 이미 axe CLI line 2720+ 에
docker ps사전 검사 + skip if running 패치 적용 (5/25). 신규_deploy_service_customer도 동일 패턴 적용 필요 (TODO).
미해소:
- 함정 1·2·3 = docs 측 갱신 필요 (partner/macmini-prep 의 함정 표 + axe CLI
_cf_dns_find시그니처 변경). - 함정 9 (frame-proxy stale archive) →
/Users/axe/.axe/frame-proxy/5/24 복원 ✅. axe CLI 가 정상 경로 참조.
1-shot onboard 갭 — realchoice (2026-05-23 분석)
목표: D-day 에 운영자가 vault master password 1 회 입력만으로 신규 customer (realchoice 등) 전체 배포 완료. 현재 6 개 수동 touchpoint 가 남음. /ops/runbook/customer-onboarding#d-day-tldr—운영자-수동-touchpoints 가 SSOT.
⚠️ 갱신 (2026-06-06): realchoice 는 2026-05-25 sovereignty self-deploy 로 LIVE 완료 (
customers.yaml에서services:의도 제거). 본 섹션의 realchoice 예시는 역사적 — 잔여 갭은services:매니페스트를 선언하는 신규 customer 에만 적용되고, realchoice 자체엔axe deploy {svc} realchoice가 미적용된다 (→ /ops/runbook/customer-onboarding sovereignty caveat).
| # | 갭 | 현재 상태 | 닫는 작업 |
|---|---|---|---|
| 1 | axe customers add {customer} = stub (line 560-561) | customers.yaml 의 customer 메타블록 + services 슬롯 모두 운영자 수동 편집 | B-onboard-customers-add |
| 2 | customer IT 회신 8 개 값 → vault 로 운영자 수동 push × 3 + customers.yaml 수동 paste × 4 (tenant_id + 3 client_id) | 안전채널 받은 값 운영자 손에서 vault 로 이동 | B-onboard-azure-pack — axe customer ingest {customer} pack.json 으로 (a) yaml fill + (b) vault push 묶음 |
| 3 | services: 섹션이 axe customer 만 등재 (realchoice services 슬롯 부재) | axe secret push --customer realchoice 가 manifest lookup 실패 | B-onboard-customers-add 결과물의 일부 |
| 4 | axe deploy hive {customer} subcommand 부재 | axe deploy choices = frame / blueprint / blueprint-mcp / hive / matrix (cmd_deploy_hive customer-path live, .axe/bin/axe) | B-onboard-hive-deploy |
| 5 | cmd_deploy_blueprint docstring 자기 명시: “SSO (Azure AD) 미설정 — Phase 3 추가 작업 필요” | Blueprint 컨테이너는 부트되지만 첫 SSO 로그인 시 secret 부재로 실패. 운영자가 별도로 customer Keychain 또는 vault → .env 채워야 함 | B-onboard-bp-sso |
| 6 | Cloudflare API token vault 등재 = first-onboard chicken-and-egg | 첫 onboard 직전 1 회 axe secret push 필요 (현재 docs 부재) | B-onboard-cf-token-doc — secrets.mdx 의 bootstrap 섹션에 명시 1 줄 |
해소 후 D-day 흐름 (목표):
# 0. vault unlock — master password 1 회
export BW_SESSION="$(bw unlock --raw)" && security add-generic-password ...
# 1. customer IT 회신 한 줄 ingest (yaml fill + vault push 묶음)
axe customer ingest realchoice ~/Downloads/realchoice-azure-pack.json
# 2. 모든 stack 묶음 배포 (frame + blueprint + hive + vault)
axe deploy all realchoice --apply현재 drift 발견 (본 sweep, 2026-05-23):
customer-onboarding.mdx 의 D-7 step 3-5 가→ 정정 완료. 실제/Users/axe/.axe/tunnels/axelabs/config.yml편집 +docker restart axelabs-tunnel요구axe onboard는 customer 별 독립 tunnel 을 customer macmini 에 생성 (_render_cloudflared_config/Users/{ssh_user}/.cloudflared/config.yml). 중앙 tunnel 편집 의무 사라짐.→ 정정 완료 (B-onboard-bootstrap-publish, 2026-05-23). 스크립트가axelabs-bootstrap.sh+realchoice_entra_id_setup_v3.md가 별도 안전채널 메시지로 전달되는 의존https://docs.axelabs.ai/axelabs-bootstrap.sh의 raw 로 노출. partner/registration §Option A + macmini-prep + domain-prep §A/§B + handoff JSON pack 양식 + index 4-step 흐름으로 docs.axelabs.ai 만으로 customer IT 자력 완료 가능. customer-facing 차단은 0. 잔여 6 갭 (위 표) 은 모두 운영자 측 자동화 (umbrella + ingest + hive deploy + bp sso + cf token) — 운영자의 D-day 명령 수를 5→1 로 줄이는 것이 우선순위. customer 측에는 영향 없음.
CLI 측 미구현 (axe CLI Phase 5 stub)
| 약속된 CLI | 실제 상태 | 우회 |
|---|---|---|
axe restore --customer X --tier local|ring|cold --target X --table X --apply | argparse stub (--tier, --target, --table, --apply flag 없음) | restic 직접 호출 (각 runbook 의 갱신된 예시 참조) |
axe backup --status / axe backup --local --tag X | axe backup subcommand 없음 — /Users/axe/.axe/bin/axe-backup shell script 가 매일 03:00 자동 | restic snapshots 직접 |
axe onboard --skip-azure | --skip-azure flag 없음 (--skip-frame 만 있음) | onboard 가 Azure 측 변경 안 함 — flag 없어도 무방 |
axe health <target> --customer X | --customer flag 없음 (positional 만) | axe health frame 처럼 사용 |
axe secret status --customer X | --customer flag 없음 | 운영자 콘솔 dashboard 에서 확인 |
axe ship (release-gate) | ✅ 구현됨 (D-ops-16) | — |
axe secret check/pull/push/rotate (manifest 기반) | ✅ 구현됨 (D-ops-17). pull merge-mode (D-ops-18) | — |
axe deploy blueprint-mcp (blue/green swap) | ✅ 구현됨 (D-bp-mcp-2, 2026-05-21) — frame cmd_deploy(frame) 1:1 미러 | — |
axe ship blueprint 안에서 mcp swap 자동 호출 | ✅ 구현됨 (D-bp-mcp-2 후속, 2026-05-21) — cmd_blueprint_upgrade 직후 cmd_deploy_blueprint_mcp wire-up | — |
→ Phase 5 (D) 작업 항목으로 CLI 완성 예정. 그때까지 runbook 의 명령어는 “현재 형식” 으로 사용.
매니페스트 / vault 측 미해결
| 항목 | 현재 상태 | 후속 |
|---|---|---|
| 매니페스트 non-secret config 흡수 결정 (Option A: 매니페스트 확장 / B: merge-mode 만 / C: customers.yaml 통합) | merge-mode pull (D-ops-18) 로 즉시 위험 X. blueprint .env 의 AZURE_AD_CLIENT_ID 등 9개는 hand-maintain | 운영 안정 후 정책 결정 |
[email protected] Vaultwarden service account | 미생성. 현재 모든 bw 호출이 운영자 개인 token | D-ops-17 Phase 6 |
cmd_blueprint_upgrade Keychain → vault 통합 | 현재 Keychain inject (line 698) + env_file 동시 사용. 두 출처 sync 책임 운영자 | vault 단일 출처로 통합 |
CLAUDE_CODE_OAUTH_TOKEN 회전 | 2026-05-21 transcript 노출. 사용자 보류 (후순위) | Anthropic console 새 토큰 발급 → axe secret rotate |
hive HIVE_MAILER_* 매니페스트 미등재 | .env.local 에 hand-maintain 중 (D-hive-23) | customers.yaml hive.secrets[] 에 추가 |
hive postgres HIVE_DB_PASSWORD substitution (compose 변수) | volume reset 시점 default hive_dev 로 fall back 위험. 현재 data dir 에 vault 값 이미 init | postgres 에도 env_file 도입 또는 .env 명시 |
bw CLI [Encrypt service] MAC comparison failed. ... Failed to decrypt user key with stretched master key — 재발성 (5/22 .broken.1779431724 + 5/26 .broken.1779783301). KDF 원인 아님 ([email protected] 이미 Argon2id). | Root cause 확정 (5/26 검증): bw CLI 의 local data.json 의 cached cryptoSymmetricKey (wrapped user key) 가 server-side patch deploy 후 bw sync 시점에 stale 상태로 저장됨. bw unlock 은 이 캐시를 invalidate 안 함 → 매 unlock 마다 stale wrapped user key 로 decrypt 시도 → MAC fail. axe Vaultwarden 의 빈번한 fork patch deploy (axe.2, axe.3, …) 가 트리거. | 즉시 복구 (확정): mv "~/Library/Application Support/Bitwarden CLI" "~/Library/Application Support/Bitwarden CLI.broken.$(date +%s)" && bw config server https://axe.axelabs.ai/vault && bw login [email protected] (fresh login). 약 1분. → B-bw-cache-stale-autoheal 영구 fix |
launchd 측 미구현
| 약속된 launchd | 실제 상태 |
|---|---|
com.axe.secret-check (매일 09:00) | 미구현. Vault item 의 만료 메타 + 운영자 콘솔 dashboard 에서 수동 확인 |
com.axe.health-check (매분) | 미구현. com.axe.console.refresh (매시) 가 운영자 콘솔에 health 표시. 매분 알림은 향후 추가 |
axe-health-monitor 바이너리 | 미구현 |
대안: 운영자 콘솔 (https://admin.axelabs.ai) 이 매시 rebuild 되며 health 표시. 즉시 알림이 필요하면 osascript 또는 Slack webhook 으로 임시 wrapper 작성 가능.
Docs 측 내부 불일치 (정리 진행 중)
| 항목 | 현재 상태 | 정리 계획 |
|---|---|---|
partner/registration.mdx 신모델 (Web platform + secret + App ID URI + mcp.access) | 작성됨 | axe customer 는 이미 신모델로 마이그됨. realchoice 도 6월 onboard 전 신모델로 적용 |
realchoice_entra_id_setup_v3.md | ✅ 작성 완료 (2026-05-21) | 발송 (5/29 마감) |
axe_frame_mcp_setup.md (구모델 기록) | historical | 본인 실작업 기록 — 사실 정확성 위해 보존 |
frame/docs/ops/onboarding-operator.md §0 (구모델 Azure 등록) | obsolete | registration.mdx 로 통합 → 향후 cross-link |
customers.yaml.axe.sso.apps.blueprint_mcp | ✅ 추가됨 (D-bp-mcp-1) | — |
| frame ahead commits 의 docs drift (auth_oidc.py, cli.py, alembic, Dockerfile 등) | operator hive integration 세션 작업분 | operator 의 axe ship frame 사이클에 함께 |
정확한 tool count
| 서비스 | docs 표기 | 실제 (코드 기준) | 비고 |
|---|---|---|---|
| frame | ~44 (수정됨) | 44 (grep -c '@mcp.tool' src/frame/mcp/server.py) | OK |
| stream | 28 (수정됨) | 28 (admin=7, sales=5, inventory=4, settlement=3, health=3, bridge=2, signals=3, meta=1) | OK |
| magnet | 39+ headline | 51+ 표 합산, 자기 진단 1 포함 시 ~62 데코레이터 | 표 합산이 실제와 다름 — magnet 측 통합 daemon 의 자체 보고가 39 |
magnet 의 39 vs 51+ 차이는 magnet 측 mcp/server.py 의 importlib 흡수 시 일부 중복/내부 도구 제외. magnet team 정리 후 docs 갱신.
hive 골든테스트 ↔ 라이브 데이터 결합 (2026-06-04)
tests/payroll/test_golden_axec_2026_04.py 가 라이브 axec DB 의 직원 종료상태에 의존 (자체 시드 없이 compute_period 실행). 골든 픽스처 (golden_axec_2026_04.json) 가 강수훈 (AXEC-001) 을 terminated 2026-04-01 로 인코딩 → CI 통과를 위해 실 직원행을 수동 (out-of-band, audit_log empty-actor) UPDATE 하게 만든 것이 강수훈 axec 종료의 근본원인 (2026-06-04 agent 조사·DB 검증). 같은 압력으로 axev/AXEV-003 강수훈도 수동 종료됐으나 그는 AXEV 파트너 직급 — 정합화는 backlog B-hive-axev-003-terminated. 구조적 해소 (테스트를 트랜잭션 내 자체 시드 또는 전용 test entity 로 분리) 는 backlog B-hive-seed-integrity. 가드: “terminated 인데 active membership/open employment_record” 면 fail 하는 invariant 테스트.
gh pr create ↔ repo redirect 함정 (origin=soohunkang/* → axelabs-ai/*) (2026-06-04)
로컬 git remote origin 이 soohunkang/<repo> (구 이름) 인데 GitHub 가 axelabs-ai/<repo> 로 redirect (repo transfer — fork 아님: gh repo view soohunkang/hive → isFork:false, parent:null, nameWithOwner:axelabs-ai/hive). 이 상태에서:
- ❌
gh pr create … --head soohunkang:<branch>(fork 스타일 head) → 존재하지 않는 fork owner 해석 시도 → GraphQLSomething went wrong500 (무한 반복). plaingh pr create도 redirect/fork-resolution GraphQL 에서 간헐 500. - ✅ REST + plain same-repo head:
gh api --method POST repos/axelabs-ai/<repo>/pulls -f head="<branch>" -f base="main" -f title=… -f body=…. 머지도gh api --method PUT …/pulls/<n>/merge -f merge_method=squash. git push origin은 redirect 통과(axelabs-ai 에 정상 반영). 진단 순서: GitHub status green +gh api rate_limit정상이면 장애 아님 → head 형식/redirect 의심하고 REST 로 우회. 모든 세션이 동일 origin 사용 → 공통 함정 (2026-06-04 hive PR #5 에서 ~6회 헛시도 후 규명).
운영 자동화 미완성
| 항목 | 현재 |
|---|---|
분기 restore drill (com.axe.restore-drill) | launchd 등록 + plist 존재. 첫 실제 drill 시기 (Jul 15) 까지 검증 |
| Cold SSD rotation | tooling ready, SSD 자체 미구매 (operator 작업) |
| Realchoice ring backup | 양방향 SSH 검증 완료 (2026-05-15). realchoice onboard (6/1) 후 첫 sync 시작 |
| mysrt-postgres backup 정책 미명시 | mysrt-postgres 컨테이너 운영 중, axe-backup 범위 밖. mysrt 가 SRT 폴링 (외부 SOT 가 source-of-truth 가능성) 이라 의도적 제외일 수도 / 누락일 수도. architecture/backup.mdx 에 “백업 대상에서 의도적 제외” 명시 또는 추가 결정. 검출 2026-05-21. |
Blueprint 측 미완 (2026-05-21 세션 발견)
| 항목 | 현재 상태 | 후속 |
|---|---|---|
Blueprint MCP get_session + list_messages tool | 9 read-only tool 중 Message 본문 외부 비공개 → agent transcript blind. 5분야 진단 (2026-05-21) 의 최대 빈 곳 | Stage 1 전 추가. PrismaClient member-scoped pattern |
cmd_deploy_blueprint_mcp edge probe URL 부정확 | 200 expected 인데 cloudflared 가 /blueprint/mcp/health path 보존 + JWTAuthMiddleware 가 Bearer 인증 요구 → 실제 응답 401 (정상 도달). axe deploy blueprint-mcp 가 매 swap 에서 ”⚠ edge not 200” 경고 출력 | URL 을 /blueprint/mcp (auth-required endpoint) 로 + expected 401 + WWW-Authenticate: Bearer 헤더 검증 |
axe blueprint upgrade active color resolve 오작동 | _blueprint_active_color() 가 stopped 컨테이너 metadata 를 alias 보유로 인식 → 실제 active 인 컨테이너 rebuild 시도 위험 (2026-05-21 회피: docker compose 직접 호출) | docker inspect 결과에 State.Running == true 필터 추가 |
| Trinity scheduled reconcile 부재 | src/lib/trinity-sync.ts:1-15 가 사용자 클릭 trigger / resolveLocalPath self-heal 만. 6/16 row drift 가 사용자 trigger 까지 잔존 | cron 또는 launchd 일일 reconcile |
/axe/personas UI 부재 | API src/app/api/personas/route.ts:11-19 만, page 없음 | persona 자기 큐레이션 UI |
/api/health 단순 200 | 14줄, DB/Postgres/Graph upstream probe 없음 | upstream probe + Cloudflare health-gate 신뢰 회복 |
| IC DATA-FIX mode | SKILL.md:140-173 4-mode (INITIAL/APPEND/REVISION/FINALIZE) 만. 숫자 정정 시 강제 v-bump | DATA-FIX mode 추가 |
| PARA dispatch UI (Project 종결 시 Area/Resource 로 artifact 이관) | schema 부분 완료 (PR #339 의 sourceWorkspaceId / sourceArtifactPath / copiedAt 3 필드). D-bp-entity-2 정식 등재. UI 만 잔여 (PR 5 예정). 미결 3종: 분배 단위 (파일 vs 의미) / Area 인스턴스 정의 권한 (org-admin vs free) / 정비 트리거 우선순위 | Path B Spike (DB 변경 없이 단일 workspace 로 LLM 분배 정확도 검증 3-5일) → Path A 본구현 (Area/Resource UI + dispatch modal + archive search separation) |
| Settings UI default entity dropdown | /api/user/default-entity PATCH endpoint (D-bp-entity-7) 있으나 UI 부재. 사용자가 직접 변경 못 함 — 운영자 SQL 또는 customers.yaml 순서 의존 | /axe/settings/SettingsClient.tsx 에 dropdown 한 줄 (~30 min) |
| axe entity register 통합 명령 | hive register-entity + Blueprint seed-entities.ts 갱신 두 명령 분리. 부분 등록 함정 (한 시스템만 등록되고 다른 시스템 안 됨) 가능 | axe CLI entity register <slug> --name ... --biz-no ... subcommand. hive subprocess + Blueprint Entity row INSERT atomic |
blueprint-mcp + hive-mcp /health anon expose | JWTAuthMiddleware 가 모든 path (/health 포함) Bearer 요구. frame-mcp 는 /health anon. axe CLI edge probe 가 401 expected 로 현재 workaround. frame 패턴 mirror 가 본질 fix | FastMCP middleware exclusion list 에 /health 추가 (blueprint-mcp + hive-mcp 각각). axe CLI expected 401 → 200 정정. mcp-server-checklist 에 항구화 |
| Teams attachment unsupported contentType → LLM false-negative | src/lib/teams/attachments.ts:138 의 unsupported-loop 가 [unsupported contentType: X] stub 만 LLM 에 넘김. LLM 은 stub 만 보고 “X 못 봤다” 그럴듯한 답 confabulate (실제 API 한계와 구분 불가). messageReference 1 종은 PR #372 에서 fix. card / file / 기타 contentType 미점검 | B-bp-teams-attachment-contenttype-audit. production 관측 contentType enumerate + handler 추가 또는 placeholder 명확화. feedback_bot_capability_gap_self_diagnosis |
| Knowledge Layer 격차 — typed fact 부재 | ctx skill 이 markdown PKM 만 제공. Per-field citation / cross-functional query (frame typed × portfolio markdown join) / time-travel / 결정론적 충돌 해소 모두 불가능. 인프라는 dev-co level (per-customer isolation / blue-green / restic backup) 인데 knowledge layer 만 single-operator PKM tool 수준 | M6 Blueprint artifact + PARA 지식 레이어 가 본 격차 직접 대응. D-bp-artifact-1~5 (2026-05-23 등재). 아키텍처 페이지 |
Multi-tenant 외부 출시 차단 (Stage 0 → 1)
5분야 진단 (2026-05-21) 결과:
| 항목 | 현재 | 후속 |
|---|---|---|
| Org tenancy FK fanout | Workspace/UsageLog/Agent 에 organizationId FK 부재 (prisma/schema.prisma:688 “별도 PR” 주석). single cross-tenant 쿼리 누락 = 전 고객 노출 | D-bp-org-fanout 신규 결정 + migration. D-bp-entity-1 의 entityId FK 와 별개 (entity = 회계 단위, org = customer 단위) |
| Azure AD tenant env-lock | src/lib/auth.ts:101 AZURE_AD_TENANT_ID 단일 env → 외부 IdP 분기 0 | per-org Organization.azureTenantId + auth.ts multi-tenant Azure AD provider |
법무 페이지 / rate-limit / Sentry / axe-health-monitor | TOS / Privacy / DPA / PIPA 페이지 0건. edge rate-limit 부재. monitor 미구현 (위 launchd 측 미구현 참조) | Stage 1 closed beta 진입 prereq |
| Backup tier-A | ✅ blueprint-postgres dump 포함 (2026-05-21 backup.mdx 갱신) | restore drill 1회 (Jul 15) |
MCP 공통 함정 (D-bp-mcp-3 cross-cutting, 2026-05-22)
D-bp-mcp-3 본질이 frame · hive 에도 부분 적용. 양파껍질 6 layer 의 한 갈래:
| 서비스 | SQLite legacy 잔재 risk | startup probe (lifespan) | 비고 |
|---|---|---|---|
| blueprint-mcp | ✅ 해소 (D-bp-mcp-3 2026-05-22) | ✅ 적용됨 | .env line 제거 + config fail-fast + lifespan SELECT 1 |
| frame-mcp | N/A (URL self-construct, env string 안 씀) | ❌ 미적용 — /health/ready 는 request-time only (frame/src/frame/mcp/http_server.py:594) | broken DB 로 부팅 가능, swap promote 위험. lifespan probe 1 블록 추가 권장 (blueprint pattern mirror) |
| hive-mcp | N/A (frame 패턴 동일) | ❌ 미적용 — hive/src/hive/mcp/http_server.py:364 | 동상 |
복붙 메시지 (운영자가 frame/hive owner 에게 전달): /architecture/mcp-server-checklist#16 startup probe pass 참조. 5-10 분 작업.
claude.ai 멀티-커넥터: 1 커넥터의 나쁜 key 가 전체 tools/list 400 (2026-05-28)
claude.ai 는 활성화된 모든 커넥터의 tool 을 하나의 tools 배열로 합쳐 API 에 보냄. 따라서 어느 한 커넥터 tool 의 inputSchema property key 가 ^[a-zA-Z0-9_.-]{1,64}$ 를 위반하면 → 400 tools.N.custom.input_schema.properties 로 요청 전체 거부 → 그 대화의 모든 커넥터(결백한 것 포함)가 같이 막힘. tools.N 의 N 은 결합 배열 인덱스라 커넥터 on/off 시 바뀜 → 범인 특정이 어려움 (Cortex 함정 #6 의 cross-connector 확장판).
- 2026-05-28 발견: cortex register_person 이
tools.15/tools.10으로 막힘. cortex(12)·frame(51)·hive(39) 라이브 tools/list + Claude-in-Chrome 22 스키마 직접 검증 → 전부 clean. 범인 = claude.ai 의GitHub 연동(Anthropic 제공 통합) — 우리가 소스 못 고침. 사용자가 그 커넥터만 비활성화 → 즉시 해소. - 진단법 (사용자에게 커넥터 토글 반복 요청 금지): 각 서비스의
mcp-tokenCLI(frame/hive) 또는 HS256 수동 mint(cortex)로 로컬tools/list(blue 컨테이너 직접) 받아 모든 property key 를 정규식 검증. Anthropic 커넥터(GitHub 등)는 소스 접근 불가 → 소거법으로 좁힌 뒤 사용자가 해당 커넥터 비활성화 + 앱 내 피드백 신고. - 예방: 우리 서비스는 trap #6(ASCII-only key) 준수. CI/
axe mcp publish에 “served tools/list 의 모든 property key 정규식 lint” 추가 권장 (backlog 가치).
신규 ship 된 MCP tool 이 이미 연결된 Claude 세션·서브에이전트에 안 보임 — tools/list 재동기 지연 (2026-05-29)
Blueprint MCP
send_mail(D-bp-mcp-mail-1) ship + self-send Graph 202 검증 직후, 운영자의 active Claude Code 세션에서 호출 →No such tool available. 같은 세션에서 spawn 한 서브에이전트도 동일 (ToolSearch 4회 모두 미발견).
MCP 클라이언트(Claude Code / claude.ai)는 연결 handshake 시점의 tools/list 를 캐시한다. 서버에 tool 을 새로 배포해도 이미 연결된 세션은 재동기 전까지 못 본다. 서브에이전트(Agent tool)는 부모 세션의 MCP 연결을 공유하므로 스폰으로도 해소 안 됨 (stale registry 상속).
- 결정적 해결 = 새 세션:
spawn_task칩 / 세션 재시작 / 커넥터 reconnect(/mcp). fresh handshake → freshtools/list. - 자가 해소되기도 함: 본 건은 일정 시간 후 클라이언트가 재-list 하여 같은 세션에
send_mail이 deferred tool 로 떠 호출 성공(재시작 없이). “전파 지연”이지 영구 차단 아님 — 타이밍은 비결정적. - 오진 주의: “tool 미발견 = 배포 실패” 단정 금지. 서버 측 검증(self-send 202 / 컨테이너 직접
tools/list)이 통과하면 배포는 정상, 클라이언트 캐시 문제다. - 운영 수칙: MCP tool ship 후 같은 세션 즉시 호출 실패는 정상. 급하면 새 세션(칩/재시작), 아니면 잠시 후 재시도.
Cortex production live — 7 함정 (2026-05-28, 신규 Rust MCP 서비스 첫 배포)
cortex (Rust + axum + sqlx, 첫 비-Python MCP 서비스) 를 claude.ai connector 까지 live 시키며 밟은 함정. index (D-index-2) 가 cortex 1:1 미러라 그대로 상속 — 등재 순서대로 회피할 것.
| # | 함정 | 증상 | 회피 |
|---|---|---|---|
| 1 | DB password 의 base64 /·+·= 가 connection URL 파싱 깨뜨림 | error with configuration: invalid port number — postgres://user:pa/ss@host:port 에서 / 뒤가 port 자리로 오인 | sqlx PgConnectOptions::new().host().port().username().password() 빌더 사용 (URL string 조립 금지). cortex src/db.rs::connect_options |
| 2 | docker compose env_file: 가 따옴표를 literal 로 처리 | axe secret pull 이 KEY="val" 로 쓰면 컨테이너 안 env = "val" (따옴표 포함) → postgres auth 실패 / secret mismatch. compose .env (variable substitution) 은 따옴표 strip 하므로 postgres init 은 unquoted, mcp 컨테이너만 quoted → 불일치 | env_file 값은 unquote. 임시: sed -i '' -E 's/^([A-Z_]+)="(.+)"$/\1=\2/'. 영구: B-axe-secret-pull-noquote (axe secret pull 이 quote 안 붙이게) |
| 3 | RFC 9728 resource-level metadata path 누락 | claude.ai 가 <application_id_uri>/.well-known/oauth-protected-resource (= /cortex/mcp/.well-known/...) fetch 시도 → 404 (server-level /cortex/.well-known/... 만 있으면 부족) → discovery 실패 → origin /authorize fallback | server-level + resource-level 둘 다 라우트. D-ops-23 frame 학습 — cortex 가 미반영했다 재발 |
| 4 | RFC 8414 path-insertion — authorization_servers 를 자기 (path 있는 issuer) 로 두면 안 됨 | authorization_servers: ["https://axe.axelabs.ai/cortex"] → claude.ai 가 RFC 8414 §3.1 따라 https://axe.axelabs.ai/.well-known/oauth-authorization-server/cortex (host 와 path 사이 삽입) 에서 metadata 찾음 → 우리가 serve 하는 /cortex/.well-known/... (path append) 와 불일치 → 404 → origin /authorize fallback → blueprint 404 | authorization_servers 를 Microsoft 직접 (login.microsoftonline.com/<tenant>/v2.0) 로. claude.ai 가 Microsoft OIDC metadata fetch → Microsoft endpoint 사용. self-hosted AS proxy 불필요 (D-cortex-8, frame “직접-Microsoft” path) |
| 5 | claude.ai connector 는 client_id + client_secret 입력 필수 | URL 만 입력 시 confidential client credential 없어 OAuth 미완 | Advanced field 에 client_id (Entra appId) + client_secret 입력. frame/hive/blueprint 와 동일 (D-vault-mcp-catalog, axe mcp publish 로 Bitwarden 확장 auto-suggest) |
| 6 | MCP tool inputSchema property key 는 ASCII only | 한글 key ("메모") → Anthropic API 400 tools.N.custom.input_schema.properties: Property keys should match pattern '^[a-zA-Z0-9_.-]{1,64}$' → 해당 connector 의 tools/list 전체 거부 (1개 나쁜 key 가 12 tools 모두 막음). claude.ai 에서 silent fail | property key 는 ASCII (memo). 한글 값은 additionalProperties: true 로 런타임 전달 가능 (key 만 제약). cortex commit dfa9330 |
| 7 | Cloudflare 터널이 Dashboard remote-managed | 로컬 ~/.axe/tunnels/axelabs/config.yml 편집 + 컨테이너 재시동해도 ingress 안 바뀜 (cloudflared log 의 Updated to new configuration version=N 이 remote 적재 증거). axe-tunnel 와 axelabs-tunnel 이 다른 컨테이너 (전자 = axellc.com zone, 후자 = axelabs.ai zone) — 잘못된 것 재시동하기 쉬움 | Cloudflare Dashboard 또는 API (PUT /accounts/<acct>/cfd_tunnel/<uuid>/configurations, token = vault Cloudflare API - axelabs) 로 ingress 변경. axe 의 _cf_request helper 가 이미 있음 → B-axe-tunnel-add-ingress CLI 화 권장 |
부수: (a) Docker image 는 cargo build (호스트) 와 별개 — 코드 변경 후 docker compose up -d --build 필수 (안 그러면 옛 binary). (b) shred 는 macOS 미존재 (Linux 전용) — rm -P 또는 APFS+SSD 에선 rm 으로 충분. (c) Google OAuth consent 2026-05-29 Testing → production 전환 (client 135512942819-l18ra7gkf1ac93ai8t4hf2h4jl6mi0a2.apps.googleusercontent.com, project no. 135512942819). Testing 시절 함정(Test users 목록의 Gmail 만 허용·100명 한도·미등록=403 access_denied, + refresh_token 7일 만료로 sync 주기적 단절 위험)은 production 전환으로 해소. 잔존 함정: 스코프 auth/contacts 는 Google sensitive 인데 앱이 unverified → 동의 화면에 “확인되지 않은 앱” 경고 + unverified 사용자 100명 상한. 사용자는 고급 → “<앱>(으)로 이동(안전하지 않음)” 으로 통과(동작엔 지장 없음). 경고 제거 = Google 정식 검증 → B-cortex-google-oauth-verify (금전비용 0 — contacts 는 sensitive 라 CASA 제3자 보안평가 불필요; restricted 였으면 수천 $/yr). Internal user type 은 Workspace 조직원 한정이라 개인 Gmail 연동엔 부적합.
Cortex — attended relationship 방향 불일치 (2026-05-29 PM12, 미해결): interaction↔person 의 attended 엣지가 두 생성 경로에서 반대 방향. log_interaction MCP = from=interaction → to=person, xlsx_hpe/backfill = from=person → to=interaction. 따라서 “interaction 의 참석자” 를 한 방향만 가정해 쿼리하면 절반 누락. 현재 대응: web fetch_attended_participants 가 $iid IN (from,to) 후 반대쪽 endpoint=person 으로 양방향 흡수 (display 레벨 해소, 36 interaction 전부 resolve). 미해결: 데이터 정규화(36건 방향 통일) migration 미실시 — 새 코드가 “interaction 의 attended” 를 짤 때 반드시 양방향. 우회 invariant 는 cortex CLAUDE.md 운영 함정에도 기록.
index 측 — proceeds 산정 정밀도 (2026-05-29, D-index-22 audit)
EV→EqV(net debt) bridge + 투자 후 dilution 이 7-deal 에 비일관 적용된 사실 (사용자 audit 발견). D-index-22 가 schema/validate 로 강제 (proceeds_basis enum + Check 7 stake×(EV−net debt) ±2% reconcile, broken bridge hard-reject) 하고 Sendy 는 ev_bridge 로 정정 (net debt 0, IRR 불변 6.47%). 잔여 분석적 사실:
| 항목 | 현재 상태 | 후속 |
|---|---|---|
| Iippo·Canopy legacy_ev | EV-based 이나 per-leaf 에 exit_ev/net_debt/stake 미itemize → validate Check 7 = legacy_ev WARN 2건. Canopy 는 net debt 300억 을 model-level exit_assumptions 에 명시(EqV 498억)했으나 per-leaf 분해 안 됨; Iippo 는 asset-light net debt~0 | B-index-proceeds-bridge-retrofit — per-leaf ev_bridge 전환 (40+/30+ leaf) |
| Iippo·Sentry entry-F/D-flat dilution | exit proceeds 가 entry fully-diluted stake (Iippo 2.065% / Sentry | Closed deal 이라 사후 정정 실익 낮음 — retrofit 시 exit_stake 를 round-별 희석 반영값으로. pre-IPO 3 + Sendy 는 이미 exit-date 희석 stake 명시라 해당 없음 |
| net debt = screening 가정 (BS 부재) | Sendy net debt 0 은 dataroom 에 BS 없어 asset-light + 자금니즈 equity 충족 근거의 보수적 screening 가정 | DD 단계 실 BS 확보 시 exit_net_debt_krw 한 줄 수정 → proceeds·IRR 자동 재산출 (deterministic) |
frame 측 미완 (회계 도메인 확장)
| 항목 | 현재 상태 | 후속 |
|---|---|---|
| AXEV 창업기획자 펀드 회계 | Migration #1 + #3 일부 완료 (2026-05-22, D-ops-22). shared.entity 에 entity_kind/fund_meta/closed_at ADD + shared.entity_relationship 확장 (numerator/denominator/unit + gp_managed_fund/lp_invested_fund) + shared.cross_journal_link 신설 + register-entity CLI --kind/--fund-meta flag + alembic 0008_shared 적용 + frame-mcp blue/green rebuild + tests/test_shared_fund_domain.py 16 PASS. axec/axev/axtest 자동 entity_kind=‘corporate’. 강태훈 대표 2026-05-23 자료 ingest 예정 (Evidence + entity meta + 통장내역 raw_transaction). 잔여: 펀드 전용 계정과목 (출자금·미출자약정·평가손익·분배금) + capital call / waterfall + AXEV↔조합 cross-entity mirror 분개 (GP commitment·운용보수·성과보수) 모두 미구현 | Architecture (2026-05-21 채택 + 2026-05-22 drift 정정 + 2026-05-22 Migration #1 적용): • 기존 shared 확장 ✅완료 (frame_meta schema 신설 폐기 — 코드 탐색 결과 기존 객체와 명명 충돌 + cross-entity 메타 layer 가 이미 shared 에 존재): ① shared.entity 에 entity_kind (corporate/kip/kvf) + fund_meta JSONB + closed_at 컬럼 ADD ✅. ② shared.entity_relationship (PR #26) 에 ownership_numerator/denominator/unit 컬럼 ADD (% 폐기, 반올림 오차 회피) + kind ENUM 확장 (gp_managed_fund/lp_invested_fund 추가) ✅. 의미 매핑: entity_a=holder, entity_b=target. ③ shared.cross_journal_link 신설 (mirror 분개 pair, schema 격리 환경 무결성) ✅.• fund schema 내부 (미구현): commitment_ledger (LP 약정/call/미출자 off-BS) + lp_master (조합원 + optional external_entity_id) + (optional) fund_waterfall_state.• fund 계정 seed (미구현): 기존 shared.account_template 에 새 standard 'fund_ksme' 추가 + 펀드 계정 row.• Blueprint = workspace↔entity scalar 1:N ( Workspace.entityId 단일). 사용자 결정 (2026-05-22, PR #339 ): OneDrive /Ventures/ /Corporation/ 폴더 분리가 SOT, fund 회계의 cross-entity 는 User.entityScopes 로 처리. N:M WorkspaceEntity join 권고는 reject (over-engineering).• 권한 enforcement = Blueprint middleware (gatekeeper). frame 은 entity_id 동등 처리. 기존 entity_session search_path 패턴 (entity_id == schema_name) 유지.• 회계 원칙: AXEV↔fund = parent-child 아닌 운용 관계 (K-IFRS 1110 통제 미달). relation_kind 가 회계 처리 갈래 (지분법/공정가치/운용보수) 결정. Migration 순서: (1) ✅완료 shared 확장 + cross_journal_link 신설 (alembic 0008_shared) [D-ops-22], (2) fund_ksme standard + seed [D-frame-N2], (3) ✅일부완료 (CLI --kind/--fund-meta) — 잔여: MCP register_entity tool (CLI 의존 제거) + list_sub_entities tool [D-frame-N3], (4) Blueprint WorkspaceEntity migration [D-bp-entity-1 수정], (5) 결성 조합 schema bootstrap + shared.entity row [조합 detail 대기], (6) fund schema 의 commitment_ledger/lp_master + 과거 결산 적재, (7) cross_journal_link 활용한 mirror 분개 tool [D-frame-N4] |
frame shared.entity 의도 불명 row (axep)
2026-05-26 audit 발견. shared.entity 의 axep (legal_name “액스파트너스 유한책임회사”, entity_kind=corporate, accounting_standard=ksme, 2026-05-22 08:56 등록) 의 등록 의도가 확인되지 않음. audit_log 은 account 한 table 의 chart seed 만 (운영 데이터 0). actor null, biz_no 없음, git 검색 결과 코드에도 언급 0.
후속: 강수훈 의도 확인 → (a) 실제 액스파트너스 유한책임회사 설립 의도면 보존 + biz_no/fund_meta 보강, (b) 단순 placeholder/test 면 DROP SCHEMA axep CASCADE + DELETE shared.entity. 본 row 는 D-frame-register-entity-atomic 2026-05-26 cleanup 사이클에서 보존 결정 (low-risk to leave, 강수훈 confirm 대기).
디자인 시스템 배포 (@axe/ui · axelabs.ai)
| 항목 | 현재 상태 | 후속 |
|---|---|---|
@axe/ui 패키지 발행 채널 | git+ssh 직접 임포트 (pnpm add 'git+ssh://...axelabs#tag'). dist 빌드 없이 raw TS export — 소비자는 transpilePackages: ["@axe/ui"] 명시 필요. npm registry / Verdaccio / GitHub Packages 모두 미사용 | v0.1 stable 후 GitHub Packages 검토. 현재 React 소비자 ≤3개라 git+ssh 로 충분 |
axelabs.ai 도메인 라이브 | ✅ LIVE — apex + www + docs + design 모두 tunnel 경유 200 (2026-05-29 검증). 컨테이너 axelabs (127.0.0.1:3900), axelabs-docs (3140). | (완료) |
design.axelabs.ai 디자인 시스템 도메인 (D-ui-3) LIVE | ✅ 2026-05-29 — ui.axelabs.ai → design.axelabs.ai 완전 교체. DNS proxied CNAME (axelabs.ai zone, CF API) + tunnel remote config v21→v22 (ui→design 1-rule swap) + axelabs rebuild (proxy.ts host 분기). 검증: design 200(@axe/ui 페이지)·sub-path 308·ui 404·axelabs.ai/www/docs 무영향 | 함정 4: ① tunnel ingress = remote-managed (config.yml 무효 — 본 페이지 “Cortex production live 7함정” #7) → PUT /accounts/<acct>/cfd_tunnel/<uuid>/configurations (token=vault Cloudflare API - axelabs). ② axelabs.ai zone DNS 는 반드시 CF API 토큰 — cloudflared tunnel route dns 는 cert-scoped(axellc.com) 라 <host>.axellc.com 오생성 (이번에 design.axelabs.ai.axellc.com 생겨 삭제함). ③ host 분기 = proxy.ts (next.config rewrites 아님 — 정적 prerender / 우회). ④ 신규 1-level host 직후 로컬 resolver AAAA-only → IPv6 무라우팅 머신 curl HTTP 000 (production 무관, curl -4/--resolve 검증) |
| Clash Display @import drop 함정 | Fontshare URL 의 f[]= 대괄호가 Next.js CSS @import 빌더에서 떨궈진다 (현 16.2.6 확인). 해결: <link rel="stylesheet"> 를 layout 의 <head> 에 직접 추가. Pretendard·D2Coding @import 는 정상 | 다른 서비스 마이그레이션 시 같은 함정 안 빠지게 README + /ui#consume 에 명시. 향후 Next.js 픽스 시 재검토 |
| nextra 테이블 셀 코드스팬 안 escape 안 된 파이프 → build 깨짐 | 2026-05-29 발견 (decisions.mdx D-cortex-google-drift). 마크다운 테이블 셀의 inline code 안에 escape 안 된 파이프 문자(예: JSON-ish payload 의 union 타입 표기)가 있으면 GFM 이 그 파이프를 셀 구분자로 먼저 분리 → 백틱 스팬이 깨지고 그 안 중괄호가 미닫힌 JSX expression 으로 노출 → nextra 가 “Unexpected end of file in expression” 로 전체 build 실패. axe ship docs 의 docker build 단계에서야 늦게 잡힘. 해결: 셀 안 파이프는 항상 백슬래시 escape, 중괄호·꺾쇠도 코드스팬 의존 말고 escape (꺾쇠는 HTML entity). 예방: ship 전 로컬 npx next build 로 컴파일 확인 (docker 왕복보다 빠름). | 여러 세션이 같은 mdx 편집 시 file 단위 git add 가 병행 세션 WIP 까지 휩쓸 수 있음 (이번 cortex D-cortex-google-drift 가 index 커밋에 혼입) → ship 전 staged diff 확인. docs ritual 에 “ship 전 npx next build” 추가 검토 |
Button asChild 미지원 | 현재 Button 컴포넌트는 raw <button> 만 — anchor 가 필요하면 <a className="axe-btn axe-btn--primary"> 패턴 직접 사용. axelabs.ai 메인 페이지가 이 패턴으로 작성됨 | v0.2 에서 Radix Slot 패턴 도입 검토 |
| dist 빌드 부재 | package.json exports 가 ./src/lib/index.ts (raw TS) 를 가리킴. tsup/unbuild dist 빌드 없음 — 소비자 측 트랜스파일러에 의존 | Next.js / Vite 둘 다 transpilePackages 로 가능. dist 도입은 외부 OSS 공개 시점에 |
standalone build 의 output: "standalone" 의존 | Dockerfile 의 .next/standalone copy 가 next.config.mjs 의 standalone output 설정에 의존. 누락 시 빌드 fail | next.config 주석으로 표시. config 변경 시 Dockerfile 같이 확인 |
문서 템플릿 @page 전역 누수 (D-ui-3 Phase 2) | .axe-doc 인쇄용 A4 @page 를 전역 bare @page (size A4) 로 선언하면 design 사이트 전 페이지 인쇄가 A4 로 강제됨 (@page 는 문서 전역 스코프). 해결: named @page axe-doc-portrait/axe-doc-landscape + .axe-doc 의 page: 속성으로 스코프 — document.css 적용 완료 | 다른 인쇄 surface 추가 시 동일 패턴. jinja 템플릿 로컬 렌더·검증엔 jinja2 필요 (system python3 미포함 → python3 -m venv 후 pip install jinja2) |
docs verify:box 오탐 — orphan next dev 의 IPv6 :3140 port-shadow (2026-05-29)
axe ship docs 의 post-deploy verify:box (playwright page.goto(localhost:3140, networkidle)) 가 TimeoutError: page.goto: Timeout 30000ms exceeded 로 실패 → ”❌ POST-DEPLOY VERIFY FAILED — production 박스 정렬 깨짐 (exit 3)” 출력. 그러나 배포는 성공했고 production 은 정상 — 전형적 오탐. ship 자체를 의심해 롤백/재빌드 하면 멀쩡한 배포를 헛돌린다.
근본 원인: host 에 떠 있던 orphan next dev --port 3140 (parent=launchd, 4h+ 방치된 로컬 docs dev 잔여) 가 IPv6 *:3140 을 점유. localhost 가 ::1 을 먼저 resolve 하는 macOS 특성상 host 의 모든 localhost:3140 접근 (playwright + curl) 이 죽은 dev 서버로 빨려감. production docker container 는 IPv4 127.0.0.1:3140 으로만 노출되고, 외부 docs.axelabs.ai 도 tunnel 이 host.docker.internal (IPv4) 로 origin 을 잡으므로 — 둘 다 멀쩡한데 host loopback 경유 검증만 깨진다.
| 검증 경로 | 결과 |
|---|---|
curl localhost:3140 (host, ::1 우선) | hang / timeout (orphan dev) |
curl -4 127.0.0.1:3140 | 200 / ~10ms (production container) |
docker inspect in-container healthcheck | FailingStreak: 0 (정상) |
외부 https://docs.axelabs.ai | 신규 본문 정상 서빙 |
진단 분기점: lsof -nP -iTCP:3140 -sTCP:LISTEN → listener 가 두 개 (IPv6 node vs IPv4 com.docker). IPv6 쪽 PID 의 parent 가 launchd 이고 그 PID 가 production 트래픽 0 이면 orphan 확정. verify:box 만 깨지고 curl -4 + in-container healthcheck + 외부 도메인이 모두 통과하면 = 배포 OK · 검증 경로 오염.
조치: orphan 을 kill -TERM <pid> (graceful, SIGKILL 불필요 — production PGID 와 무관). 이후 localhost:3140 이 IPv4 로 fallback → verify:box 재실행 PASS. 예방: 로컬 docs dev 는 끝나면 반드시 종료할 것. :3140 은 production container 점유 포트이므로 로컬 dev 는 다른 포트 권장 (npx next dev --port 3142 등).
Blueprint Postgres — prisma migrate deploy 가 P3005 로 신규 마이그레이션 자동 적용 안 함 (2026-05-30)
blueprint-postgres 는 D-config-17 cutover 때 db push 로 부트스트랩돼서 _prisma_migrations 베이스라인 테이블이 없다. 그래서 컨테이너 기동 시 start.sh 의 prisma migrate deploy 가 P3005 (“The database schema is not empty”) 로 거부하고 [start] prisma migrate deploy failed (non-fatal) 만 찍고 넘어간다. 결과: prisma/migrations/ 에 새 마이그레이션 파일을 추가해도 배포 시 자동 적용되지 않는다 (테이블이 생성되지 않음).
증상: route·코드는 떠 있는데 신규 테이블이 없어 prisma.<model>.create() 가 런타임에 실패. 감사 로그처럼 try/catch 로 감싼 경로면 조용히 실패할 수 있다 (D-bp-mcp-mail-1 의 MailSendLog 가 정확히 이 케이스 — route 정상 배포됐으나 테이블 부재로 audit write 가 silent fail 직전).
해결 (스키마 변경 시마다 1회):
# additive 변경(새 테이블 등)이면 해당 마이그레이션 SQL 만 직접 적용 — 가장 안전, 기존 테이블 무영향
docker exec -i blueprint-postgres psql -U blueprint -d blueprint -v ON_ERROR_STOP=1 \
< prisma/migrations/<ts>_<name>/migration.sql
# 적용 확인
docker exec blueprint-postgres psql -U blueprint -d blueprint -tc "SELECT to_regclass('public.\"<Table>\"');"2026-05-30 기준 같은 함정에 걸린 마이그레이션: 20260529120000_add_mail_send_log (수동 적용 완료), 20260531000000_add_user_entra_oid (다른 세션 작업 — User.entraOid 컬럼, 수동 적용 필요할 수 있음), 20260603100452_add_entity_legal_name (C1 — Entity.legalName 영문 법인명 + axec/axev 백필, 미적용 — 배포 시 위 psql 직접적용 패턴 사용; additive·기존 테이블 무영향).
근본 fix 후보 (backlog): 기존 마이그레이션 전부를 prisma migrate resolve --applied <name> 로 베이스라인 등록 → 이후 migrate deploy 정상화. 또는 deploy hook 에 db-push fallback 추가.
문서 작업 다음 단계
- CLI Phase 5 완성 후 runbook 의 restic 명령을
axe restore로 단순화 -
com.axe.secret-check/com.axe.health-checklaunchd 구현 후 cloudflared.mdx 갱신 - partner/registration.mdx 와 frame/docs/ops/onboarding-operator.md §0 일관성 점검
- customers.yaml 에 blueprint app 슬롯 추가 (현재 frame_mcp 와 vaultwarden 만)
- Blueprint MEMORY.md 33.5KB → 24.4KB pruning (
~/.claude/projects/-Users-axe-blueprint/memory/MEMORY.mdindex 항목 길이 정리,/ctx review별도 세션)
axelabs 5 표준 (2026-05-26, Truvia 측 요구로 명시화)
운영자 측 분석 (B-customer-deploy-generalization Phase 1 + Truvia D-day 후속 회신 기반) 의 axelabs platform 표준. 각 항목은 decisions.mdx 의 D-config-N 또는 D-ops-N 으로 별도 격상 검토.
| # | 표준 | 본질 | 시행 |
|---|---|---|---|
| 1 | macmini 운영 패턴 = 기존 자산 ≡ 신규 발주 (동등 지원) | customer 가 가동 중 macmini 에 platform 도입 시 발주·재설치 의무 없음. 14 컨테이너 기존 가동 + Tailscale 합류 + SSH key 기등록 케이스 (realchoice) 1급 지원. | /partner/macmini-existing (신설 예정 — B-docs-ssot-extension), B-port-conflict-preflight 가 충돌 게이트 |
| 2 | Vault repo (soohunkang/vault.git) 표준 image = timshel/vaultwarden:latest | Vaultwarden Timshel fork 가 axelabs platform 표준. customer 측이 다른 vault image (e.g. official vaultwarden/server) 사용 시 SSO/sso_nonce/healthcheck/path mount 패턴 모두 다름. Truvia 측 5/26 D-ops-32 검증 = Timshel + collection 구조 정렬. | /services/vaultwarden (신설 예정), image_override slot 으로 customer-별 version pin 허용 |
| 3 | bw CLI 표준 version = 2025.7.0 (D-ops-28 line) | bw 2026.4.x 가 Timshel SSO user 호환성 차단 (“Master password unlock data was not found”, trap #12). 2025.7.0 = 최후 호환 release. | bw-bootstrap.sh 가 install hint + warning gate. _svc_step_customer_vault_check 가 version mismatch warning. backlog B-bw-cli-version-pinning. |
| 4 | Customer-측 self-deploy 일반화 = R1-R5 빌트인 (B-customer-deploy-generalization Phase 1) | (R1) ${HOME} 변환 — hardcoded /Users/axe/ 금지. (R2) vault SoT — bw CLI 기반, keychain 의존 폐기. (R3) env_file $ → $$ escape — escape_dollar flag 자동. (R4) image_override slot — customer-별 service repo image pin. (R5) ${CUSTOMER_PREFIX}- container name 정규화 (Phase 2). | axe CLI _deploy_service_customer + wrapper 3종 + customers.yaml schema. |
| 5 | customers.yaml schema = secret manifest + service_tenant_map + image_override + escape_dollar slots | customer 메타 + sso.apps[].client_id + services.<svc>.<env_file, secrets[], image_override> + service_tenant_map (D-magnet-tenant-map-1) + ring_backup + entity_meta. ruamel.yaml round-trip 으로 comment 보존. axe customers ingest 가 자동 fill. | /Users/axe/.axe/customers.yaml (SSOT), INGEST_MANIFEST_TEMPLATE (axe CLI), schema doc = /architecture/customers (신설 검토). |
세션 의례 (다음 세션 / 다른 세션 모두)
모든 axelabs.ai 작업 세션은 본 페이지를 의례로 사용:
-
세션 시작 시 — READ: 작업 주제와 관련된 known-gap 이 있는지 본 페이지 확인. 이미 등재된 gap 을 모르고 작업하면 다른 사람의 작업과 중복되거나 충돌 가능.
-
세션 중 — DISCOVER: 작업 중 새 gap / 미구현 / 약속 vs 실제 불일치 발견 시 즉시 본 페이지에 한 줄 추가 (해당 섹션에 표 row 추가). 메모리에 들고만 있지 말 것.
-
세션 종료 시 — WRITE: 본 세션이 해소한 gap 은 즉시 제거. 새로 발견한 gap 은 등재. 다음 세션의 다른 사람이 본 페이지만 보고도 현재 상태 파악할 수 있어야 함.
-
페이지가 비어가는 것이 목표. 단 의도된 deferral (예: “Phase 5 에서 통합”) 은 등재 후 phase 표기.
운영자 또는 협업 세션에 본 ritual 이 적용되도록 root CLAUDE.md + per-repo CLAUDE.md 에 명시.
본 페이지의 의도
다음 세션의 본인 (또는 협업자) 이 문서를 보고 작업할 때, 약속된 것 과 실제 작동하는 것 의 격차를 명확히 알도록 함. 격차 자체가 함정이 되지 않게 정리.