/* * 유입 진행 단계 — **모든 커넥터가 같은 낱말로 말한다.** * * ── 왜 표준화하나 (2026-08-21) ────────────────────────────────────────────── * 마스터 읽기는 원본에 여러 질의를 보내는 긴 작업이고, 그 사이 화면은 아무것도 알 수 없었다. 커넥터마다 * 자기 낱말로 말하게 두면 화면이 커넥터 수만큼 문구를 알아야 하고, 새 커넥터가 생기면 화면에 낯선 글자가 * 나온다. * * 그래서 낱말을 고정한다. 그리고 그 낱말은 **트윈의 자리 이름**이다 — 커넥터가 지금 채우는 자리가 곧 * 지금 하는 일이다(`design/integration/connector-checklist.md` 의 마스터 축과 같은 이름). * * 그러면 셋이 함께 얻어진다. * · 화면은 **단계마다 하나의 번역**만 두면 모든 커넥터를 보여 준다. * · 사람은 원본이 달라도 **같은 말로** 진행을 읽는다. * · 실패했을 때 「어느 자리를 읽다 실패했다」가 곧 「트윈의 무엇이 비었다」가 된다. * * ── 퍼센트를 만들려면 분모가 있어야 한다 ──────────────────────────────────── * 어댑터가 **자기가 보낼 단계를 미리 선언**한다(`ReferenceAdapter.masterSteps`). 그것이 분모다. 선언하지 * 않으면 화면은 퍼센트를 보이지 않고 단계 이름만 보인다 — **모르는 진행률을 지어내지 않는다.** */ /** * 표준 단계. 순서는 대개 이 차례이지만 **강제하지 않는다** — 원본에 따라 없는 자리가 있고, 그때는 그 * 단계를 선언하지도 보내지도 않는다. */ export const REFERENCE_STEPS = [ /** 원본 스키마 확인 — 무엇을 물을 수 있는지 먼저 본다(버전이 다를 수 있다). */ 'schema', /** 공간·좌표 — 자리가 놓일 좌표계. */ 'space', /** 자리 — 물건이 놓이고 작업이 일어나는 곳. */ 'locations', /** 설비 — 일하는 것. */ 'equipment', /** 공정 — 무슨 일을 하는가. */ 'operations', /** 자재 — 무엇이 흐르는가. */ 'materials', /** 경로 — 공정의 순서. */ 'routes', /** 레시피 — 투입과 산출. */ 'recipes', /** 시험 명세 — 무엇을 어떻게 검사하는가. */ 'testSpecifications', /** 인원. */ 'persons', /** 물리자산. */ 'assets', /** 트윈 생성 — 여기부터는 호스트가 하는 일이다(어댑터가 선언하지 않는다). */ 'provision', /** 기동. */ 'start' ] as const export type ReferenceStep = (typeof REFERENCE_STEPS)[number] /** 호스트가 마스터 읽기 뒤에 하는 단계 — 어댑터의 선언에 이만큼을 더해 분모를 만든다. */ export const HOST_STEPS: ReferenceStep[] = ['provision', 'start'] /** * 한 단계의 진행 — 구독으로 흐르는 메시지의 몸통. * * `detail` 은 **그 단계 안에서 사람이 알아야 하는 것**이다(읽은 건수 등). 비어 있어도 된다 — 지어내지 * 않는다. */ export interface ReferenceProgress { /** 어느 사이트를 만드는 중인가. */ siteId: string step: ReferenceStep /** 이 사이트에서 끝낸 단계 수. */ done: number /** 이 사이트의 전체 단계 수 — 어댑터가 선언하지 않으면 없다(그때 퍼센트를 만들지 않는다). */ total?: number /** 여러 사이트를 한 번에 만들 때, 지금 몇 번째인가. */ siteIndex: number siteCount: number /** 그 단계 안의 사실 — 「자리 128건」처럼. */ detail?: string /** 이 단계에서 실패했는가. 실패해도 화면이 그 자리를 그대로 보여 줄 수 있어야 한다. */ failed?: boolean /** 실패 이유 — 실패했을 때만. */ error?: string } /** * 전체 진행률 — **분모를 알 때만 낸다.** * * 사이트가 여럿이면 사이트 사이의 진행도 함께 센다: 앞선 사이트는 끝난 것으로 본다. */ export function referencePercent(p: ReferenceProgress): number | undefined { if (!p.total || p.total <= 0 || p.siteCount <= 0) return undefined const perSite = 1 / p.siteCount const within = Math.min(p.done, p.total) / p.total return Math.round((p.siteIndex * perSite + within * perSite) * 100) }