5탭 계약 대조 — 계약 원본 사본을 앱 저장소에 두어 어느 체크아웃에서도 검증

This commit is contained in:
Sn-Kinos
2026-09-14 13:30:26 +09:00
parent 60ea6974df
commit 1c51662c25
3 changed files with 76 additions and 1 deletions
+29
View File
@@ -27,6 +27,35 @@ fvm flutter config --jdk-dir ~/.jdks/jdk-17.0.20.1+1/Contents/Home
`brew install --cask temurin@17` 은 sudo 를 요구한다. sudo 없이 쓰려면 Adoptium `brew install --cask temurin@17` 은 sudo 를 요구한다. sudo 없이 쓰려면 Adoptium
tar.gz 를 `~/.jdks/` 에 풀면 된다. tar.gz 를 `~/.jdks/` 에 풀면 된다.
## 테스트
```bash
export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools
fvm flutter analyze
FANUZU_DESIGN_DIR=/path/to/p-fancake/design fvm flutter test
```
`FANUZU_DESIGN_DIR` 은 5탭 계약 대조용이다. 스모크 테스트가
`craft-allowlist.yaml` 의 `APP_TABS` 를 직접 읽어 앱의 `appTabs` 와 정확 일치를 검증한다.
**정본은 상위 저장소 `p-fancake/design/craft-allowlist.yaml`** 이고 이 저장소는 분리돼
있으므로, 같은 파일의 사본을 `design/craft-allowlist.yaml` 로 두었다. 탐색 순서는 아래와 같다.
1. `$FANUZU_DESIGN_DIR/craft-allowlist.yaml` — 정본을 직접 가리킬 때 쓴다.
2. `design/craft-allowlist.yaml` — 이 저장소의 사본.
3. `../design/craft-allowlist.yaml` · `../p-fancake/design/craft-allowlist.yaml` — p-fancake 와
형제로 둔 체크아웃.
사본이 있으므로 **git worktree 에서도 변수 없이 통과한다.** 그래도 못 찾으면 통과가 아니라
실패한다 — 계약 대조가 조용히 사라지면 안 된다.
사본은 정본과 갈리면 저장소마다 다른 답을 내므로, **정본을 고칠 때 사본에도 함께 복사한다.**
파일 머리에 같은 문장을 적어 두었다.
```bash
cp /path/to/p-fancake/design/craft-allowlist.yaml design/craft-allowlist.yaml
# 머리 주석 5줄은 사본에만 있으니 지우지 말고 APP_TABS·CAPS_OK 만 맞춘다.
```
## 빌드 ## 빌드
```bash ```bash
+23
View File
@@ -0,0 +1,23 @@
# ⚠️ 이 파일은 사본이다. 정본은 상위 저장소 p-fancake/design/craft-allowlist.yaml 이며,
# 정본을 갱신할 때 이 사본에도 함께 복사한다. 두 파일이 갈리면 계약 대조(APP_TABS)가
# 저장소마다 다른 답을 내므로, 사본만 고치는 일은 없어야 한다.
# 사본을 둔 이유는 git worktree 에서 형제 경로(../design/)가 없어 스모크 테스트가
# 계약 대조를 못 하고 실패하기 때문이다(BUILD.md 「테스트」).
# craft-allowlist.yaml — 크래프트 체커의 "분류 결정"을 적는 곳.
# 경고를 끄는 곳이 아니다. 각 항목에 근거(reason)가 없으면 킷이 읽지 않는다.
# 계약: 하네스/디자인/resources/codex/check_craft_render.py C2
CAPS_OK:
# ── 5탭 라벨 — 견적서 v2.1 「5탭 네비게이션」으로 고정된 화면 이름이다.
# 모든 앱 화면의 하단 탭바에 그대로 들어가므로 화면당 대문자 예산을 통째로 먹는다.
- {term: STATION, reason: "견적서 v2.1 5탭 라벨 · 한글 대체 불가(계약 표기)"}
- {term: INSIDE, reason: "견적서 v2.1 5탭 라벨 · 한글 대체 불가(계약 표기)"}
- {term: GALAXY, reason: "견적서 v2.1 5탭 라벨 · 한글 대체 불가(계약 표기)"}
# ── 지표·재화 약어 — 화면 전반에서 단위처럼 쓰인다.
- {term: FPI, reason: "Fandom Planet Index 약어 · PRD v9.0 PRD-03 공식 명칭"}
# ── 앱 하단 5탭 라벨 (게이트 C15) ──────────────────────────
# 견적서 v2.1 「5탭 네비게이션」으로 확정된 제품 IA다. 렌더러가 화면마다 한글화하거나
# 순서를 바꾸면 계약 산출물과 어긋난다. 순서까지 정확히 일치해야 한다.
APP_TABS: [HOME, INSIDE, GALAXY, STATION, MY]
+24 -1
View File
@@ -300,8 +300,31 @@ Future<void> _enterShell(WidgetTester tester) async {
/// `design/craft-allowlist.yaml` 의 `APP_TABS` 를 읽어 온다. /// `design/craft-allowlist.yaml` 의 `APP_TABS` 를 읽어 온다.
/// 앱 코드가 계약 표기에서 벗어나면 여기서 깨진다. /// 앱 코드가 계약 표기에서 벗어나면 여기서 깨진다.
///
/// 계약 정본은 상위 저장소(p-fancake)에 있고 이 저장소(p-fanuzu)는 분리돼 있어
/// 상대 경로가 체크아웃 위치를 탄다. 그래서 저장소 안에 사본을 두고
/// **FANUZU_DESIGN_DIR → 저장소 내 design/ → 형제 경로** 순으로 본다.
/// 사본이 있으므로 git worktree 에서도 환경 변수 없이 통과한다.
///
/// 하나도 없으면 **통과가 아니라 실패**로 끝낸다 — 못 읽은 것을 「맞다」로 세면
/// 계약 대조가 조용히 사라진다.
List<String> _contractTabs() { List<String> _contractTabs() {
final file = File('../design/craft-allowlist.yaml'); final candidates = [
if (Platform.environment['FANUZU_DESIGN_DIR'] case final dir?)
'$dir/craft-allowlist.yaml',
'design/craft-allowlist.yaml',
'../design/craft-allowlist.yaml',
'../p-fancake/design/craft-allowlist.yaml',
];
final file = candidates
.map(File.new)
.firstWhere(
(f) => f.existsSync(),
orElse: () => throw StateError(
'craft-allowlist.yaml 을 찾지 못했다. FANUZU_DESIGN_DIR 로 지정하라. '
'찾아본 곳: ${candidates.join(' · ')}',
),
);
final line = file.readAsLinesSync().firstWhere( final line = file.readAsLinesSync().firstWhere(
(l) => l.trimLeft().startsWith('APP_TABS:'), (l) => l.trimLeft().startsWith('APP_TABS:'),
orElse: () => orElse: () =>