빌드 안내 · 주석을 이 저장소 기준으로 정리
- 빌드 안내(BUILD.md)의 JDK 경로를 설치 위치와 무관한 지정 방식으로 변경 - 5탭 계약 파일 탐색 순서를 이 저장소 구조(app/design → 루트 design)로 정리 - README · 보안 규칙 · 소스 주석에서 이 저장소에 없는 문서 경로를 문서 이름으로 바꾸거나 삭제 - Flutter 버전 고정 파일(app/.fvmrc, 3.47.2) 추가, README 필수 환경을 같은 버전으로 갱신 - 동작 변경 없음
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"flutter": "3.47.2"
|
||||
}
|
||||
+11
-22
@@ -6,7 +6,7 @@ Gradle 이 조용히 실패하지 않고 명확히 죽으므로 아래를 그대
|
||||
| 구성 | 값 | 비고 |
|
||||
|---|---|---|
|
||||
| Flutter | **3.47.2** | `fvm` 으로 고정 — `app/.fvmrc` |
|
||||
| JDK | **Temurin 17** | `~/.jdks/jdk-17.0.20.1+1/Contents/Home` |
|
||||
| JDK | **Temurin 17** | `fvm flutter config --jdk-dir` 로 지정 |
|
||||
| Gradle | 8.14 | `android/gradle/wrapper/gradle-wrapper.properties` |
|
||||
| AGP | 8.11.1 | `android/settings.gradle.kts` |
|
||||
| Android SDK | `/opt/homebrew/share/android-commandlinetools` | `brew install --cask android-commandlinetools` |
|
||||
@@ -21,40 +21,29 @@ Android Studio 번들 JBR 은 **JDK 25** 이고 **Gradle 8.14 와 비호환**이
|
||||
JDK 17 을 쓰는 쪽을 택했다.
|
||||
|
||||
```bash
|
||||
fvm flutter config --jdk-dir ~/.jdks/jdk-17.0.20.1+1/Contents/Home
|
||||
fvm flutter config --jdk-dir <JDK 17 설치 경로>
|
||||
```
|
||||
|
||||
`brew install --cask temurin@17` 은 sudo 를 요구한다. sudo 없이 쓰려면 Adoptium
|
||||
tar.gz 를 `~/.jdks/` 에 풀면 된다.
|
||||
tar.gz 를 사용자 폴더에 풀고 그 경로를 지정하면 된다.
|
||||
|
||||
## 테스트
|
||||
|
||||
```bash
|
||||
export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools
|
||||
fvm flutter analyze
|
||||
FANUZU_DESIGN_DIR=/path/to/p-fancake/design fvm flutter test
|
||||
fvm flutter test
|
||||
```
|
||||
|
||||
`FANUZU_DESIGN_DIR` 은 5탭 계약 대조용이다. 스모크 테스트가
|
||||
`craft-allowlist.yaml` 의 `APP_TABS` 를 직접 읽어 앱의 `appTabs` 와 정확 일치를 검증한다.
|
||||
**정본은 상위 저장소 `p-fancake/design/craft-allowlist.yaml`** 이고 이 저장소는 분리돼
|
||||
있으므로, 같은 파일의 사본을 `design/craft-allowlist.yaml` 로 두었다. 탐색 순서는 아래와 같다.
|
||||
스모크 테스트가 `craft-allowlist.yaml` 의 `APP_TABS` 를 직접 읽어 앱의 `appTabs` 와 정확 일치를
|
||||
검증한다(5탭 계약 대조). 파일은 아래 순서로 찾는다.
|
||||
|
||||
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 와
|
||||
형제로 둔 체크아웃.
|
||||
1. `$FANUZU_DESIGN_DIR/craft-allowlist.yaml` — 다른 위치의 파일을 가리킬 때 쓴다.
|
||||
2. `app/design/craft-allowlist.yaml` — 앱 폴더 안의 사본.
|
||||
3. 저장소 루트 `design/craft-allowlist.yaml`.
|
||||
|
||||
사본이 있으므로 **git worktree 에서도 변수 없이 통과한다.** 그래도 못 찾으면 통과가 아니라
|
||||
실패한다 — 계약 대조가 조용히 사라지면 안 된다.
|
||||
|
||||
사본은 정본과 갈리면 저장소마다 다른 답을 내므로, **정본을 고칠 때 사본에도 함께 복사한다.**
|
||||
파일 머리에 같은 문장을 적어 두었다.
|
||||
|
||||
```bash
|
||||
cp /path/to/p-fancake/design/craft-allowlist.yaml design/craft-allowlist.yaml
|
||||
# 머리 주석 5줄은 사본에만 있으니 지우지 말고 APP_TABS·CAPS_OK 만 맞춘다.
|
||||
```
|
||||
하나도 못 찾으면 통과가 아니라 실패한다 — 계약 대조가 조용히 사라지면 안 된다.
|
||||
루트 파일을 고칠 때는 `app/design/` 사본도 함께 맞춘다.
|
||||
|
||||
## 빌드
|
||||
|
||||
|
||||
+7
-17
@@ -16,7 +16,7 @@
|
||||
|
||||
## 2. 빌드·실행
|
||||
|
||||
전제: Flutter stable 3.41.7 / Dart 3.11.5 (검증에 쓴 버전), Android SDK 36, Xcode 26.6
|
||||
전제: Flutter stable 3.47.2 / Dart 3.13.2 (`app/.fvmrc` 로 고정), Android SDK 36, Xcode 26.6
|
||||
|
||||
```bash
|
||||
cd app
|
||||
@@ -30,7 +30,7 @@ flutter build apk --debug # Android 디버그 APK
|
||||
빌드 산출물은 `build/app/outputs/flutter-apk/app-debug.apk` 에 떨어진다.
|
||||
|
||||
> ⚠️ **`pubspec.yaml` 의 의존성을 추가·변경·업그레이드하면 최소 지원 OS 하한을 재계산할 것.**
|
||||
> 절차: `주간보고/2주차/src/2주차_플러그인_선검증리포트.md` §3.5
|
||||
> 절차: 2주차 플러그인 선검증 리포트 §3.5
|
||||
>
|
||||
> 최소 지원 OS 는 **별첨1 v1.0 에 계약값으로 박힌다**(견적서 1228행). 지킬 수 없는 하한을
|
||||
> 계약에 적으면 그 아래 단말의 장애가 개발사 책임이 된다. 선검증 리포트의 값은
|
||||
@@ -42,7 +42,7 @@ flutter build apk --debug # Android 디버그 APK
|
||||
|
||||
## 3. 디자인 토큰 — 손으로 옮기지 않는다
|
||||
|
||||
`lib/core/theme/tokens.dart` 는 **생성물**이다. 직접 고치면 시안(`시안/assets/ds.css`)과 어긋난다.
|
||||
`lib/core/theme/tokens.dart` 는 **생성물**이다. 직접 고치면 토큰 문서(`design/design-tokens.md`)와 어긋난다.
|
||||
|
||||
```bash
|
||||
cd app
|
||||
@@ -79,7 +79,7 @@ Firebase 설정이 없으면 실 소셜 로그인이 물리적으로 불가능
|
||||
* `FirebaseBoot.reason` 은 사용자에게 보이는 문자열이라 **내부 파일 경로를 넣지 않는다.**
|
||||
경로와 원시 예외는 `FirebaseBoot.detail` 로 분리해 로그에만 남긴다.
|
||||
|
||||
**가드가 두 겹인 이유 (교차검증 A-01 반영)**
|
||||
**가드가 두 겹인 이유**
|
||||
|
||||
```dart
|
||||
bool get isMockSessionAvailable => !FirebaseBoot.isLive && !_releaseMode;
|
||||
@@ -191,7 +191,7 @@ Firebase가 live가 되면 보존된 `currentUser`를 앱 시작 시 복원하
|
||||
**이메일 가입은 구현하지 않았다.** CD-18 미확정(Q2)이라 `SocialAuthProvider` 인터페이스에
|
||||
구현체 하나가 더 붙을 자리만 비워 뒀다. **기능 플래그 상수는 두지 않았다** — 읽는 분기가 없는
|
||||
플래그는 "false 라서 닫혀 있다" 는 착각만 만들고, 누가 true 로 바꿔도 아무 일이 일어나지 않는다
|
||||
(교차검증 A-02). 지금 이메일을 닫고 있는 것은 `AuthRepository._defaultProviders()` 에
|
||||
. 지금 이메일을 닫고 있는 것은 `AuthRepository._defaultProviders()` 에
|
||||
구현체가 없다는 사실 하나다.
|
||||
|
||||
## 7. 계약 고정 표기 — 5탭
|
||||
@@ -204,9 +204,8 @@ HOME · INSIDE · GALAXY · STATION · MY
|
||||
|
||||
| 근거 | 위치 |
|
||||
|---|---|
|
||||
| 견적서 v2.1 「5탭 네비게이션」 | `Docs/Fancake_팬덤플랫폼_견적서.html` |
|
||||
| 견적서 v2.1 「5탭 네비게이션」 | 견적서 v2.1 |
|
||||
| 크래프트 게이트 C15 | `design/craft-allowlist.yaml` → `APP_TABS` |
|
||||
| 시안 하단 탭바 | `시안/screens/S-{H-HOME,I-LOUNGE,G-PLANET,S-HUB,M-HOME}.html` 의 `<nav class="tabbar">` |
|
||||
|
||||
**한글화·치환·순서 변경 금지.** 스모크 테스트가 `design/craft-allowlist.yaml` 을 직접 읽어
|
||||
문자열과 순서를 정확 일치로 검증한다 — 앱 코드만 고치면 테스트가 깨진다.
|
||||
@@ -288,16 +287,7 @@ $ python3 tool/gen_tokens.py
|
||||
|
||||
`contrast_checks` 25쌍에는 앱 테마가 조립하며 새로 만드는 3쌍(선택 탭 라벨 9.21:1 ·
|
||||
인디케이터 위 선택 라벨 7.93:1 · 인디케이터 위 비선택 라벨 8.05:1)이 포함돼 있다.
|
||||
토큰 문서에만 있는 쌍을 검사하면 위젯 테마에서 대비가 무너져도 생성기가 통과를 찍는다(교차검증 A-04).
|
||||
|
||||
### 토큰 대조 — 시안 CSS ↔ 생성된 Dart
|
||||
|
||||
`시안/assets/ds.css` 의 `:root` 변수와 `tokens.dart` 의 색 토큰을 값 비교했다.
|
||||
|
||||
```
|
||||
ds.css 변수 119개 · tokens.dart 색 토큰 51개 · 공통 51개
|
||||
값 불일치: 0개
|
||||
```
|
||||
토큰 문서에만 있는 쌍을 검사하면 위젯 테마에서 대비가 무너져도 생성기가 통과를 찍는다.
|
||||
|
||||
### 실기기 기동 확인 (Android 에뮬레이터 · API 36)
|
||||
|
||||
|
||||
@@ -2,3 +2,6 @@ analyzer:
|
||||
exclude:
|
||||
# Firebase 도구가 내려받는 서드파티 소스. 우리 분석 대상이 아니다.
|
||||
- '**/node_modules/**'
|
||||
- build/**
|
||||
- android/**
|
||||
- ios/**
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
# ⚠️ 이 파일은 사본이다. 정본은 상위 저장소 p-fancake/design/craft-allowlist.yaml 이며,
|
||||
# 정본을 갱신할 때 이 사본에도 함께 복사한다. 두 파일이 갈리면 계약 대조(APP_TABS)가
|
||||
# 저장소마다 다른 답을 내므로, 사본만 고치는 일은 없어야 한다.
|
||||
# 사본을 둔 이유는 git worktree 에서 형제 경로(../design/)가 없어 스모크 테스트가
|
||||
# 계약 대조를 못 하고 실패하기 때문이다(BUILD.md 「테스트」).
|
||||
# ⚠️ 이 파일은 저장소 루트 design/craft-allowlist.yaml 의 사본이다.
|
||||
# 루트 파일을 고칠 때 이 사본도 함께 맞춘다(BUILD.md 「테스트」).
|
||||
|
||||
# craft-allowlist.yaml — 크래프트 체커의 "분류 결정"을 적는 곳.
|
||||
# 경고를 끄는 곳이 아니다. 각 항목에 근거(reason)가 없으면 킷이 읽지 않는다.
|
||||
# 계약: 하네스/디자인/resources/codex/check_craft_render.py C2
|
||||
|
||||
CAPS_OK:
|
||||
# ── 5탭 라벨 — 견적서 v2.1 「5탭 네비게이션」으로 고정된 화면 이름이다.
|
||||
|
||||
@@ -3,8 +3,8 @@ rules_version = '2';
|
||||
// FANUZU — Firestore 보안 규칙 (2주차 설계본 v0.1)
|
||||
//
|
||||
// 근거
|
||||
// - wireframe/handoff/rbac-summary.md 역할 3종 · 운영자 등급 3단계(D-18)
|
||||
// - Docs/별첨1_기능별_완료기준표.html 「전 기능 공통 완료 기준 — 서버검증 원칙」
|
||||
// - 요구사항 정의서 권한 요구 역할 3종 · 운영자 등급 3단계
|
||||
// - 별첨 1 기능별 완료 기준표 「전 기능 공통 완료 기준 — 서버검증 원칙」
|
||||
// - 1주차 정책 설계서 P-02 원장 무결성 · P-06 어뷰징 방어
|
||||
//
|
||||
// 3대 원칙
|
||||
@@ -48,15 +48,15 @@ service cloud.firestore {
|
||||
function myUser() {
|
||||
return get(/databases/$(database)/documents/users/$(request.auth.uid)).data;
|
||||
}
|
||||
// 작성자 표시명이 프로필 원본과 같은지 — 운영팀 사칭 차단 (교차검증 F-05)
|
||||
// 작성자 표시명이 프로필 원본과 같은지 — 운영팀 사칭 차단
|
||||
function authorNameMatchesProfile() {
|
||||
return request.resource.data.authorName == myUser().displayName;
|
||||
}
|
||||
// 소속 팬덤이 본인 소속과 같은지 — 타 팬덤 피드 삽입 차단 (교차검증 F-05)
|
||||
// 소속 팬덤이 본인 소속과 같은지 — 타 팬덤 피드 삽입 차단
|
||||
function fandomIsMine() {
|
||||
return request.resource.data.fandomId == myUser().fandomId;
|
||||
}
|
||||
// 무인증 열람을 허용하는 캠페인 상태 — draft·review 는 제외 (교차검증 F-08)
|
||||
// 무인증 열람을 허용하는 캠페인 상태 — draft·review 는 제외
|
||||
function campaignIsPublic() {
|
||||
return resource.data.status in ['funding', 'success', 'fail', 'report'];
|
||||
}
|
||||
@@ -97,7 +97,7 @@ service cloud.firestore {
|
||||
match /users/{uid} {
|
||||
allow get: if isSelf(uid) || isViewer();
|
||||
allow list: if isViewer(); // 전체 사용자 목록은 운영자만(S-AD-USER)
|
||||
// 교차검증 F-06 — 클라이언트 생성 경로를 닫는다.
|
||||
// 클라이언트 생성 경로를 닫는다.
|
||||
// 기존에는 클라이언트 생성도 허용해 ① fn-auth-oncreate(§8.5)와 생성 경합이 생기고
|
||||
// ② 클라이언트가 먼저 만들면 totalContributedPop 등 서버 산출 필드가 빈
|
||||
// 사용자 문서가 남았다(별첨1 조건부적합 제외 ④ 로그인 불가와 닿는 지점).
|
||||
@@ -150,7 +150,7 @@ service cloud.firestore {
|
||||
// 보유 배지 이력. 부여는 서버 판정.
|
||||
// 화면: S-M-BADGE(본인 도감) · S-M-IDCARD(본인 카드)
|
||||
//
|
||||
// 교차검증 G-04 — 예전 주석은 「타인 프로필의 배지 노출 허용」이라 적고 signedIn() 으로 열어 뒀는데,
|
||||
// 예전 주석은 「타인 프로필의 배지 노출 허용」이라 적고 signedIn() 으로 열어 뒀는데,
|
||||
// **1차 범위에 타인 프로필 화면이 없다**(화면 95개 전수 확인). 주석이 사실과 달랐다.
|
||||
// 타인 배지가 실제로 보이는 곳은 S-M-HERO 히어로 랭킹 하나뿐이고,
|
||||
// 그것은 /fandoms/{f}/members/{uid}.honorBadgeIds 비정규화 필드로 읽는다(설계서 §2.3·§4.4).
|
||||
@@ -179,7 +179,7 @@ service cloud.firestore {
|
||||
// 본인은 자기 동의 이력을 볼 수 있어야 한다(열람권). 화면: S-A-TERMS · S-M-SETTINGS
|
||||
match /users/{uid}/consents/{consentId} {
|
||||
allow read: if isSelf(uid) || isViewer();
|
||||
allow write: if false; // 기록은 fn-consent-record 경유 (교차검증 F-04)
|
||||
allow write: if false; // 기록은 fn-consent-record 경유
|
||||
}
|
||||
|
||||
// 차단 목록. 본인만 읽고 쓴다.
|
||||
@@ -315,19 +315,19 @@ service cloud.firestore {
|
||||
// 캠페인. 목표 달성률·후원 합계는 집계 필드라 클라이언트가 못 쓴다.
|
||||
// 화면: S-C-LIST · S-C-DETAIL · S-C-NEW · S-AD-CAMPAIGN
|
||||
//
|
||||
// ⚠️ 목록 쿼리에는 status 필터가 필수다 (교차검증 G-03).
|
||||
// ⚠️ 목록 쿼리에는 status 필터가 필수다.
|
||||
// 아래 read 규칙이 공개 상태만 허용하므로, status 필터가 없는 목록 쿼리는
|
||||
// draft·review 문서까지 매칭돼 **쿼리 전체가 거부**된다(Firestore 는 list 규칙을
|
||||
// 반환 후보 전체에 적용한다). 인덱스 문제로 오인하기 쉬운 지점이다.
|
||||
// 허용되는 목록 쿼리 형태는 설계서 §6.1 IDX-15·16·17·19 참조.
|
||||
match /campaigns/{campaignId} {
|
||||
// 교차검증 F-08 — 예전에는 read: if true 라 심사 중(review)·초안(draft) 캠페인 본문이
|
||||
// 예전에는 read: if true 라 심사 중(review)·초안(draft) 캠페인 본문이
|
||||
// URL만 알면 미로그인 상태로 읽혔다. FR-CMP-01「승인 대기 캠페인은 목록에 노출하지 않는다」(E-CMP-03)를
|
||||
// 클라이언트 쿼리 필터가 아니라 규칙에서 강제한다. 공개 상태만 무인증 열람 대상이다.
|
||||
allow read: if campaignIsPublic()
|
||||
|| isOps()
|
||||
|| (signedIn() && resource.data.creatorUid == request.auth.uid);
|
||||
// 교차검증 F-02 — 화이트리스트 방식. 목록에 없는 필드가 하나라도 있으면 거부되므로
|
||||
// 화이트리스트 방식. 목록에 없는 필드가 하나라도 있으면 거부되므로
|
||||
// 나중에 집계 필드가 추가돼도 자동으로 막힌다. 집계·분류 필드는 초기값을 못 벗어난다.
|
||||
allow create: if signedIn()
|
||||
&& request.resource.data.keys().hasOnly([
|
||||
@@ -367,7 +367,7 @@ service cloud.firestore {
|
||||
// 화면: S-C-RESULT
|
||||
match /campaigns/{campaignId}/reports/{reportId} {
|
||||
allow read: if true;
|
||||
// 교차검증 F-07 — §7.3 표는 「성공 캠페인 개설자」라고 적었는데 규칙은 creatorUid만 보고
|
||||
// §7.3 표는 「성공 캠페인 개설자」라고 적었는데 규칙은 creatorUid만 보고
|
||||
// 상태를 확인하지 않아 실패·초안 캠페인에도 결과보고를 올릴 수 있었다.
|
||||
// 별첨1 ⑤ 판정 기준 「결과보고 게시 확인」의 대상은 성공한 캠페인이다.
|
||||
allow create: if signedIn()
|
||||
@@ -385,7 +385,7 @@ service cloud.firestore {
|
||||
// 화면: S-I-LOUNGE · S-I-POST · S-I-POST-NEW
|
||||
match /posts/{postId} {
|
||||
allow read: if signedIn();
|
||||
// 교차검증 F-05 — 예전에는 authorUid·likeCount·visibility 3개만 봐서
|
||||
// 예전에는 authorUid·likeCount·visibility 3개만 봐서
|
||||
// ① authorName 을 "FANUZU 운영팀"으로 적어 사칭 ② commentCount 위조
|
||||
// ③ 소속하지 않은 팬덤 피드에 글 삽입이 가능했다. 화이트리스트 + 원본 대조로 닫는다.
|
||||
allow create: if authorIsSelf()
|
||||
@@ -398,7 +398,7 @@ service cloud.firestore {
|
||||
&& request.resource.data.likeCount == 0
|
||||
&& request.resource.data.commentCount == 0
|
||||
&& request.resource.data.visibility == 'visible';
|
||||
// 교차검증 F-14 — 'title' 을 뺐다. 설계서 §2.6 posts 스키마에 title 필드가 없다.
|
||||
// 'title' 을 뺐다. 설계서 §2.6 posts 스키마에 title 필드가 없다.
|
||||
allow update: if signedIn() && resource.data.authorUid == request.auth.uid
|
||||
&& touchesOnly(['body','imageUrls','updatedAt']);
|
||||
// 숨김·복원. rbac-summary 「콘텐츠 숨김·복원」 = 운영 이상
|
||||
@@ -416,7 +416,7 @@ service cloud.firestore {
|
||||
// 댓글. 화면: S-I-POST
|
||||
match /posts/{postId}/comments/{commentId} {
|
||||
allow read: if signedIn();
|
||||
// 교차검증 F-05 — 댓글에도 같은 표시명 사칭 경로가 있었다.
|
||||
// 댓글에도 같은 표시명 사칭 경로가 있었다.
|
||||
allow create: if authorIsSelf()
|
||||
&& request.resource.data.keys().hasOnly([
|
||||
'authorUid','authorName','body','visibility','createdAt','updatedAt'
|
||||
@@ -440,7 +440,7 @@ service cloud.firestore {
|
||||
// 화면: S-I-QUESTION · S-I-ANSWER-NEW
|
||||
match /questions/{questionId}/answers/{answerId} {
|
||||
allow read: if signedIn();
|
||||
// 교차검증 F-05 — 답변에도 같은 표시명 사칭 경로가 있었다.
|
||||
// 답변에도 같은 표시명 사칭 경로가 있었다.
|
||||
allow create: if authorIsSelf()
|
||||
&& request.resource.data.keys().hasOnly([
|
||||
'authorUid','authorName','body','sourceUrls','accepted',
|
||||
@@ -505,7 +505,7 @@ service cloud.firestore {
|
||||
}
|
||||
|
||||
// 푸시 발송 이력·도달률. 운영자만 보고, 발송 집계는 Functions(FCM)가 쓴다.
|
||||
// 화면: S-AD-PUSH · rbac-summary 「푸시 발송 관리」 = 운영 이상 (교차검증 F-04 #2)
|
||||
// 화면: S-AD-PUSH · rbac-summary 「푸시 발송 관리」 = 운영 이상
|
||||
match /pushCampaigns/{pushId} {
|
||||
allow read: if isViewer();
|
||||
allow write: if false; // 발송·집계는 fn-push-send 경유
|
||||
@@ -579,7 +579,7 @@ service cloud.firestore {
|
||||
allow write: if false;
|
||||
}
|
||||
|
||||
// ─────────────── 12. 컬렉션 그룹 쿼리 (교차검증 F-01) ───────────────
|
||||
// ─────────────── 12. 컬렉션 그룹 쿼리 ───────────────
|
||||
//
|
||||
// Firestore에서 `match /users/{uid}/popTransactions/{txId}` 형태의 규칙은
|
||||
// **단일 컬렉션 쿼리에만** 적용되고 collectionGroup('popTransactions') 에는 적용되지 않는다.
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ERR, fail } from '../core/errors.js';
|
||||
/**
|
||||
* `fn-auth-oncreate` — 계정 문서의 유일한 생성 주체.
|
||||
*
|
||||
* 보안 규칙이 `users` 의 클라이언트 `create` 를 닫아 두었으므로(교차검증 F-06)
|
||||
* 보안 규칙이 `users` 의 클라이언트 `create` 를 닫아 두었으므로
|
||||
* 이 트리거가 만들지 않으면 계정 문서가 존재하지 않는다. 생성 주체를 하나로
|
||||
* 고정한 이유는 경합 때문이다 — 클라이언트가 먼저 만들면 서버 산출 필드가 빈
|
||||
* 사용자 문서가 남는다.
|
||||
@@ -49,7 +49,7 @@ export const fnAuthOnCreate = functionsV1.auth.user().onCreate(async (user) => {
|
||||
});
|
||||
|
||||
/**
|
||||
* `fn-consent-record` — 약관 동의 이력(교차검증 F-04).
|
||||
* `fn-consent-record` — 약관 동의 이력.
|
||||
*
|
||||
* append-only 다. 철회도 기존 행을 고치지 않고 `granted: false` 인 새 행으로 남긴다.
|
||||
* 동의 시점을 되짚을 수 없으면 분쟁에서 증명할 수단이 사라진다.
|
||||
|
||||
@@ -37,10 +37,9 @@ class AppTab {
|
||||
|
||||
/// 계약 고정 5탭.
|
||||
///
|
||||
/// 근거 3개가 서로 일치한다:
|
||||
/// 근거 2개가 서로 일치한다:
|
||||
/// - 견적서 v2.1 「5탭 네비게이션」
|
||||
/// - `design/craft-allowlist.yaml` → `APP_TABS: [HOME, INSIDE, GALAXY, STATION, MY]`
|
||||
/// - 시안 `시안/screens/S-*.html` 의 `<nav class="tabbar">` (5개 루트 화면 전부 동일)
|
||||
///
|
||||
/// 라벨 문자열과 순서는 스모크 테스트가 정확 일치로 검증한다.
|
||||
/// 아이콘은 Figma Iconset(house · message-square · badge · circle-user-round)이다.
|
||||
|
||||
@@ -73,7 +73,7 @@ final class AuthCancelled extends AuthOutcome {
|
||||
const AuthCancelled();
|
||||
}
|
||||
|
||||
/// 실패. [code] 는 `wireframe/planning/error-catalog.yaml` 의 ID 다(소셜 실패 = E-AUTH-03).
|
||||
/// 실패. [code] 는 에러 카탈로그 ID 다(소셜 실패 = E-AUTH-03).
|
||||
final class AuthFailed extends AuthOutcome {
|
||||
const AuthFailed(this.code, this.message);
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ const lineProvider = KeylessSocialProvider(AuthProviderKind.line, [
|
||||
'LINE Developers 콘솔 채널 등록(사업자 정보 필요)',
|
||||
]);
|
||||
|
||||
/// SDK 코드를 에러 카탈로그(`wireframe/planning/error-catalog.yaml`) ID 로 옮긴다.
|
||||
/// SDK 코드를 에러 카탈로그 ID 로 옮긴다.
|
||||
///
|
||||
/// **계정 존재 여부를 드러내지 않는다.** 「등록되지 않은 이메일」과 「비밀번호
|
||||
/// 오류」를 갈라 주면 어느 이메일이 가입돼 있는지 훑을 수 있다.
|
||||
|
||||
@@ -314,10 +314,8 @@ Future<void> _enterShell(WidgetTester tester) async {
|
||||
/// `design/craft-allowlist.yaml` 의 `APP_TABS` 를 읽어 온다.
|
||||
/// 앱 코드가 계약 표기에서 벗어나면 여기서 깨진다.
|
||||
///
|
||||
/// 계약 정본은 상위 저장소(p-fancake)에 있고 이 저장소(p-fanuzu)는 분리돼 있어
|
||||
/// 상대 경로가 체크아웃 위치를 탄다. 그래서 저장소 안에 사본을 두고
|
||||
/// **FANUZU_DESIGN_DIR → 저장소 내 design/ → 형제 경로** 순으로 본다.
|
||||
/// 사본이 있으므로 git worktree 에서도 환경 변수 없이 통과한다.
|
||||
/// 계약 파일은 저장소 루트 `design/` 에 있고, `app/design/` 에 같은 사본을 둔다.
|
||||
/// **FANUZU_DESIGN_DIR → app/design/ → 저장소 루트 design/** 순으로 본다.
|
||||
///
|
||||
/// 하나도 없으면 **통과가 아니라 실패**로 끝낸다 — 못 읽은 것을 「맞다」로 세면
|
||||
/// 계약 대조가 조용히 사라진다.
|
||||
@@ -327,7 +325,6 @@ List<String> _contractTabs() {
|
||||
'$dir/craft-allowlist.yaml',
|
||||
'design/craft-allowlist.yaml',
|
||||
'../design/craft-allowlist.yaml',
|
||||
'../p-fancake/design/craft-allowlist.yaml',
|
||||
];
|
||||
final file = candidates
|
||||
.map(File.new)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
"""design/design-tokens.md 의 머신블록(<!-- TOKENS --> 아래 yaml) → lib/core/theme/tokens.dart 생성기.
|
||||
|
||||
- SoT 는 design/design-tokens.md 하나다. tokens.dart 를 손으로 고치면 시안(시안/assets/ds.css)과 어긋난다.
|
||||
- SoT 는 design/design-tokens.md 하나다. tokens.dart 를 손으로 고치면 토큰 문서와 어긋난다.
|
||||
- 재실행 가능(멱등). 실행: python3 tool/gen_tokens.py (앱 루트 app/ 에서)
|
||||
- 부수 기능: design-tokens.md 의 contrast_checks 쌍을 WCAG 로 재계산해서 표로 찍고,
|
||||
AA(4.5:1) 미달이 하나라도 있으면 exit 1. 테마가 대비를 깎았는지 여기서 걸린다.
|
||||
|
||||
Reference in New Issue
Block a user