rewriting-technical-prose-naturally 를 서브에이전트 셋으로 나눠 56편에 적용했다. ai-tells.md 의 첫 절대로 다른 표현으로 바꾸는 대신 문장을 통째로 지웠다. 설명한 것의 중요성을 다시 평가하는 꼬리 19 이미 설명한 것을 추상어로 되풀이 19 독자에게 읽는 법을 지시하거나 오해를 가정 9 자료가 뒷받침하지 않는 덧붙인 이득 4 문서군 전체의 문형 편중도 풀었다 — 함께 27→7(한 묶음), 그대로 22→12(두 묶음), 하게 된다 1→0. 한 편에서 세 번 반복되던 「같은 병이 ~에서도 났다」와 두 기록에 같은 문장으로 있던 세 쌍을 갈랐다. 계약 제목 「여덟 자리」가 본문의 「여덟 곳」과 어긋나 있었다. 제목이 spatial-metaphor 규칙에도 걸리므로 계약과 기록을 함께 「여덟 곳」으로 맞췄다. 검사 넷 전부 통과한다 — check_prose 56편 error 0 · check_body PASS · check_evidence --repo 문제 없음 · verify-tech-log-tree error 0 warn 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.0 KiB
kind, slug, title, topic, topicName, project, status, lastVerifiedOn, sourceRevision, source
| kind | slug | title | topic | topicName | project | status | lastVerifiedOn | sourceRevision | source | |
|---|---|---|---|---|---|---|---|---|---|---|
| CASE | a-narrow-implementation-satisfied-a-wide-port | 구현이 종류를 좁게 적어도 넓은 포트를 만족했다 | what-the-compiler-lets-through | 타입 검사가 통과시키는 곳 | TechLog | 게시 전 | 2026-09-04 | tech-log@2026-09-02 |
|
구현이 종류를 좁게 적어도 넓은 포트를 만족했다
개념 삭제가 계속 질문 삭제 경로로 나갔다. 앞선 커밋이 게이트웨이를 고치지 못했는데 타입 검사가 통과해서 반영된 줄 알았다. TypeScript 에서 메서드 매개변수는 bivariant 라, 구현이 종류를 좁게 적어도 넓은 포트 시그니처를 만족한 것으로 통과한다.
관계
- TypeScript 가 검사를 놓아 주는 네 곳 bivariance 가 그중 하나다.
- 타입에는 보이는데 부를 수 없는 연산이 네 번 나왔다 같은 삭제 경로에서 난 다른 부류의 결함이다.
- 한 경계를 고쳤으면 값의 여정 끝에서 확인한다 이 사건이 그 규칙의 근거 하나다.
문제
포트는 네 종류를 받는다고 선언돼 있는데 구현은 세 종류만 적혀 있었다. 그 상태로 타입 검사가 통과했다.
CONCEPT 을 넘기면 구현의 삼항 사슬이 마지막 else 로 떨어뜨려 질문 삭제 경로를 부른다. 배포된 번들에서 서버 로그에 DELETE /api/v1/studio/questions/{id} 404 가 계속 찍혔다.
결론
TypeScript 에서 메서드 매개변수는 bivariant 다. 구현이 매개변수를 좁게 적어도 넓은 포트 시그니처를 만족한 것으로 통과한다.
포트 : CASE · REFERENCE · QUESTION · CONCEPT 구현 : CASE · REFERENCE · QUESTION 타입 검사 : 통과 실행 결과 : CONCEPT 이 질문 삭제로 나감
같은 병이 필터 타입에서도 났다. 포트와 정적 어댑터가 타입을 따로 들고 있어, 포트에 필터가 늘어도 어댑터는 모르는 상태가 됐다. satisfies 도 같은 이유로 잡지 못했다. 타입을 하나로 합쳐서 고쳤다.
검증 환경
tech-log-frontend : 6429aee 이후 · 67a5491 에서 필터 타입 통합 TypeScript : 메서드 매개변수 bivariance 확인 방식 : 배포본의 서버 로그에서 실제로 나가는 경로를 확인
재현 조건
- 포트에 네 값을 받는 메서드를 선언한다
- 구현에서 세 값만 적는다 — 타입 검사가 통과한다
- 네 번째 값을 넘겨 실제로 어떤 경로가 나가는지 본다
본문
좁은 구현이 넓은 포트를 만족한다
// 포트 시그니처
deleteDocument(kind: "CASE" | "REFERENCE" | "QUESTION" | "CONCEPT", id: string): Promise<void>;
// 구현이 이렇게 좁게 적혀 있어도 위 시그니처를 "만족"한다
deleteDocument(kind: "CASE" | "REFERENCE" | "QUESTION", id: string) { … }
메서드 매개변수가 bivariant 이므로 이 둘은 호환된다고 판정된다. 구현 안의 삼항 사슬은 CONCEPT 을 마지막 else 로 떨어뜨린다.
왜 반영된 줄 알았나
앞선 커밋에서 게이트웨이를 고쳤다고 판단한 근거가 타입 검사 통과였다. 좁게 적힌 구현을 그 커밋이 건드리지 않았고, 검사는 그것을 묻지 않았다.
배포된 번들에서 서버 로그를 보고서야 알았다. 삭제 요청이 질문 경로로 나가고 있었다.
포트와 어댑터가 필터 타입을 따로 들고 있었다
포트에 축 필터를 더해도 어댑터의 타입은 그대로였고, satisfies 도 같은 이유로 통과했다.
타입을 하나로 합쳐서 고쳤다. 포트가 아는 필터와 어댑터가 아는 필터가 같은 타입이면 한쪽만 늘어날 수 없다.
확인한 것과 확인하지 못한 것
CONCEPT 을 deleteQuestion 으로 되돌려 가드가 깨지는 것을 확인했다.
배포된 번들에 옛 삼항이 남아 있던 것은 서버 로그의 404 로 확인했다. 그 로그 원문은 이 저장소에 없다.