Files
llm-wiki/raw/errors/contract-registry-reference-row-universal-column-false-fail-2026-06-20.md

66 lines
5.4 KiB
Markdown

---
title: error / contract-registry-reference-row-universal-column-false-fail-2026-06-20
source_type: error-note
status: raw
related_branches: [feature-contract-registry-governance]
related_projects: [ca-skeleton]
tags: [error, ca-skeleton, registry, governance, yaml, test, false-positive]
created: 2026-06-20
status_label: resolved
---
# error: contract-registry-reference-row-universal-column-false-fail-2026-06-20
> Layer: `raw/errors/` — schema-owner gate 구현 중 발견한, 모든 row 에 universal column 을 요구하는 naive 게이트의 false-FAIL 함정.
## Parent / 부모
- [[raw/branch-notes/feature-contract-registry-governance]] — 본 branch 의 schema governance 게이트(`ContractRegistrySchemaGovernanceTest`) 구현(Phase C2, 2026-06-20)에서 발견.
## 증상 / Symptom
- 발생 컨텍스트: `feature-contract-registry-governance` 의 schema-owner 게이트를 구현하기 위해, "모든 registry row 는 universal-3 column(`owner_branch`/`compatibility_impact`/`required_test`)을 가져야 한다"(branch-note §구현 가이드 §1/§2, grep "7/7")를 그대로 테스트로 옮기려 했다.
- 사전 검사(테스트 작성 전 row 수 vs column 수 대조):
```text
secrets-classification rows(name)=15 owner_branch=15 compat=10 req_test=10
(그 외 6 registry: rows == compat == req_test 로 일치)
```
- 즉 `secrets-classification.yaml` 의 15 row 중 5개가 `compatibility_impact`/`required_test` 를 보유하지 않는다. 모든 row 에 universal-3 를 요구하는 게이트는 이 5 row 에서 hard FAIL 한다(실제 데이터는 정상인데 게이트가 틀린 false-positive).
- 재현 가능 여부: `always` (게이트가 reference-row 면제를 모르면 항상)
## 재현 절차 / Reproduction
1. branch-note §1/§2 의 "universal-3 column 7/7 필수" 를 곧이곧대로 옮겨, 모든 registry 의 모든 row 에 대해 `compatibility_impact ∈ legal-enum` AND `required_test != blank` 를 단언하는 테스트를 작성.
2. 로컬에 seed 된 `docs/registries/*.yaml` 로 실행.
3. `secrets-classification.yaml` 의 Tier-1 public-config 5 row 에서 `compatibility_impact`/`required_test` 부재로 단언 실패.
## 조사 단계 / Investigation log
- 2026-06-20 — 사전 검사에서 `secrets compat=10 != rows=15` 불일치 포착. 테스트를 쓰기 전이라 false-FAIL 을 코드로 만들기 전에 차단됨(= "데이터로 먼저 검증" 의 효용).
- 2026-06-20 — `secrets-classification.yaml` 전문 확인. 해당 5 row 는 헤더 L17 `# - public-config 항목은 env-keys.yaml에서 직접 정의되며 본 파일에는 reference row만 둔다` 가 규정한 **reference row** 였다(APP_PROFILE / APP_NAME / SERVER_PORT / SPRING_PROFILES_ACTIVE / OTEL_EXPORTER_OTLP_ENDPOINT). 각 row 는 `reference: env-keys.yaml#<KEY>` 를 갖고 contract column 은 의도적으로 생략.
- 2026-06-20 — `grep -cE "^ reference:" docs/registries/*.yaml` 로 reference row 가 secrets 전용(5건)임을 확인(나머지 6 registry 0건). 면제 메커니즘이 secrets 한정임을 데이터로 확정.
## 근본 원인 / Root cause
- branch-note 의 "universal-3 column 7/7 필수" 요약은 **full row** 기준이었고, as-built schema 에는 문서화된 예외 — **reference row** — 가 존재한다. reference row 는 자기 식별자(`name`)와 위임 포인터(`owner_branch`, `reference`)만 갖고, `compatibility_impact`/`required_test` 의 authoritative 값은 `reference` 가 가리키는 registry(여기선 `env-keys.yaml`)에 있다. 한 곳에만 contract column 을 두는 **single-source 위임** 이므로, 면제는 누락이 아니라 설계다.
## 해결 / Resolution
- 게이트를 두 단계로 분리:
- **모든 row**(reference 포함): identity column(error=`code`/mdc=`key`/그 외=`name`) + `owner_branch` 필수.
- **full row 만**(= `reference:` 키 부재): `compatibility_impact ∈ {none, additive, behavior-change, breaking}` + `required_test != blank`.
- **reference row 만**(= `reference:` 키 보유): `reference` target 이 non-blank 인지 검증(면제를 명시적·검증 가능하게 — "그냥 빠뜨린 것" 과 "위임" 을 구분).
- `isReferenceRow(row) = row.containsKey("reference")` 단일 술어로 분기.
- 결과: 6 tests green(skipped=0). 음성 변이(headers row 에 illegal `compatibility_impact: BOGUS_ILLEGAL` 주입)로 `every_full_row_declares_compatibility_impact_within_the_legal_enum()` FAIL 확인 후 원복.
## 교훈 / Lessons
- **요약(grep "7/7")을 곧이곧대로 단언으로 옮기지 말 것** — 요약은 보통 happy-path(full row) 기준이고, as-built 에는 파일 헤더 주석에만 적힌 예외가 있다. 테스트 작성 전 row 수 vs column 수 대조(데이터 검증)가 false-FAIL 을 코드화하기 전에 잡아준다.
- **면제는 "검증 가능하게" 모델링** — reference row 를 그냥 skip 하지 않고, `reference` target 보유를 별도 단언으로 강제하면 "위임" 과 "단순 누락" 이 구분된다.
- gitignore 된 seed 데이터(`/docs`) 위에서 도는 테스트는 부재 시 SKIP(=Assumptions), 존재 시 위반 FAIL 의 이중 모드를 따른다(기존 registry drift 테스트 패턴과 동일). 관련: [[raw/errors/ca-gitignored-seed-divergence-at-rebase]].
## 관련 / Related
- [[raw/branch-notes/feature-contract-registry-governance]]
- [[raw/blog-topics/contract-registry-schema-owner-vs-row-owner-gate-2026-06-20]]