architecture.md의 클래스 책임 표는 코드 구조를 문서에 복제한 것이라
rename이나 package 이동이 있으면 조용히 낡는다. 이 테스트는 그 이유로
만들어졌고, 삭제된 원본 Javadoc에 "패키지 재구성 한 번에 네 개의
이름이 죽은 적이 있다"고 적혀 있다.
c9a6bd2의 dap에서 dat으로의 개명에서 같은 일이 다시 일어났다. 테스트가
그 커밋에서 함께 삭제되어 드리프트를 잡지 못했고, dc6f602에서 사람이
손으로 대조해 아홉 건을 고쳐야 했다.
패키지 문자열만 dat으로 바꿔 복원한다. 표의 클래스 행 서른 개를 모두
읽어 src/main/java에 해당 소스가 있는지 확인한다. 패키지 경계 표의
일곱 행은 첫 칸이 소문자 패키지명이라 정규식이 의도대로 건너뛰므로
사각지대는 없다.
존재하지 않는 클래스 행을 표에 심어 실패를 내는 것까지 확인했고 확인 후
되돌렸다. 195개 테스트 전부 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
문서가 3de052a 시점 상태로 복구되면서 그 뒤 main의 코드 커밋 일곱 개가
반영되지 않았다. 계약 테스트가 보는 범위 밖이라 194건이 통과해도
드러나지 않는다. 코드와 대조해 확인한 아홉 건을 고친다.
사실이 틀린 것:
- 갱신 모델. ToolRegistryRefreshScheduler에는 @Scheduled가 없고
ApplicationReadyEvent 하나만 있다. 갱신은 주기 실행이 아니라 요청
시점 TTL 만료로 일어난다. 설정 키도 refresh-interval-seconds가 아니라
refresh-ttl-seconds다.
- 1:1 배포 전제. ADR-0007은 Superseded이고 코드는 route당 N개 Tool
Service를 병합한다. ADR-0013 기준으로 다시 쓴다.
- route table이 없다는 서술. McpController가 /mcp/{routeKey}를 받고
PortalToolRegistryClient가 bundlesByRoute를, ToolRegistryService가
route별 snapshot을 들고 있다. route 매핑은 애플리케이션 안에 있다.
- 실행 흐름 1번의 publicPath. route key는 URI에서만 결정된다.
빠진 것:
- 클래스 표에 McpRouteKeyValidator, PortalToolRegistryClient,
RedisPortalRegistryCache, AgentRoutingHintsProperties,
LocalFixtureProperties를 추가한다. 앞의 셋은 Portal 모드의 핵심
경로인데 표에 없었다.
- Agent routing hint 절을 새로 쓴다. 9905d52가 들여온 기능이 문서에
전혀 없었다.
- Tool 호출 retry 절을 새로 쓴다. 6526e73의 retrySafeByAnnotation이
destructiveHint를 항상 금지하고 annotations 미선언 Tool은 재시도하지
않는다는 사실이 없었다.
- 스키마 정책 두 클래스를 표에 넣고, 검사 지점이 ToolMetadata 생성자
하나라는 것을 실행 흐름 9번에 적는다.
인용한 식별자 열여섯 개가 코드에 실재하는지, 상대링크가 깨지지 않는지
확인했다. retry 기본값도 application.yml과 대조했다. 194개 테스트 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
c9a6bd2가 dap에서 dat으로 옮기면서 docs 패키지의 계약 테스트 셋을
삭제했다. 같은 커밋이 다른 테스트는 모두 이전했으므로 의도적 판단으로
보이지만, architecture.md는 여전히 "이 규칙은
PackageBoundaryContractTest가 강제한다"고 적고 있어 문서가 없는 장치를
가리키는 상태였다.
규칙 자체는 지금도 유효하고 위반도 없다. jakarta.servlet을 쓰는 네
파일이 모두 transport/http 안에 있고, transport에서 execute나 registry를
import하는 파일도 없다. McpRouteKeyValidator는 registry 지식이 필요한데도
transport/http에 인터페이스를 두고 구현을 주입받는 방식으로 경계를
지킨다.
패키지 문자열만 dat으로 바꿔 그대로 복원한다. 소스 파일을 읽기만 하고
application context를 띄우지 않으므로 의존이 늘지 않는다.
검사가 실제로 동작하는지 두 규칙 각각에 위반 파일을 심어 확인했고
둘 다 실패를 냈다. 확인 후 삭제했다. 194개 테스트 전부 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4e4c341에서 mcp.portal.route-key가 main에 없다고 보고 "남은 판단"의
해당 항목을 삭제했다. 근거로 삼은 grep이 YAML 키만 봤고
McpProperties.Portal의 record 컴포넌트를 놓친 오판이었다.
routeKey 컴포넌트는 McpProperties.java:266에 그대로 있고, .portal()
호출 다섯 곳 중 routeKey()를 읽는 곳은 없다. 원래 항목이 맞았으므로
확인한 근거를 덧붙여 되살린다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
.ua/와 skills-lock.json은 에이전트 도구가 작업 중 로컬에 만드는
파일이다. 개발자마다 내용이 달라지고 저장소 산출물이 아니므로
공유하지 않는다. 지금은 untracked로 남아 git status를 계속 채운다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ADR-0007은 MCP 배포 하나가 Tool Service 하나만 보게 했으나 구현은
이미 Portal이 route와 endpoint를 소유하는 구조다. 결정 문서가 없어
ADR-0007이 Accepted로 남은 채 코드와 정반대되는 내용을 현재 설계
근거처럼 제시하고 있었다.
ADR-0013이 그 경로를 확정하고 ADR-0007 전체와 ADR-0009 결정 4를
대체한다. ADR-0010은 Tool 실행 주소의 소유자가 설정이 아니라 Tool
Service 매니페스트라는 6078852의 결정을 사후 기록한다. 두 ADR이
정본으로 인용하는 Portal-MCP 계약 v0.1도 함께 넣는다.
ADR-0013은 main 현재 코드에 맞춰 세 곳을 고쳤다. route 간 갱신 격리
부재는 6653030이 해소해 격리 표로 옮겼고, 제거된 mcp.portal.route-key
항목은 뺐다. 남은 위험 둘(Portal 모드 warm start 미동작, readiness의
route별 상태 미노출)은 코드에서 유효함을 확인해 남긴다.
ADR-0010이 기록하는 대로 endpoint 검증에는 도메인 허용목록이 없고
NetworkPolicy도 Ingress만 선언한다. 매니페스트 원천의 신뢰성이 곧
outbound 대상의 신뢰성이다. McpProperties와 application.yml의 주석이
이 결정과 반대로 남아 있다는 사실도 ADR에 적었다. 코드는 바꾸지
않는다.
192개 테스트 통과. 문서 상대링크 깨짐 없음.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
매니페스트가 선언한 inputSchema는 외부가 정하는 입력이다. JSON Schema
검증기는 문서 밖 $ref를 만나면 그 주소로 직접 조회하므로 매니페스트가
서버의 outbound 호출 대상을 정하는 통로가 된다. pattern은 joni와
graal-js가 없어 java.util.regex의 백트래킹 경로로 처리되고, 인증이 없는
경계(ADR-0006)라 호출 빈도를 줄여 주는 계층도 없다.
ToolSchemaReferencePolicy가 문서 밖 참조와 미지원 dialect를 막고,
ToolSchemaPatternPolicy가 정규식 길이·무한 수량자 개수·중첩 반복을
검사하며 pattern을 쓰는 필드에 maxLength를 요구한다. 길이를 묶을 수
없는 patternProperties는 거부한다.
검사는 ToolMetadata의 표준 생성자 한 곳에서만 한다. Portal 매니페스트
파싱, local 파일 로딩, Redis snapshot 역직렬화가 모두 이 생성자를
지나므로 조회 경로가 늘어도 검사 지점은 하나로 남는다. 위반은
IllegalStateException이라 기존 매니페스트 형식 오류와 같게 다뤄지고
bundle 단위 실패 격리가 그대로 적용된다.
근거와 한계는 ADR-0011, ADR-0012에 있다. ADR-0012가 classpath 근거로
인용하는 docs/sbom도 함께 가져온다.
192개 테스트 전부 통과. 기존 169건은 새 검사에 걸리지 않는다.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
계약 테스트는 docs/contracts 아래 예제를 golden example로 읽는다.
docs/와 README.md가 ignore되어 있어 예제 파일이 사라졌고 9건이
실패하고 있었다. 문서가 온전했던 마지막 상태(3de052a)에서 복원하고
.gitignore에서 두 항목을 제거한다. 에이전트 산출물인
docs/superpowers/ 제외는 유지한다.
initialize 응답의 capabilities.tools.listChanged를 문서는 true로
적고 있었으나 InitializeHandler는 false를 낸다. 현재 HTTP 단발 응답
transport가 notification을 push할 수 없으므로 false가 맞다. 예제와
architecture.md를 코드에 맞추고, ToolListChangedEvent가 발행되지만
아직 소비되지 않는다는 점과 SSE 도입 시 true로 바꾼다는 조건을
남긴다.
169개 테스트 전부 통과.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* AX Portal이 배포되지 않은 상태에서 Gateway가 툴 엔드포인트를 찾지 못하는 문제를 해결하기 위해,
Nginx 기반의 정적 가짜 포털(portal-mock)을 도입했습니다.
* deploy/docker-compose.yml 에는 MCP_PORTAL_REGISTRY_URL을 portal-mock으로 바라보게 설정하였으며,
deploy/portal-registry.json 에 하드코딩된 CUS, SAL, PRO, SYS 경로를 제공합니다.
* 향후 실제 AX Portal이 구성되면 docker-compose.yml의 주소를 실제 운영 포털로 변경해야 합니다.