- REGISTRY-001 Summary 문자열 4곳 (additive, revision 은 1 유지). registry-governance 는 본문 서술 12곳의 '8개'도 함께 9개로 갱신 - OFFLINE-CACHE-001 pin @1->@2 와 Summary·적용점 (behavior-change) - DELEG-FE-007~011 delegate 5곳 접수 완료 — 미접수 위임 0건 - runtime-schema-validation 이 FLOW-FE-EVENT-003/004 소유를 명시 - env-runtime-config 가 FE-REG-CAPABILITY registry 와 FE-GATE-033 gate owner 를 취득하고 claim 2건 추가 - 신규 6개 노트의 delegation pin 을 기존 @1 형식으로 정규화
379 lines
43 KiB
Markdown
379 lines
43 KiB
Markdown
---
|
||
title: branch / feature-frontend-observability-logging-trace-contract
|
||
source_type: branch-note
|
||
status: raw
|
||
id: BR-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012
|
||
kind: project-work-item
|
||
project: ca-skeleton-frontend-operational-contract
|
||
work_item: WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-012
|
||
inherits: [DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1, DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1]
|
||
refines: []
|
||
overrides: []
|
||
depends_on: [WI-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-004]
|
||
contract_packet: 1
|
||
branch: feature-frontend-observability-logging-trace-contract
|
||
parent_branch:
|
||
related_projects: [ca-skeleton-frontend, ca-skeleton]
|
||
governing_docs: [raw/project-notes/ca-skeleton-frontend-operational-contract.md]
|
||
tags: [branch, ca-skeleton, frontend, observability, error-handling, javascript]
|
||
created: 2026-07-18
|
||
target_merge:
|
||
status_label: in-progress
|
||
contract_packet_sha256: e79f4a8ea9b3ea9f54126cdb47a1228194322b9fa883ca499d546f9ffa607e63
|
||
imports: [FE-OC-008@1, FE-OC-015@1, FE-OC-021@1, FE-OC-025@1]
|
||
|
||
---
|
||
|
||
# branch: feature-frontend-observability-logging-trace-contract
|
||
|
||
> Layer: `raw/branch-notes/` — 단일 브랜치의 TODO·결정·진행 기록. `/branch-spec` 로 hub 계약을 구현-준비 spec 으로 내렸다. 프론트엔드 코드가 아직 없으므로 **모든 구현 주장은 `planned`** 이며 코드 evidence 는 repository 생성 후 채운다.
|
||
|
||
<!-- section-id: branch-parent -->
|
||
## 부모 (필수)
|
||
|
||
- [[raw/project-notes/ca-skeleton-frontend-operational-contract]]
|
||
|
||
<!-- GENERATED: branch-contract:start -->
|
||
<!-- section-id: branch-contract-packet -->
|
||
## 브랜치 계약 패킷
|
||
|
||
- **생성 시 프로젝트 개정**: `1`
|
||
- **패킷 스키마**: `contract_packet: 1`
|
||
- **완료 조건**: telemetry registry·redaction·bounded queue·sink failure test가 통과한다
|
||
|
||
<!-- section-id: inherited-project-decisions -->
|
||
### 상속한 프로젝트 결정
|
||
|
||
| Decision Ref | Project Summary | Branch Application | Source |
|
||
|---|---|---|---|
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-TELEMETRY-001@1` | telemetry는 best-effort queue와 redaction을 사용하며 sink failure가 UI를 실패시키지 않는다 | TelemetryPort·queue·redaction·degradation 계약에 적용한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
||
| `DEC-CA-SKELETON-FRONTEND-OPERATIONAL-CONTRACT-REGISTRY-001@1` | route, API operation, env, storage, error, query, telemetry, release token, capability를 9개 registry로 관리한다 | hub §5.8이 정의한 FE-REG-TELEMETRY 스키마·초기 event를 코드 registry로 구현한다 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] |
|
||
|
||
<!-- section-id: branch-local-decisions -->
|
||
### 브랜치 지역 결정
|
||
|
||
| Decision ID | Decision | Relation | Supporting Claims | Status |
|
||
|---|---|---|---|---|
|
||
| D1 | telemetry를 best-effort non-blocking 경로로 격리한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D2 | low-cardinality allowlist와 forbidden attribute redaction을 강제한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D3 | bounded queue와 비재귀 drop reporting을 사용한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D4 | FE-REG-TELEMETRY를 event schema의 single SSOT로 사용한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D5 | backend 지원 여부에 따라 trace correlation을 전파하거나 local ID로 강등한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D6 | delivery transport를 adapter-owned degradation 경로로 둔다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D7 | terminal failure telemetry를 bounded safe event로 제한한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
| D8 | TELEMETRY_ENABLED를 composition-root kill-switch로 소비한다 | `local` | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | `proposed` |
|
||
|
||
<!-- section-id: declared-overrides -->
|
||
### 선언한 예외
|
||
|
||
| Override ID | Overrides | Reason | Approval | Status |
|
||
|---|---|---|---|---|
|
||
|
||
없음.
|
||
<!-- GENERATED: branch-contract:end -->
|
||
|
||
<!-- section-id: branch-goal -->
|
||
## 목표
|
||
|
||
이 브랜치는 project-wide contract `FE-OC-014` (telemetry 는 best-effort 이며 render·API success 를 차단하면 안 되고 PII·token 을 전송하면 안 됨) 와 그 owner decision [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 (telemetry = best-effort queue + redaction, sink failure 는 UI 를 실패시키지 않음) 를 **구현자가 되묻지 않아도 코드를 쓸 수 있는 수준의 spec** 으로 내린다. 핵심 불변식은 **운영 격리 (operational isolation)** — telemetry 실패가 사용자 경험(render/API critical path)과 완전히 분리된다는 것이다. 동시에 이 브랜치는 `FE-REG-TELEMETRY` registry (§5.8 event/attribute/redaction) 의 single owner 로서 hub §5.8 이 정의한 최소 스키마와 초기 event 집합을 코드 registry 로 구현하고 emit 지점을 확정하며, `FE-OC-008` (실패→telemetry rule), `FE-OC-021` (low-cardinality 성능 attribute), `FE-OC-025` (`FE-RB-004` telemetry sink failure runbook) 에 telemetry 기여 edge 를 제공한다. 등급: 전 항목 `planned` (repository 부재).
|
||
|
||
- 이슈: 없음 (repository 미생성)
|
||
- PR: 없음
|
||
|
||
<!-- section-id: branch-scope -->
|
||
## 범위
|
||
|
||
### 포함 범위
|
||
|
||
- **Best-effort 전달 불변식** — telemetry send 가 render·API critical path 를 절대 block 하지 않음, sink/queue/adapter-init 실패가 UI 를 실패시키지 않음 (`FE-OC-014`, [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.2).
|
||
- **`FE-REG-TELEMETRY` registry 파일 소유** — registry 스키마와 초기 5 event 의 *정의* 는 hub §5.8 소유이고, 본 브랜치는 그 registry 를 코드로 구현하고 emit 지점을 확정한다(§5.8, §5.1). 자유 문자열 event 금지.
|
||
- **Redaction** — low-cardinality allowlist context 만 전송, forbidden attribute(token·email·raw URL/query/body·storage value·stack) 전송 금지 (§11.1, §5.8).
|
||
- **Bounded queue + overflow 정책 + 비재귀** — bounded queue, overflow drop 정책 registry 명시, telemetry 실패를 동일 sink 로 재귀 보고하지 않음 (§11.2, §16.4).
|
||
- **Delivery degradation** — local/dev console-safe sink, prod endpoint 부재/invalid 시 telemetry 만 degrade 하고 app 계속 실행 (§11.2, §16.4).
|
||
- **`TELEMETRY_ENABLED` kill-switch 소비** — runtime flag(default `false`)가 off 일 때 telemetry 전체를 무력화하는 **소비 측 의미**와 그 단일 적용 지점 확정, `FE-RB-004` mitigation "telemetry runtime flag disable" 의 실행 가능성 보장 (§5.4, §16.4). key 선언·schema 검증 자체는 `FE-OC-004` 소유.
|
||
- **Trace correlation (telemetry 관점)** — W3C `traceparent` 가 backend contract 상 허용될 때만 전파, 미지원 시 local operation ID 로 degrade, retry 는 같은 logical operation correlation 유지하되 attempt 구분 (§11.3, §8.1).
|
||
- **기여 edge** — `FE-OC-008` 실패→telemetry rule column, `FE-OC-021` duration/attempt bucket 제공, `FE-OC-025` `FE-RB-004` recovery assertion.
|
||
|
||
### 제외 범위
|
||
|
||
> 의도적으로 제외 — 다른 owner 브랜치/외부 계약이 소유. 본 브랜치는 telemetry 관점의 consume/기여만 한다.
|
||
|
||
- **Error kind 정규화 taxonomy 자체** — `FE-OC-008` owner (frontend-error-classification-boundary branch). 본 브랜치는 `error_kind` 를 소비만 하고 정의하지 않음.
|
||
- **Render error boundary 소유·복구** — `FE-OC-015` owner (frontend-render-recovery-boundary branch). 본 브랜치는 boundary-catch 신호를 consume 해 `ui.render.failed` 를 emit 만 함.
|
||
- **Web Vitals 측정·NFR 리포트** — `FE-OC-021` owner ([[raw/branch-notes/feature-web-vitals-performance-budget-contract]]). 본 브랜치는 low-cardinality attribute bucket 만 공급.
|
||
- **`FE-RB-004` runbook 1차 소유** — `FE-OC-025` owner (frontend-operational-runbook branch). 본 브랜치는 technical escalation 이며 diagnosis evidence field 만 공급.
|
||
- **Telemetry endpoint 의 CSP `connect-src`·bundle 내 secret 금지 강제** — `FE-OC-019` owner (frontend-browser-security-boundary branch).
|
||
- **Runtime config 로딩·검증** — `FE-OC-004` owner (frontend-env-runtime-config branch). 본 브랜치는 endpoint 값을 consume 만 함(의존, §엣지·실패·의존).
|
||
- **Token lifecycle** — 외부 Keycloak / [[raw/branch-notes/feature-frontend-auth-session-integration-contract]] (`FE-OC-010`). telemetry 는 token 을 절대 전송하지 않음.
|
||
|
||
## 근거 (필수, 최소 1개+)
|
||
|
||
| Source | 정당화하는 결정 |
|
||
|---|---|
|
||
| [[raw/project-notes/ca-skeleton-frontend-operational-contract]] | 본 브랜치의 거의 모든 결정의 SSOT — FE-D021(§3.2), FE-OC-014(§2.1), telemetry 계약(§11), `FE-REG-TELEMETRY`(§5.8/§5.1), 실패 matrix telemetry column(§8.2), `FE-RB-004`(§16.4), `TELEMETRY_ENABLED` runtime key(§5.4) + boot sequence(§6.3)/config validation(§6.4)/composition root(§4.5). D1~D8 전부 이 hub 의 project decision 을 인용한다. |
|
||
| [[raw/official-docs/react-ui-library-official]] | `REACT-UI-C1` — presentation 이 React component 로 구성됨(버튼~페이지). `ui.render.failed` event 의 emit point 가 React component boundary catch 라는 D4 event catalog 항목을 간접 뒷받침. boundary 자체의 소유는 render-recovery branch(`FE-OC-015`)에 위임. |
|
||
|
||
> 참고: §11.3 trace correlation 이 언급하는 W3C `traceparent` (Trace Context) 는 실제 표준이나 `raw/official-docs/` 에 아직 아카이브되지 않았다. 따라서 사실로 인용하지 않고 hub §11.3 의 project decision(허용될 때만 전파)만 근거로 쓴다. 표준 자체를 근거로 삼아야 할 결정이 생기면 `wiki-source-summarizer` 로 먼저 아카이브한다.
|
||
|
||
## TODO
|
||
|
||
각 항목 옆 증거 등급. repository 부재 → 전부 `planned` / `needs-confirmation`.
|
||
|
||
- [ ] `FE-REG-TELEMETRY` registry (`src/contracts/telemetry.js`): hub §5.8 의 7-field 스키마와 초기 5 event 를 코드로 구현 + emit 지점 배선 — 등급: `planned`
|
||
- [ ] Redaction 강제 (allowlist projection) + forbidden-attribute scan test — 등급: `planned`
|
||
- [ ] Bounded queue + overflow drop 정책 + queue drain/memory test — 등급: `planned`
|
||
- [ ] Sink failure / degradation test (endpoint invalid → telemetry 만 degrade, app 계속) — 등급: `planned`
|
||
- [ ] Trace correlation 전파 + retry attempt 구분 test — 등급: `planned`
|
||
- [ ] `TelemetryPort` (application) + telemetry adapter + composition-root wiring — 등급: `planned`
|
||
- [ ] `TELEMETRY_ENABLED=false` → no-op port 주입 + zero-network/zero-queue 회귀 test (`FE-RB-004` mitigation 재현) — 등급: `planned`
|
||
- [ ] 구현 repository 및 검증 evidence 식별 — 등급: `needs-confirmation`
|
||
|
||
## 진행 중 메모
|
||
|
||
- `/branch-spec` 로 hub §2/§3/§5/§8/§11/§16 을 내려 D1~D7 을 확정. 모든 grounding 은 hub project decision(FE-D021 중심) — 외부 official-doc 은 react-ui(간접)만 관여. web research 0건(hub 가 충분).
|
||
- 2026-07-20 loop-back fill: coverage 감사에서 `TELEMETRY_ENABLED` kill-switch **소비 측** 메커니즘이 미결정(MISSING_CONCERN)으로 드러나 D8 + 구현 가이드 7 을 추가했다. hub §5.4 는 key 를 선언하고 §16.4 는 그 disable 을 mitigation lever 로 *요구* 하지만 소비 형태는 미명시 — 사용자 소유 브랜치가 없어 본 브랜치가 소비 owner 다(`FE-OC-004` 는 key 선언·schema 검증만 소유). 같은 pass 에서 `telemetry.delivery.dropped` 의 전달 채널(비재귀 구체화)과 `route_id`/`operation_id` producer 의존을 명시했다.
|
||
- 운영 격리(operational isolation)가 이 브랜치의 축: telemetry 는 관찰 목적이며 절대 UX 를 볼모로 잡지 않는다. 그래서 delivery guarantee 를 주장하지 않고 best-effort 로 못 박는다.
|
||
|
||
## 결정 사항
|
||
|
||
> 아래 Decision Evidence Map 의 prose mirror. 각 결정의 근거는 hub project decision.
|
||
|
||
- 2026-07-18: **Telemetry = best-effort, non-blocking** — render/API critical path 를 차단하지 않고 sink failure 가 UI 를 실패시키지 않는다. 대안(delivery-guaranteed audit channel)은 regulated audit event 가 필요할 때만 별도 계약으로 분리. 근거: [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.2.
|
||
- 2026-07-18: **Redaction 우선** — low-cardinality allowlist context 만 전송, token/PII/raw payload 는 forbidden. 근거: hub §11.1, §5.8.
|
||
- 2026-07-18: **Bounded queue + 비재귀** — overflow drop 정책을 registry 에 명시, telemetry 실패를 동일 sink 로 재귀 전송하지 않음. 근거: hub §11.2, §16.4.
|
||
- 2026-07-18: **`FE-REG-TELEMETRY` single SSOT** — 자유 문자열 event 금지, 초기 5 event 고정. 근거: hub §5.8, §5.1, FE-D018.
|
||
- 2026-07-18: **Trace correlation 은 조건부 전파** — backend contract 가 허용할 때만 traceparent 전파, 아니면 local operation ID 로 degrade. 근거: hub §11.3, §8.1.
|
||
- 2026-07-18: **Delivery transport 는 adapter-owned·degradable** — dev console sink, prod endpoint invalid 시 telemetry 만 degrade. 근거: hub §11.2, §4.2, §16.4.
|
||
- 2026-07-18: **실패→telemetry 매핑은 bounded·safe** (`FE-OC-008` 기여) — terminal 실패당 safe field 만으로 최대 1 event, abort 는 error event 미발생, `TELEMETRY_FAILURE` 재귀 금지. 근거: hub §8.2, §8.1.
|
||
- 2026-07-20: **`TELEMETRY_ENABLED` 는 composition-root 단일 지점의 kill-switch** — flag 가 `false`(hub 기본값)면 real adapter 를 **아예 구성하지 않고** no-op `TelemetryPort` 를 주입한다. queue·redaction·sink·counter 가 전혀 생성되지 않으므로 disable 은 "전송 억제"가 아니라 "경로 부재"다. flag 는 boot-time runtime config 이므로 in-session flip 은 없고, 다음 boot 에 반영된다. 근거: hub §5.4(`TELEMETRY_ENABLED` runtime·required·default `false`), §16.4 Mitigation("telemetry runtime flag disable"), §6.3 boot sequence, §4.5 composition root.
|
||
|
||
## 결정-근거 매핑
|
||
|
||
> 모든 Supporting Claim 은 hub project decision. `[[...operational-contract]]` (project link) 옆의 `FE-D###`·`§n` 은 consistency hook 상 project 링크로 안전하게 검증된다.
|
||
|
||
| Decision ID | Decision | 선택 조건 (언제 이 결정 / 언제 대안) | Supporting Claims | Evidence Strength | Open Risk |
|
||
|---|---|---|---|---|---|
|
||
| D1 | Telemetry 는 best-effort — render·API critical path 를 block 하지 않고 sink failure 가 UI 를 실패시키지 않는다 (`FE-OC-014`) | product telemetry 는 best-effort default 유지. regulated audit event 처럼 delivery guarantee 가 필요하면 best-effort 와 분리된 **별도 audit channel 계약** 신설 (FE-D021 revisit trigger) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.2 | `project-decision` | repository 부재 — telemetry throw 가 render/API success 를 깨지 않음을 증명하는 non-blocking test 필요 |
|
||
| D2 | Redaction — low-cardinality allowlist context 만 전송, forbidden attribute(token·email·raw URL/query/body·storage value·stack) 전송 금지 | allowlist 가 invariant(accepted-documented-only). 신규 attribute 는 registry 추가 전 low-cardinality + non-PII 검토 통과 시에만 허용; 실패하면 forbidden 분류 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.1 §5.8 | `project-decision` | redaction 은 caller 가 아니라 transport boundary(adapter)에서 강제해야 함 — forbidden-attribute scan test 로 leakage 0 증명 필요 |
|
||
| D3 | Bounded queue + overflow drop 정책 registry 명시 + telemetry 실패 비재귀 보고 | queue 는 항상 bounded. drop 방향(oldest vs newest)은 event class 별 registry 선언값 — 미선언 시 기본 oldest-drop (§구현 가이드 4 의 `UNSUPPORTED_IMPL_DECISION`) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.2 §16.4 | `project-decision` | queue 상한 크기 미확정 — memory/queue drain test 로 bound 내 drain 증명 필요 |
|
||
| D4 | `FE-REG-TELEMETRY` 가 event/attribute/redaction 의 single SSOT; 자유 문자열 event 금지; 초기 5 event(`app.boot.failed`·`api.request.failed`·`ui.render.failed`·`release.mismatch.detected`·`telemetry.delivery.dropped`) 고정 | registry-owned 유지. code generation SSOT 채택이 FE-D018 revisit trigger. `ui.render.failed` trigger 는 React boundary catch (`REACT-UI-C1` 이 presentation=React 구성을 뒷받침) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018 §5.8 §5.1; [[raw/official-docs/react-ui-library-official]] REACT-UI-C1 | `project-decision` | registry schema validation test(자유 문자열 event reject) 필요; event 별 required attribute 가 실제 발생 지점에서 수집 가능한지 미검증 |
|
||
| D5 | Trace correlation — W3C `traceparent` 는 backend contract 허용 시에만 전파, requestId/traceId 는 safe internal reference 로 보관, raw trace header user 미노출, 미지원 backend 는 local operation ID 로 degrade, retry 는 같은 logical operation correlation 유지하되 attempt 구분 | backend contract 가 traceparent 지원 → 전파; 미지원 → local operation ID 로 degrade | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.3 §8.1 | `conditional-default` | 전파는 backend contract 의존(외부); W3C Trace Context 표준 미아카이브 → 표준 세부는 사실 인용 불가; retry 간 correlation(같은 op, distinct attempt) test 필요 |
|
||
| D6 | Delivery transport 는 adapter-owned·degradable — local/dev console-safe sink, prod endpoint 부재/invalid 면 telemetry 만 degrade 하고 app 계속, page-hide `sendBeacon` 은 adapter decision 이며 delivery guarantee 아님 | local/dev → console sink; prod → endpoint sink; page-hide `sendBeacon` 은 optional(no guarantee) | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.2 §4.2 §16.4 | `project-decision` | endpoint invalid boot path 가 telemetry 만 degrade(app 계속)함을 증명하는 sink-failure test 필요 |
|
||
| D7 | 실패→telemetry 매핑은 bounded·safe (`FE-OC-008` 기여) — §8.2 각 terminal normalized failure 는 safe field(status group·attempt bucket·route ID)만으로 최대 1 event, abort 는 error event 미발생, `TELEMETRY_FAILURE` 는 재귀 금지 | normalized error taxonomy 는 error-classification branch(`FE-OC-008`) 소유 — 본 브랜치는 그 kind 를 consume 해 telemetry rule column 만 구현. taxonomy 가 바뀌면 매핑 갱신 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §8.2 §8.1 | `project-decision` | `error_kind` registry 소유는 error-classification boundary branch(`FE-OC-008`) — 그 registry 미확정 시 매핑 draft 상태 |
|
||
| D8 | `TELEMETRY_ENABLED` kill-switch 는 composition root 단일 지점에서 소비 — `false`(hub default)면 real adapter 미구성 + no-op `TelemetryPort` 주입(queue·redaction·sink·counter 모두 미생성), `true` 면 D6 delivery ladder 진입. flag 는 boot-time 값이므로 in-session flip 없음(다음 boot 반영), 따라서 flip 시 stranded queue 문제가 정의상 발생하지 않음. `FE-RB-004` mitigation "telemetry runtime flag disable" 은 이 경로로 실행된다 | flag `false` → no-op(관측 0, 부작용 0); `true` → 정상 경로. call-site 조건 분기(`if (telemetry)`)나 port null 주입은 채택하지 않음 — hub §4.2 상 presentation/use-case 는 `TelemetryPort` 만 참조하므로 disable 이 call site 로 새면 안 됨 | [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §5.4 §16.4 §6.3 §4.5 §11.2 | `project-decision` | no-op vs 미구성의 *구현 형태* 는 hub 미명시(구현 가이드 7 의 `UNSUPPORTED_IMPL_DECISION`); flag off 상태에서도 product e2e 가 동일해야 함을 증명하는 both-state test 필요 |
|
||
|
||
## 구현 가이드
|
||
|
||
> `planned` blueprint (프론트엔드 코드 부재). 경로는 hub §4.6 Planned directory blueprint + §5.1 registry owner map 에서 도출된 `planned` anchor 이며 repository 생성 시 변경될 수 있다. 3-rule (R1 Trace 필수 / R2 UNSUPPORTED_IMPL_DECISION / R3 no OUT_OF_BRANCH_SCOPE) 준수.
|
||
|
||
### 1. TelemetryPort + adapter + composition-root wiring
|
||
|
||
> **Trace**: D1 + D6 · `FE-OC-014` · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §4.2 §4.6
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: port method 표면(`record(event)` 단일 vs `record`+`flush`+`shutdown`) 과 파일명은 hub 미명시 → 최소 표면(`record` only)을 초기 default 로 제안. trade-off: 최소 표면은 오용 여지가 적으나 page-hide flush 를 adapter 내부로 숨겨야 함.
|
||
|
||
| 요소 | Planned 경로 | 책임 | MUST NOT |
|
||
|---|---|---|---|
|
||
| `TelemetryPort` (application-owned interface) | `src/application/ports/` | use-case/presentation 이 부르는 telemetry 계약 정의 | 구현·browser transport·UX 결정 |
|
||
| telemetry adapter | `src/adapters/telemetry/` | queue·redaction·sink 구현, port 구현 | navigation/UX 결정 (hub §4.2) |
|
||
| composition root | `src/bootstrap/composition-root.js` | runtime config(`TELEMETRY_ENABLED` + endpoint)로 **real adapter 또는 no-op port** 를 생성·주입 (kill-switch 단일 지점 — 7 참조) | business rule, call-site 조건 분기 |
|
||
|
||
- presentation/use-case 는 `TelemetryPort` 만 참조하고 transport 를 직접 부르지 않는다 (hub §4.2 presentation MUST NOT own telemetry transport).
|
||
- adapter 는 endpoint 값을 runtime config 에서 주입받는다 (config 로딩은 env-runtime-config branch 소유 — §엣지·실패·의존).
|
||
|
||
### 2. `FE-REG-TELEMETRY` registry
|
||
|
||
> **Trace**: D4 · `FE-REG-TELEMETRY` · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D018 §5.8 §5.1
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: 없음 — 스키마 필드와 초기 event 집합은 hub §5.8 을 그대로 채택(신규 제안 아님).
|
||
|
||
Planned 경로: `src/contracts/telemetry.js` (single owner: 본 브랜치, hub §5.1).
|
||
|
||
**registry 최소 스키마(7-field)의 owner 는 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.8 이다** — "§5.8 그대로" 라고 스스로 밝혔듯 복제였으므로 걷어낸다. 요약 한 줄: event 는 `eventName`·`trigger`·`requiredAttributes`·`optionalAttributes`·`forbiddenAttributes`·`sampling`·`delivery` 를 모두 갖고, required attribute 는 low-cardinality 만 허용한다.
|
||
|
||
초기 5 event 의 **정의(trigger + required attributes)는 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §5.8 소유**다. 본 절은 그 event 를 *어디서 emit 하는가* 만 정한다 — 정의를 옮겨 적으면 hub 가 attribute 를 바꿀 때 이 표가 조용히 낡는다(실제로 `attempt_count` → `attempt_count_bucket` rename 을 놓쳤었다).
|
||
|
||
| Event | 본 브랜치의 emit 지점 |
|
||
|---|---|
|
||
| `app.boot.failed` | boot config/release validation 실패 경로 |
|
||
| `api.request.failed` | API client 의 terminal normalized failure 반환 직전 |
|
||
| `ui.render.failed` | render recovery boundary 의 catch 핸들러 |
|
||
| `release.mismatch.detected` | release check 가 mismatch 를 확정한 지점 |
|
||
| `telemetry.delivery.dropped` | 본 브랜치 sink adapter 의 queue drop 경로 |
|
||
|
||
- 자유 문자열 event 전송 금지 (hub §5.1 ad hoc use failure). registry 미등록 event 는 build/test 에서 reject.
|
||
|
||
### 3. Redaction 강제
|
||
|
||
> **Trace**: D2 · `FE-OC-014` · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.1 §5.8
|
||
>
|
||
> - **해소됨(2026-07-21) — 근거 있는 결정**: redaction 메커니즘은 hub 가 정한다. [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §11.1 이 "이 목록은 **exhaustive default-deny allowlist**다 … 목록 밖 attribute 는 transport boundary 에서 제거된다" 로 메커니즘(default-deny allowlist projection)과 강제 지점(transport boundary)을 모두 명시했다. 본 브랜치가 고른 trade-off 가 아니므로 `UNSUPPORTED_IMPL_DECISION` 라벨을 뗀다.
|
||
|
||
- **허용/금지 attribute 어휘의 owner 는 [[raw/project-notes/ca-skeleton-frontend-operational-contract]] §11.1** 이며 exhaustive default-deny allowlist 다. 목록을 여기에 옮겨 적지 않는다 — 옮겨 적은 사본이 hub 보다 짧으면 본 절 §2 가 선언한 event(`release.mismatch.detected` 의 `active_release_id`·`mismatch_kind`, `telemetry.delivery.dropped` 의 `reason`·`queue_size_bucket`)가 transport boundary 에서 전부 제거되어 계약이 자기모순에 빠진다.
|
||
- 본 브랜치가 소유하는 것은 *강제 방법* 이다: redaction 은 adapter 의 transport boundary 에서 수행하고 caller 를 신뢰하지 않는다. forbidden-attribute scan test 가 emit payload 를 검사해 위반 시 실패(§검증).
|
||
|
||
### 4. Bounded queue + overflow + degradation ladder + 비재귀
|
||
|
||
> **Trace**: D3 + D6 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.2 §16.4
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: (a) queue 상한 크기, (b) 기본 drop 방향(oldest vs newest), (c) page-hide `sendBeacon` 사용 여부는 hub 미명시 → 초기 default 로 **oldest-drop + 유한 상한(초기 제안값, memory test 로 확정)** 제안, `sendBeacon` 은 adapter 내부 optional. trade-off: oldest-drop 은 최신 event 를 보존하나 boot 초기 event 를 잃을 수 있음.
|
||
|
||
| 단계 | 동작 | 근거 |
|
||
|---|---|---|
|
||
| 정상 | bounded queue 적재 → sink flush | §11.2 |
|
||
| overflow | drop 정책(registry 선언; 기본 oldest-drop) 적용 + `telemetry.delivery.dropped` self-metric 1건 | §11.2, §5.8 |
|
||
| sink non-2xx/network 실패 | product flow 계속, console-safe fallback(safe field 한정), 동일 sink 재귀 보고 금지 | §16.4 Containment |
|
||
| adapter init 실패 | telemetry 만 degrade, app 계속 | §11.2 |
|
||
| prod endpoint 부재/invalid | telemetry 만 degrade, app 계속 | §11.2 |
|
||
|
||
- telemetry failure 를 telemetry 로 재귀 전송하지 않는다 (hub §11.2). `telemetry.delivery.dropped` 는 self-metric 이며 sink 실패의 원인 event 를 다시 sink 로 보내지 않는다.
|
||
|
||
**`telemetry.delivery.dropped` 자체의 전달 채널** (비재귀 불변식의 구체화):
|
||
|
||
> **UNSUPPORTED_IMPL_DECISION**: hub §5.8 은 `telemetry.delivery.dropped` 의 event *shape* 만 정의하고 그 event 자신이 *어느 채널로* 나가는지는 명시하지 않는다. hub §16.4 Diagnosis evidence 가 요구하는 산출물이 event stream 이 아니라 **"dropped event count"·"queue size bucket"** 이라는 점에 근거해, 아래 counter-우선 채널을 초기 default 로 제안한다. trade-off: counter 는 drop 폭주 시에도 자기 증폭이 없고 §16.4 evidence 형태와 1:1 이지만, 개별 drop 의 시점 분포(timeline)를 잃는다.
|
||
|
||
- self-metric 은 **동일 bounded queue 에 재적재(re-enqueue)하지 않는다** — full/dead queue 로 되돌리는 것은 정의상 순환이며 overflow 를 가속한다.
|
||
- 대신 adapter 내부의 **in-process 단조 counter**(key = `reason` × `queue_size_bucket`, hub §5.8 required attribute 와 동형)로 집계하고, hub §16.4 Containment 의 console-safe fallback(safe field 한정)으로 즉시 관측 가능하게 한다.
|
||
- 이 counter 는 `FE-RB-004` diagnosis evidence 의 `dropped event count` 로 그대로 공급된다(§6 기여 edge).
|
||
- sink 가 회복되어 **정상 flush 가 성공한 이후**에 한해, 누적 counter 를 aggregated event 1건으로 승격 전송하는 것은 adapter 의 optional 결정이다 — 실패 중인 sink 로는 시도하지 않으며 delivery guarantee 로 표현하지 않는다 (hub §11.2).
|
||
- counter 자체는 sink 실패로 소실되지 않아야 하므로 queue 와 독립된 lifetime 을 가진다(document lifetime 한정, 영속화 없음 — 영속화는 storage registry owner 영역).
|
||
|
||
### 5. Trace correlation
|
||
|
||
> **Trace**: D5 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §11.3 §8.1
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: local operation ID 포맷(예: `operationId#attempt`)은 hub 미명시 → 사용자 선택. trade-off: 짧은 포맷은 로그 가독성↑ 이나 충돌 회피를 위해 request-scoped uniqueness 보장 로직 필요.
|
||
|
||
- W3C `traceparent` 는 외부 auth/backend contract 가 허용할 때만 전파 (hub §11.3).
|
||
- backend 응답의 `requestId`/`traceId` (envelope `meta`, §7.3)는 safe support reference 로 내부 state 보관 가능, user 에 raw 노출 금지.
|
||
- normalized failure shape(§8.1)의 `requestId`/`traceId` 는 optional — 존재 시 telemetry attribute 로 승격하지 않고 내부 correlation 에만 사용.
|
||
- retry(new request)는 같은 logical operation correlation 유지하되 `attempt` 로 구분 (hub §11.3, §7.2 `attempt`).
|
||
- trace propagation 미지원 backend 는 local operation ID 로 degrade.
|
||
|
||
### 6. 기여 edge (contribution, ownership 은 위임)
|
||
|
||
> **Trace**: D7 · `FE-OC-008` / `FE-OC-021` / `FE-OC-025` 기여 · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §8.2 §16.4
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: 없음 — 아래는 hub 계약 인용이며 각 owner 브랜치에 위임(R3). 본 절은 telemetry 기여 edge 만 명세.
|
||
|
||
| 기여 대상 | 본 브랜치가 제공하는 telemetry edge | Owner (위임) |
|
||
|---|---|---|
|
||
| `FE-OC-008` 실패 taxonomy | §8.2 Telemetry rule column 구현 — terminal 실패당 safe field 만으로 최대 1 event, abort 는 error event 미발생, raw URL/body 금지 | error-classification boundary branch |
|
||
| `FE-OC-021` NFR | `duration_bucket`·`attempt_count_bucket` 등 low-cardinality attribute 공급(측정·리포트는 미소유) | web-vitals-performance-budget branch |
|
||
| `FE-OC-025` runbook | `FE-RB-004` diagnosis evidence field(endpoint classification·queue size bucket·dropped count·build/release ID·redaction test result) + recovery assertion 공급, **및 Mitigation "telemetry runtime flag disable" 의 실행 경로(D8, 구현 가이드 7) 보장** | frontend-operational-runbook branch |
|
||
|
||
### 7. `TELEMETRY_ENABLED` kill-switch 소비
|
||
|
||
> **Trace**: D8 (+ D1 non-blocking / D6 degradation ladder) · `FE-OC-014` · [[raw/project-notes/ca-skeleton-frontend-operational-contract]] FE-D021 §5.4 §16.4 §6.3 §4.5 §11.2
|
||
>
|
||
> - **UNSUPPORTED_IMPL_DECISION**: hub §5.4 는 `TELEMETRY_ENABLED` 를 required runtime key(default `false`)로 *선언*하고 §16.4 는 그 disable 을 *mitigation lever* 로 *요구*하지만, 소비 형태(real adapter 미구성 + **no-op port 주입** vs port 자체를 optional/null 로 두고 call site 에서 분기)는 명시하지 않는다 → **no-op port 주입** 을 초기 default 로 제안. trade-off: no-op 은 disable 경로를 composition root 한 곳에 가두고 call site 를 flag-무지 상태로 유지하나(hub §4.2 의 "presentation 은 `TelemetryPort` 만 참조" 와 정합), no-op 객체가 항상 존재하므로 "telemetry 가 꺼져 있다"는 사실이 호출자에게 보이지 않는다(관측은 boot-time config snapshot 으로만 확인 가능).
|
||
|
||
**flag 상태별 구성 (composition root 분기 지점 1곳)**
|
||
|
||
| `TELEMETRY_ENABLED` | composition root 동작 | 생성되는 것 | 생성되지 않는 것 | 근거 |
|
||
|---|---|---|---|---|
|
||
| `false` (hub default, §5.4) | no-op `TelemetryPort` 주입 | port 표면(`record`)만 | queue · redaction projection · sink/transport · dropped counter · `TELEMETRY_ENDPOINT` 해석 | §5.4, §16.4 Mitigation |
|
||
| `true` | real telemetry adapter 구성 후 주입 | 구현 가이드 2~5 전체 경로 | — | §11.2, §4.5 |
|
||
|
||
- **no-op 의 계약**: `record(event)` 는 인자를 읽지 않고 즉시 반환하며 throw 하지 않는다(D1 non-blocking 불변식을 flag 양쪽 상태에서 동일하게 유지). 어떤 event 도 buffer 하지 않으므로 나중에 flag 가 켜져도 소급 전송되는 event 는 없다.
|
||
- **disable 은 "전송 억제"가 아니라 "경로 부재"**: queue 도 counter 도 생성되지 않으므로 §11.1 redaction 위반 표면과 §11.2 overflow 표면이 동시에 0 이 된다. `TELEMETRY_ENDPOINT` 는 hub §5.4 상 `Required: conditional` — 그 조건이 곧 `TELEMETRY_ENABLED=true` 라는 것이 본 브랜치의 소비 측 해석이며, schema 상 conditional 강제는 `FE-OC-004` 소유(§엣지·실패·의존).
|
||
- **runtime flip 가능성**: runtime config 는 hub §6.3 boot sequence 에서 `GET /config.json` (no-store) 로 **boot 시 1회** 로드된 뒤 §4.5 composition root 가 의존성을 구성한다. hub 에 config hot-reload 계약이 없으므로 **in-session flip 은 존재하지 않는다** — flag 변경은 provider 측에서 반영한 뒤 **다음 document load(boot)** 부터 적용된다.
|
||
- **flip 시 이미 queue 에 쌓인 event**: 위 결과로 정의상 문제가 발생하지 않는다. `true`→`false` 는 이전 session 의 queue 를 flush 하지 않고 document 와 함께 폐기하며(§11.2 best-effort — delivery guarantee 없음이므로 손실이 계약 위반이 아님), `false`→`true` 는 시작 시점부터의 event 만 다룬다(no-op 이 아무것도 보관하지 않았으므로 backfill 대상 없음).
|
||
- **`FE-RB-004` mitigation 충족 경로**: §16.4 Mitigation 의 "telemetry runtime flag disable" 은 ① provider 의 runtime config 에서 `TELEMETRY_ENABLED=false` 설정 → ② 이후 boot 부터 no-op 주입 → ③ sink 호출·queue 적재·drop counter 증가가 **발생 원천에서** 중단 → ④ §16.4 Containment("product flow 계속")와 Recovery assertion("product e2e unaffected")이 flag 양쪽 상태에서 동일하게 성립, 의 순서로 실행된다. 이 lever 는 sink restore 없이도 즉시 사용 가능한 격리 수단이다.
|
||
- **invalid value**: hub §5.4 failure column 은 `TELEMETRY_ENABLED` invalid 를 **boot fail** 로 규정하고 §6.4 는 "boolean parsing without truthy string ambiguity" 를 요구한다. 따라서 composition root 는 **검증된 boolean** 만 받으며 `"false"` 같은 문자열을 스스로 해석하지 않는다(파싱·거부는 `FE-OC-004`). endpoint 부재/invalid 의 **telemetry degrade**(§5.4)와 달리 flag invalid 는 degrade 가 아니라 boot fail 이라는 비대칭에 유의.
|
||
|
||
## 엣지·실패·의존
|
||
|
||
> R4 캡처. sibling 브랜치 링크는 소유 계약 `FE-OC-###` 로만 참조(consistency hook 안전).
|
||
|
||
- **실패·엣지 경로**:
|
||
- sink non-2xx/network 실패 → `TELEMETRY_FAILURE`(hub §8.2), product error 없음, console-safe/drop, 재귀 금지.
|
||
- queue overflow → 정책(기본 oldest-drop) 적용 + `telemetry.delivery.dropped` self-metric, unbounded 적재 금지 (§16.4).
|
||
- telemetry adapter init 실패 / prod endpoint 부재·invalid → telemetry 만 degrade, app 계속 (§11.2).
|
||
- redaction miss(forbidden attribute 유출) → forbidden-attribute scan test 가 build 를 실패시켜야 함 (§검증).
|
||
- page hide → `sendBeacon` best-effort, delivery guarantee 로 표현 금지 (§11.2).
|
||
- backend 가 `traceparent` 미지원 → local operation ID 로 degrade (§11.3).
|
||
- `TELEMETRY_ENABLED=false` (hub §5.4 기본값) → real adapter 미구성, no-op port 주입, network·queue·counter 전부 부재. app 은 정상 동작하며 `FE-RB-004` mitigation lever 로 사용 (구현 가이드 7).
|
||
- `TELEMETRY_ENABLED` invalid → **boot fail** (§5.4, degrade 아님). 파싱·거부는 `FE-OC-004` 소유이며 telemetry adapter 는 검증된 boolean 만 수신.
|
||
- flag `true`→`false` 전환 → 이전 session queue 는 flush 되지 않고 폐기 (§11.2 best-effort, delivery guarantee 없음). in-session flip 은 §6.3 boot-time config 로딩상 존재하지 않으며 다음 boot 부터 반영.
|
||
- navigation/user abort(`REQUEST_ABORTED`, §8.2) → error telemetry event 미발생(interaction-only).
|
||
- **다른 계약 의존**:
|
||
- [[raw/branch-notes/feature-frontend-env-runtime-config-contract]] (`FE-OC-004`) — `TELEMETRY_ENABLED`(boolean, required, default `false`) 와 `TELEMETRY_ENDPOINT`(conditional) 를 runtime config 로 consume. 그 registry 는 key 선언·분류·schema 검증만 소유하고 **전송·redaction·kill-switch 소비 메커니즘은 본 브랜치 소유**(D8, 구현 가이드 7). config 계약이 바뀌면 flag 해석·endpoint 해석·boot degrade 경로 영향. (hub §20 Dependency 가 본 브랜치의 유일 명시 dependency 로 이 브랜치를 지목.)
|
||
- [[raw/branch-notes/feature-routing-navigation-guard-contract]] (`FE-OC-005`) — `route_id` 의 **producer**. `FE-REG-ROUTE` 가 low-cardinality route ID 를 발급하며, telemetry 는 `ui.render.failed`·`api.request.failed` 의 required attribute 로 그 값을 그대로 소비한다(직접 생성·정규화 금지). route ID 어휘가 바뀌면 event attribute cardinality 가 영향받음.
|
||
- [[raw/branch-notes/feature-api-client-response-envelope-contract]] (`FE-OC-006`) — `operation_id`(+`route_id`·`attempt`)의 **producer**. hub §7.2 request context 가 logical request 마다 `operationId`/`routeId`/`attempt` 를 보유하므로, telemetry emit point 는 이 request context 에서 값을 읽고 `attempt` → `attempt_count_bucket` 만 파생한다. request context 필드가 바뀌면 emit point 수집 경로 영향.
|
||
- [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] (`FE-OC-008`) — `error_kind` 를 consume 해 `api.request.failed` 등 event 의 required attribute 채움. taxonomy 변경 시 매핑 갱신.
|
||
- [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]] (`FE-OC-015`) — React boundary-catch 신호를 consume 해 `ui.render.failed` emit. boundary 소유 계약 변경 시 emit point 영향.
|
||
- [[raw/branch-notes/feature-frontend-release-cache-rollback-contract]] (`FE-OC-016`) — `build_id`·`active_release_id`·release token 을 consume 해 `app.boot.failed`·`release.mismatch.detected` attribute 채움.
|
||
- [[raw/branch-notes/feature-frontend-browser-security-boundary-contract]] (`FE-OC-019`) — telemetry endpoint 의 CSP `connect-src`·bundle 내 secret 금지를 강제(그 브랜치가 본 브랜치를 contributor 로 지목).
|
||
- [[raw/branch-notes/feature-frontend-operational-runbook-contract]] (`FE-OC-025`) — `FE-RB-004` recovery assertion(product e2e unaffected·delivery self-check·queue drains·forbidden-attribute scan pass) 소비.
|
||
- [[raw/branch-notes/feature-frontend-contract-registry-governance]] (`FE-OC-022`) — `FE-REG-TELEMETRY` 를 8-registry governance 의 single-owner/compatibility check 로 감사.
|
||
|
||
## 검증해야 할 주장
|
||
|
||
> hub 계약은 근거지만 내 프로젝트 코드의 동작을 자동 보장하지 않는다. repository 생성 후 검증. 모두 `needs-confirmation`.
|
||
|
||
| Claim | Why uncertain | How to verify | Status |
|
||
|---|---|---|---|
|
||
| telemetry throw/sink 실패가 render·API critical path 를 깨지 않는다 (D1) | 코드 부재; non-blocking 은 wiring 방식에 의존 | throwing sink 주입 후 render + API success 유지 assert 하는 **sink failure test** (§20 Measurable completion) | `needs-confirmation` |
|
||
| forbidden attribute 가 client 를 절대 떠나지 않는다 (D2) | redaction 이 transport boundary 에서 강제되는지 코드로 확인 필요 | emit payload 를 검사하는 **forbidden-attribute scan / redaction test**; 위반 시 build 실패 | `needs-confirmation` |
|
||
| bounded queue 가 planned bound 내 drain 하고 정책대로 drop 한다 (D3) | queue 상한·drop 방향이 UNSUPPORTED_IMPL_DECISION | **memory/queue drain test** (`FE-RB-004` recovery assertion) | `needs-confirmation` |
|
||
| 자유 문자열/미등록 event 가 reject 된다 (D4) | registry enforcement 미구현 | `FE-REG-TELEMETRY` **schema validation test** | `needs-confirmation` |
|
||
| retry 간 같은 logical operation correlation 유지 + attempt 구분 (D5) | traceparent 전파는 backend contract 의존 | local operation ID + attempt 구분 **correlation test** (MSW 로 backend 유/무 traceparent 시나리오) | `needs-confirmation` |
|
||
| prod endpoint 부재/invalid 시 telemetry 만 degrade 하고 app 계속 (D6) | boot 경로에서 degrade 격리 미검증 | invalid endpoint **boot/sink-failure matrix test** | `needs-confirmation` |
|
||
| `TELEMETRY_ENABLED=false` 에서 network 요청·queue·counter 가 전혀 생성되지 않고 product e2e 가 flag `true` 와 동일하다 (D8) | no-op 주입이 composition root 한 곳에만 있는지, call site 로 새지 않는지 코드로 확인 필요 | flag off/on **both-state test** — off 상태에서 telemetry 관련 network 호출 0건 assert + `FE-RB-004` recovery assertion("product e2e unaffected") 양쪽 상태 재실행 | `needs-confirmation` |
|
||
| `telemetry.delivery.dropped` self-metric 이 실패한 queue/sink 로 재진입하지 않는다 (D3 + 구현 가이드 4) | counter 채널이 queue 와 독립 lifetime 인지 미검증 | overflow 유발 후 **비재귀 test** — queue 재적재 0건 assert + dropped counter 가 `FE-RB-004` diagnosis evidence 로 노출되는지 확인 | `needs-confirmation` |
|
||
|
||
## 관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)
|
||
|
||
| 관심사 | 상태 | owner | 심각도 | 근거 |
|
||
|---|---|---|---|---|
|
||
|
||
## 마주친 문제
|
||
|
||
- 없음 — scaffolding/spec 단계 (구현 전).
|
||
|
||
## 묶음 (이 branch에서 파생된 자료)
|
||
|
||
<!-- GENERATED: project-contract-imports:start -->
|
||
## 가져온 프로젝트 계약
|
||
|
||
| Ref | Owner | 요약 | Branch 적용 |
|
||
|---|---|---|---|
|
||
| `FE-OC-008@1` | [[raw/branch-notes/feature-frontend-error-classification-boundary-contract]] | 모든 failure는 stable frontend error kind로 MUST 정규화하고 raw body·stack을 UI에 노출하면 안 됨 | import 참조로 적용 |
|
||
| `FE-OC-015@1` | [[raw/branch-notes/feature-frontend-render-recovery-boundary-contract]] | expected operational error와 render defect를 MUST 분리하고 reload loop를 금지 | import 참조로 적용 |
|
||
| `FE-OC-021@1` | [[raw/branch-notes/feature-web-vitals-performance-budget-contract]] | NFR은 device/network/cache/build context와 함께 MUST 측정 | import 참조로 적용 |
|
||
| `FE-OC-025@1` | [[raw/branch-notes/feature-frontend-operational-runbook-contract]] | boot, chunk mismatch, API degradation, telemetry failure, rollback runbook을 MUST 유지 | import 참조로 적용 |
|
||
<!-- GENERATED: project-contract-imports:end -->
|
||
|
||
### Sub-branches (세부 작업)
|
||
|
||
- 없음 — scaffolding 단계
|
||
|
||
### 오류 기록 (이 branch 작업 중 발생)
|
||
|
||
- 없음 — scaffolding 단계
|
||
|
||
### 면접 준비 (이 작업에서 나올 수 있는 면접 질문)
|
||
|
||
- 없음 — scaffolding 단계
|
||
|
||
### 강의 (이 작업을 위해 학습한 강의)
|
||
|
||
- 없음 — scaffolding 단계
|
||
|
||
### job-posting tie-ins (이 작업에서 파생된 글감)
|
||
|
||
- 없음 — scaffolding 단계
|
||
|
||
## 관련 일일 노트
|
||
|
||
- 없음 — scaffolding 단계
|
||
|
||
## 완료 후 정리
|
||
|
||
- PR 링크: TODO
|
||
- 리뷰 메모: TODO
|
||
- 머지 결과 / 배포 환경: TODO
|
||
- **wiki 추출 대상**: 없음 — 전 항목 `planned` (repository 부재, 추출 조건 미충족)
|
||
- **추출하지 않을 항목**: D1~D8 전부 — `planned` 등급이므로 verified 승급 및 wiki/projects 추출 전까지 제외
|