63 lines
4.0 KiB
Markdown
63 lines
4.0 KiB
Markdown
---
|
|
title: error / ulid-fixture-crockford-u-self-inconsistency-2026-06-01
|
|
source_type: error-note
|
|
status: raw
|
|
related_branches: [feature-resource-identifier-contract]
|
|
related_projects: [ca-skeleton]
|
|
tags: [error, ca-skeleton, identifier, ulid, crockford-base32, fixture, validation]
|
|
created: 2026-06-01
|
|
status_label: resolved
|
|
---
|
|
|
|
# error: ulid-fixture-crockford-u-self-inconsistency-2026-06-01
|
|
|
|
> Layer: `raw/errors/` — 작업 중 마주친 단일 실패·트러블슈팅 기록. 원본은 raw에 영구 보관한다.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-resource-identifier-contract]] — D19 sample-portfolio `WorkLogId` reference fixture 값이 자기 자신의 D2 charset / D19 regex와 모순되어 빌드 불가였다.
|
|
- [[raw/project-notes/ca-skeleton-operational-contract]] — ca-tmpl sample fixture(§17/§22)가 cross-cite하는 값이라 cross-document 정합 이슈다.
|
|
|
|
## 증상 / Symptom
|
|
|
|
- 에러 메시지 (원문 그대로):
|
|
```text
|
|
UlidCodecTest > normalize_is_identity_on_canonical_input() FAILED
|
|
java.lang.IllegalArgumentException at UlidCodecTest.java:20
|
|
```
|
|
- 발생 컨텍스트: `cd src && ./gradlew :adapter-outbound:test` 실행 중, fixture 값 `01HRGC7K2N4F6P8Q0R2S4T6U8V`를 `Ulid.from(...)` / `WorkLogId.of(...)`로 파싱하는 모든 테스트가 실패.
|
|
- 발생 시점: 2026-06-01 (구현 중 1차 빌드)
|
|
- 재현 가능 여부: `always` — 해당 fixture 문자열을 ULID 파서/검증기에 넣으면 항상 실패.
|
|
|
|
## 재현 절차 / Reproduction
|
|
|
|
1. `WorkLogId.of("01HRGC7K2N4F6P8Q0R2S4T6U8V")` 또는 `Ulid.from("01HRGC7K2N4F6P8Q0R2S4T6U8V")` 호출.
|
|
2. 기대 결과: canonical ULID로 수용.
|
|
3. 실제 결과: `IllegalArgumentException` (23번째 문자 `U`가 Crockford base32 alphabet에 없음).
|
|
|
|
## 근본 원인 / Root cause
|
|
|
|
- 직접 원인: fixture 문자열 `...T6**U**8V`의 `U`는 Crockford base32 alphabet(`0123456789ABCDEFGHJKMNPQRSTVWXYZ`)에서 **제외**된 문자(I/L/O/U)다. ULID 파서와 D19 regex `^[0-9A-HJKMNP-TV-Z]{26}$`(U 미포함) 모두 거부한다.
|
|
- 근본 원인: spec 문서가 D2(charset 결정 = Crockford, I/L/O/U 제외)와 D19(구체 fixture 값)를 따로 작성하면서, fixture 예시 값을 직접 검증하지 않아 self-inconsistency가 남았다. 사람이 손으로 만든 "ULID처럼 보이는" placeholder가 실제로는 유효하지 않았다.
|
|
- 트리거 조건: 구현체가 placeholder가 아닌 실제 파서(`ulid-creator`의 `Ulid.from`)로 fixture를 검증하는 순간 노출.
|
|
|
|
## 해결 / Resolution
|
|
|
|
- 적용한 조치: fixture 값을 ULID spec 공식 예제값 `01ARZ3NDEKTSV4RRFFQ69G5FAV`(I/L/O/U 부재, 외부 검증 가능)로 교체. 문서(D19/§5/§8/Decision map/Redis 예시/OpenAPI example)와 코드(`SamplePortfolioFixture` + 9개 테스트 파일) 전부 일괄 치환.
|
|
- 검증 방법:
|
|
- `cd src && ./gradlew :adapter-outbound:test :sample-portfolio:test` 성공.
|
|
- `WorkLogIdTest`가 canonical 값 수용 + I/L/O/U 포함 값 거부를 모두 단언.
|
|
- 잔여 위험 / 후속 작업: baseline branch + project-note §17/§22의 cross-cite 값도 동일하게 갱신됐는지 확인 필요.
|
|
|
|
## 회고 / Lessons
|
|
|
|
- 빨리 감지하는 신호: "ULID/Crockford 문자열인데 `IllegalArgumentException`" → 먼저 I/L/O/U 포함 여부와 길이(26)를 점검.
|
|
- 예방 체크리스트: 문서에 박는 예시 식별자 값은 *실제 라이브러리 파서로 1회 검증*한 값만 사용한다. charset 결정(D2)과 구체 예시(D19)는 같은 alphabet으로 교차 검증한다.
|
|
- 일반화된 교훈: "spec이 자기 자신과 모순될 수 있다." 예시 값/정규식/charset을 별도 섹션에 쓰면 사람이 어긋낸다 — 구현이 곧 spec의 단위테스트다.
|
|
|
|
## Related / 관련
|
|
|
|
- 관련 branch note: [[raw/branch-notes/feature-resource-identifier-contract]]
|
|
- 관련 형제 branch: [[raw/branch-notes/feature-api-contract-baseline]] (URL path variable 예시로 동일 fixture cite)
|
|
- 파생 blog 글감: [[raw/blog-topics/ulid-crockford-base32-excluded-letters-2026-06-01]]
|