Skip to content

docs(contract): declare the provider key header, the shared 429 and 503, and the request id - #1024

Merged
Eomdahyeon merged 13 commits into
mainfrom
docs/issue-994-contract-headers-and-shared-errors
Oct 4, 2026
Merged

Eomdahyeon merged 13 commits into
mainfrom
docs/issue-994-contract-headers-and-shared-errors

Conversation

@Eomdahyeon

Copy link
Copy Markdown
Collaborator

Refs #994

#1023(#1000) 위에 쌓은 PR 이다 (그 PR 은 #1022 위에 있다). base 를 그 브랜치로 잡아 이 PR 의 diff 만 보이게 했다. 과부하 503 의 code 가 #1023 에서 생기기 때문이다.

이 PR 이 하는 것 — #1009 가 "이 PR 에 없는 것"으로 남긴 세 항목 중 둘

  1. X-Provider-Key 를 header 파라미터로 선언 — components.parameters.ProviderKey. 프로바이더를 호출하는 다섯 operation(previewBuild, createBuild, submitBuild, getProviderStatus, testProviderConnection)에 참조를 달았다. Studio 가 이 헤더를 보내는 호출과 같은 다섯이다.
  2. 429 auth_throttled, 과부하 503, X-Request-ID — 어느 operation 에서나 나올 수 있으므로 SignupNotApproved 와 같은 방식(x-status 를 가진 공용 응답)으로 선언했다: components.responses.AuthThrottled, ServerOverloaded, 그리고 components.headers.RequestId.

계약 1.79.0 → 1.80.0. wire 는 바뀌지 않는다(선언만). response fixture 를 생성기로 다시 만들었다.

하지 않은 것

  1. _DISPATCH_ROUTES 를 라우트 어댑터에서 생성 — docs(contract): declare the publish recovery routes #1009 본문이 적은 대로 라우트가 if 연쇄라 표로 바꾸는 리팩터링이 먼저다. 이 PR 범위에 넣지 않았다. 그래서 Refs.

X-Request-ID 는 헤더 컴포넌트로만 선언했고 각 operation 의 응답마다 참조를 달지는 않았다 — 모든 응답(수백 개)에 다는 것은 계약을 읽기 어렵게 만든다고 판단했다. 설명에 "핸들러가 쓰는 모든 응답에 실린다"고 적었다.

검증 — 선언과 실제를 대조한다

tests/unit/test_contract_shared_declarations.py:

  • 파라미터의 이름이 코드의 PROVIDER_KEY_HEADER 와 같고, 참조한 operation 집합이 정확히 위 다섯이다
  • _overloaded_response() 의 상태·본문·헤더가 ServerOverloaded 선언과 같다
  • 인증 실패 한도를 넘긴 실제 응답의 상태·키·code 가 AuthThrottled 선언과 같다
  • 실제 HTTP 응답에 X-Request-ID 가 있다

계약 관련 테스트 파일: 424 passed. check_contract_compat.py: origin/main 대비 1.77.0 -> 1.80.0 호환. ruff, mypy 통과. 전체 스위트는 CI 가 돌린다.

#1009 와의 관계

#1009 는 지금 main 과 충돌 상태다 — 내가 #1010 을 머지하면서 main 이 1.77.0 이 됐고, #1009 도 1.77.0 을 쓴다. 이 스택(#1022 → #1023 → 이 PR)은 1.78.0 ~ 1.80.0 을 쓴다. 어느 쪽이 먼저 들어가든 나중 쪽이 버전 줄을 다시 맞춰야 하니, 순서를 정해 주면 내 쪽을 그에 맞추겠다.

🤖 Generated with Claude Code

Eomdahyeon and others added 3 commits October 4, 2026 23:59
Closes #996

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…503 a stable code

Closes #1000

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…03, and the request id

Refs #994

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Eomdahyeon

Copy link
Copy Markdown
Collaborator Author

머지 순서 주의. 이 PR 이 추가하는 에러 예시와 code 는 Studio 의 드리프트 검사(ERROR_READERS)가 아직 모른다. Builder main 에 들어가면 Studio 의 모든 PR 에서 Builder contract drift 가 실패한다 — 이 스택의 계약으로 Studio 드리프트 테스트를 돌려 확인했다(7개 실패). kpubdata-lab/kpubdata-studio#743 이 그 항목들을 버전 조건과 함께 추가하므로, #743 을 먼저 머지한 뒤 이 PR 을 넣어야 한다. #743 은 지금 Builder main(1.77.0)에서도 통과한다.

Eomdahyeon and others added 7 commits October 5, 2026 00:17
The fixture released the first service's worker and returned. The worker then
ran its build behind the following tests, and test_json_array_reader measures
peak memory with tracemalloc, which counts every thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The code was read back from the end of the reason sentence, so rewording the
reason would have turned token_expired into unauthorized without a failure
anywhere. AuthError now carries the fact. Also pin that a missing API key and a
wrong one both answer with the contract example's sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…000-stable-error-codes

# Conflicts:
#	tests/unit/test_oidc_auth.py
…-contract-headers-and-shared-errors

# Conflicts:
#	CHANGELOG.md
Eomdahyeon added a commit to kpubdata-lab/kpubdata-studio that referenced this pull request Oct 4, 2026
…tract (#743)

Refs #727

## 이 PR 이 하는 것 — 완료 조건 3번

"클라이언트의 모든 method+path 가 계약에 있는지 단언한다." `builderApi` 의 59개 함수 각각을 자리표시
인자로 호출하고(stub `fetch`), 보낸 method 와 path 가 계약의 operation 과 맞는지 본다. path
파라미터는 세그먼트 하나와 맞는다.

- 소스 정적 분석이 아니라 **실제 호출**을 기록한다 — 템플릿 문자열·조건부 경로를 그대로 따라간다.
- probe 가 요청까지 끌고 가지 못하는 함수는 `UNPROBED` 목록에 사유와 함께 올려야 한다. 지금은 빈
목록이다(59개 전부 요청을 보냈다). 새 함수가 조용히 빠지지 않는다.
- CI 가 `BUILDER_CONTRACT` 를 주고 실행하는 파일이
`src/shared/lib/contractDrift.test.ts` 하나라서, 별도 파일이 아니라 그 파일 끝에 넣었다. 계약이
없으면 다른 드리프트 검사처럼 skip 된다.

## 검증

- Builder 계약(1.78.0, kpubdata-lab/kpubdata-builder#996 브랜치 — main 의
1.77.0 과 경로는 같다) 기준: `553 passed`. 계약 없이: 기존대로 skip.
- 매처 자체의 테스트: 선언된 경로는 통과, 선언되지 않은 method(`DELETE
/builds/{id}/manifest`)·없는 경로·세그먼트가 하나 더 많은 경로는 거부.
- `tsc --noEmit`, eslint 통과.

## 추가 (두 번째 커밋) — 완료 조건 2번과 Builder 의 에러 코드

- **`X-Provider-Key`**: probe 가 이 헤더를 싣는 호출 다섯(`build`, `preview`,
`submitBuild`, `getProviderStatus`, `testProviderConnection`)을 찾고, 계약이 그
operation 에 파라미터로 선언했는지 본다. 계약
1.80.0(kpubdata-lab/kpubdata-builder#1024) 부터 강제하고, 그 전 계약에서는 다섯 개를
찾았는지만 확인한다.
- **이 PR 은 Builder 의 #1023·#1024 보다 먼저 머지되어야 한다.** 그 두 PR 이 추가하는 에러
예시(`build_queue_full`, `auth_throttled`, `server_overloaded`)와 401 예시의
`code: unauthorized` 를 지금의 Studio 드리프트 검사는 모른다 — Builder `main` 에 들어가는
순간 Studio 의 모든 PR 에서 `Builder contract drift` 가 실패한다. 직접 돌려 확인했다(7개 실패).
- 그래서 `ERROR_READERS` 항목에 `since`(그 예시나 코드가 생긴 계약 버전)를 둘 수 있게 했다. 그 버전
전의 계약에서는 예시가 없어도 stale 로 보지 않고, 있어도 code 가 없는 것을 정상으로 본다. Studio CI 가
Builder `main` 을 읽는 구조에서 양쪽 PR 이 서로를 깨뜨리지 않고 순서대로 들어갈 수 있게 하는 장치다.

드리프트 테스트를 세 계약에 대해 돌린 결과: Builder `main`(1.77.0) `555 passed`,
#1022(1.78.0) `555 passed`, #1024 까지(1.80.0) `558 passed`.

## 남은 것

없다 — #727 의 세 조건을 모두 다뤘다. 다만 2번은 Builder #1024 가 머지돼야 실제로 강제되므로 `Refs` 로
둔다.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Eomdahyeon <213566566+Eomdahyeon@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

@yeongseon yeongseon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 (코멘트 — #1023 위에 쌓인 PR 이고 체크가 도는 중이라 승인은 그 뒤에).

diff 를 읽었고 걸리는 점이 없습니다. 선언마다 실제 응답과 대조하는 테스트가 있고(_overloaded_response() 의 상태·본문·헤더, 한도를 넘긴 실제 429, 실제 HTTP 응답의 X-Request-ID), ProviderKey 를 참조하는 operation 집합을 정확히 다섯으로 고정했습니다. X-Request-ID 를 응답마다 달지 않고 헤더 컴포넌트로만 둔 판단도 동의합니다.

본문이 물은 순서 — 이 스택을 먼저 넣습니다. #1022 → (studio#743) → #1023 → #1024 순으로 1.78.0 ~ 1.80.0 을 쓰고, #1009 는 그 뒤에 리베이스해서 다음 번호(1.81.0)를 씁니다. #1009 는 어차피 지금 충돌 상태라 다시 손봐야 하고, 이 스택은 그대로 들어갈 수 있습니다. 이쪽 버전 줄은 바꿀 필요 없습니다.

로컬에서 테스트를 돌리지는 않았습니다.

…e-error-codes

# Conflicts:
#	contract/builder-api.yaml
#	contract/fixtures/responses.json
#	src/kpubdata_builder/service/app.py
@Eomdahyeon
Eomdahyeon changed the base branch from feat/issue-1000-stable-error-codes to main October 4, 2026 15:46
…ct-headers-and-shared-errors

# Conflicts:
#	contract/builder-api.yaml
#	contract/fixtures/responses.json
#	src/kpubdata_builder/service/app.py
@Eomdahyeon
Eomdahyeon merged commit d13d64a into main Oct 4, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants