// ─── IMP-41 u2 — application_mode helper (issue #70) ──────────────────────── // Pure deterministic helpers for forwarding backend Step 9 // `unit.application_candidates[]` to the FramePanel V4-label badge tooltip. // // Keyed by backend `application_mode` VALUE (NOT V4 label) — preserves the // AI-isolation contract: tooltip text is a read-only display of backend // authority, never re-derived on the frontend from V4 label. // // Source of truth = src/phase_z2_pipeline.py APPLICATION_MODE_BY_V4_LABEL // (:107-112) emitted via _application_candidates_for_unit() (:3071-3092) // onto unit.application_candidates[] in step09_application_plan.json. /** Backend application_mode enumeration (verbatim from APPLICATION_MODE_BY_V4_LABEL). */ export type ApplicationMode = | 'direct_insert' | 'same_frame_with_adjustment' | 'layout_or_region_change' | 'exclude'; /** Korean consequence phrases per issue #70 spec item #2. Keyed by mode VALUE. */ export const APPLICATION_MODE_TOOLTIP_KR: Record = { direct_insert: '코드 직접 적용', same_frame_with_adjustment: 'AI 보강 필요', layout_or_region_change: 'AI restructure 필요', exclude: 'render path 제외', }; /** * Compose the V4-label badge tooltip title. When `applicationMode` resolves * to a known mode the title shows the Korean consequence + raw mode token; * otherwise (undefined or unknown — legacy fixtures pre-IMP-32) it falls * back to the raw V4 label string per Stage 2 contract. */ export function buildBadgeTitle( label: string, applicationMode: string | undefined, ): string { const consequence = applicationMode ? APPLICATION_MODE_TOOLTIP_KR[applicationMode as ApplicationMode] : undefined; return consequence ? `${consequence} (${applicationMode})` : `V4 label: ${label}`; } /** * Build a Map from a Step 9 * `unit.application_candidates[]` array. Entries with a non-string or empty * `template_id` are skipped. First occurrence wins on duplicate keys. * Pure — does NOT sort, slice, or filter by label/confidence. */ export function mergeApplicationCandidates( applicationCandidates: unknown, ): Map { const out = new Map(); if (!Array.isArray(applicationCandidates)) return out; for (const ac of applicationCandidates) { const key = (ac as any)?.template_id; if (typeof key === 'string' && key.length > 0 && !out.has(key)) { out.set(key, ac); } } return out; }