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

5.4 KiB

title, source_type, status, related_branches, related_projects, tags, created, status_label
title source_type status related_branches related_projects tags created status_label
error / contract-registry-reference-row-universal-column-false-fail-2026-06-20 error-note raw
feature-contract-registry-governance
ca-skeleton
error
ca-skeleton
registry
governance
yaml
test
false-positive
2026-06-20 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 / 부모

증상 / 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 수 대조):
    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.