@things-factory/headless-twin 10.0.13 → 10.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/engine/ingest-health.d.ts +41 -0
- package/dist-server/engine/ingest-health.js +48 -0
- package/dist-server/engine/ingest-health.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +34 -0
- package/dist-server/engine/twin-engine.js +180 -6
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +26 -0
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-assessment.d.ts +11 -1
- package/dist-server/service/reference/reference-assessment.js +22 -2
- package/dist-server/service/reference/reference-assessment.js.map +1 -1
- package/dist-server/service/reference/reference-live.js +18 -0
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +12 -2
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +24 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-model/epcis-coverage.js +27 -0
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
- package/dist-server/service/twin-model/isa95-coverage.js +85 -5
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
- package/dist-server/service/twin-model/item-ref.d.ts +3 -3
- package/dist-server/service/twin-model/item-ref.js +4 -4
- package/dist-server/service/twin-model/item-ref.js.map +1 -1
- package/package.json +3 -3
- package/server/engine/ingest-health.ts +78 -0
- package/server/engine/twin-engine.ts +199 -6
- package/server/service/reference/reference-adapter.ts +23 -0
- package/server/service/reference/reference-assessment.ts +39 -4
- package/server/service/reference/reference-live.ts +20 -0
- package/server/service/reference/reference-master.ts +12 -2
- package/server/service/twin-forecast/twin-forecast-query.ts +27 -1
- package/server/service/twin-model/epcis-coverage.ts +27 -0
- package/server/service/twin-model/isa95-coverage.ts +85 -5
- package/server/service/twin-model/item-ref.ts +4 -4
- package/test/checkpoint-refuses-empty.test.ts +103 -0
- package/test/cursor-stall-not-read-failure.test.ts +126 -0
- package/test/item-ref.test.ts +3 -3
- package/test/mirror-resumes-from-checkpoint.test.ts +192 -0
- package/test/revision-axis.test.ts +13 -2
- package/test/status-tally.test.ts +3 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-master.js","sourceRoot":"","sources":["../../../server/service/reference/reference-master.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Face2 마스터 인제스트(ADR-0018 반영 모드) — 레퍼런스 시스템(실 WMS/YMS/MES 또는 가상 레퍼런스)의\n * 마스터를 읽어 트윈 구조(TwinModelDef) + 공간(Space content)을 생성. 손배선이 아니라 인제스트로 엔티티가 흐른다.\n *\n * 규율(ADR-0018): 도메인 타입(location.type=노드용)은 커널 카탈로그 어휘를 그대로 반영(발명 금지).\n * location 은 role 로 노드(운영점)·area(공간영역)·both 를 구분 — 노드→TwinModelDef, area→Space.areas.\n * 좌표·representations 는 트윈 공간층(보완/추가). 실 시스템이 좌표를 안 주면 layout 없이 위상만 인제스트.\n */\n\nimport type { DomainSystem, EquipmentLevel, WorkCalendarEntry } from '@operato/twin-kernel'\n\nexport interface RefLocation {\n id: string\n type: string // 노드는 커널 노드타입(dock/storage/yard-slot…), area 는 공간 region 타입\n name?: string\n description?: string\n role: 'location' | 'area' | 'both'\n parentId?: string\n /**\n * **전기적으로 어디서 받는가** — 이 자리에 전기를 주는 상류 자리(커널 계약의 `upstreamId`).\n *\n * `parentId`(공간 포함)와 따로 두는 이유는 커널 계약에 적혀 있다. 여기서 빠뜨리면 마스터가 말한 계통이\n * 모델까지 오지 못하고, 뿌리 계량 판정이 분기 계량기까지 뿌리로 세어 **부하가 두 배로** 보인다.\n */\n upstreamId?: string\n /** 저장 용량 — 이 자리에 동시에 놓일 수 있는 물품 수. */\n capacity?: number\n /**\n * 동시 처리 수 — 이 자리에서 한 번에 진행될 수 있는 작업 수. **저장 용량과 다른 축**이다\n * (자재를 20개 쌓아 두는 절단 스테이션이 한 번에 한 대만 깎는다). 미지정=제약 없음.\n */\n parallelism?: number\n /**\n * **ISA-95 설비 계층 단계** — `EquipmentLevel` 열거값(`ProductionLine`·`StorageZone`·`WorkCenter`…).\n *\n * `type` 은 현장의 낱말(`paint-booth`)이고 이것은 **표준이 정한 역할**이다. 둘은 다른 축이라 함께 든다 —\n * 연동·집계는 표준 축으로, 화면은 현장 낱말로. 미지정이면 그 자리의 표준 역할을 아직 모른다는 뜻이고,\n * 짐작해 채우지 않는다.\n */\n level?: EquipmentLevel\n /**\n * 자리의 **열린 속성** — 표준 `OperationalLocationProperty`(설비·사람·자산과 같은 모양).\n *\n * 무엇이 여기 실리나: **자리의 물리량**이다. 에너지의 계약전력(`contract.kW`)이 첫 사례다 —\n * 그 값이 앉을 자리가 없어서 EMS 커널은 계약 대비 판정을 할 수 없었다(자리가 없으면 사실이\n * 들어오지 못한다). 커널은 자기가 **읽는** 속성 id 만 선언하고(`EMS_PROPERTY`) 나머지는 나른다.\n */\n properties?: RefResourceProperty[]\n footprint?: { x: number; y: number; w: number; h: number } // 있으면 area 기하 + 노드 중심좌표\n gs1Id?: string // 정식 GS1 정체성(§J) — 위치=SGLN. 결합(tag)/추적 canonical id. 받아들이기 경계서 부여(로컬 id=name/xref 로 보존).\n}\n\n/**\n * 설비 속성 — **ISA-95 `EquipmentSpecificationProperty`(`OpEquipmentSpecificationPropertyType`) 1:1.**\n * 그 타입도 `ID` + `Value`(`ValueString`·`DataType`·`UnitOfMeasure`) 이므로 공정 모수와 같은 모양이다.\n *\n * 단위 코드는 **UN/CEFACT 권고 20 공통코드**(EPCIS `uom` 과 같은 코드계): 속도는 `MTS`(metre per\n * second) 또는 `KMH`(kilometre per hour). 코드를 우리가 발명하지 않는다.\n */\n/**\n * 자원 속성 — **커널 `ResourceProperty` 와 같은 모양**이고, 그 모양은 ISA-95 가 세 자원에 똑같이\n * 정의한 것이다(`EquipmentPropertyType` · `PersonPropertyType` · `PhysicalAssetPropertyType`).\n *\n * 그래서 자원마다 다른 속성 타입을 두지 않는다 — 예전에는 설비만 속성을 가졌고(RefEquipmentProperty),\n * 인원·자산에 붙는 사실은 **실을 자리가 없어서 들어오지 못했다.**\n */\nexport interface RefResourceProperty {\n id: string\n description?: string\n value?: string\n /** 표준 `Value.DataType`. */\n dataType?: string\n /** 표준 `Value.UnitOfMeasure` — UN/CEFACT 공통코드. 물리량에 단위가 없으면 소비처가 거절한다. */\n uom?: string\n /** 하위 속성(재귀) — 표준 `<X>PropertyChild`. */\n children?: RefResourceProperty[]\n /** 이 개체 속성이 구체화하는 등급 속성 — 표준 `<X>ClassPropertyID`. */\n classPropertyId?: string\n}\n\n/** 옛 이름 — 설비 전용이던 시절의 별칭(소비처 호환). 신규 소싱은 `RefResourceProperty`. */\nexport type RefEquipmentProperty = RefResourceProperty\n\n/**\n * 자원 **등급 정의** — 커널 `ResourceClassDef` 와 같은 모양이고, 그 모양은 ISA-95 가 세 자원에 똑같이\n * 정의한 것이다(`PersonnelClassType`·`EquipmentClassType`·`PhysicalAssetClassType`).\n *\n * 왜 필요한가: 원 시스템은 등급 **체계**를 갖고 있다(\"6축 용접 자격자\" 는 \"용접 자격자\" 이고, 그것은\n * \"생산직\" 이다). 그 체계가 들어올 자리가 없으면 요구와 소속이 **문자열이 똑같을 때만** 맞아,\n * 현장에서는 되는 배정이 모델에서 막힌다.\n */\nexport interface RefResourceClass {\n id: string\n description?: string\n /** 상위 등급들 — 표준 `<X>ClassBaseID`(복수 = 다중 상속). */\n baseIds?: string[]\n properties?: RefResourceProperty[]\n testSpecificationIds?: string[]\n /** 유효 기간(ISO) — 표준 `EffectiveStartDate`/`EffectiveEndDate`. 밖이면 자격이 성립하지 않는다. */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 검사 명세 — **자원이 가리키는 대상**(ISA-95 `TestSpecification`).\n *\n * 표준 필드는 셋만 담는다(`ID`·`Description`·`Version`) — 재는 속성과 합격 기준은 아직 1차 출처와\n * 대조하지 않았다(커널 계약의 머리말이 그 사실을 적어 두었다).\n */\n/**\n * 시험 결과 — **\"이 개체가 그 시험을 통과했다\"** 는 기록(ISA-95 결과).\n *\n * 결과를 낳는 곳은 트윈이 아니다(인사·정비 시스템이 기록한다) — 그래서 관측이면서 **선언 경로로** 온다.\n * 커널은 이 값이 있을 때만 자격을 판정한다: 없으면 제약이 아니고(라인이 굶지 않는다), 있으면 만료·\n * 불합격이 배정을 막는다.\n */\nexport interface RefTestResult {\n specId: string\n result: 'pass' | 'fail'\n at?: string\n /** 없으면 무기한 — 짐작으로 기한을 만들면 멀쩡한 자격자가 조용히 사라진다. */\n expiresAt?: string\n}\n\nexport interface RefTestSpecification {\n id: string\n /** 표시명 — i18n 키일 수 있다. 읽는 어휘를 하나로 맞추려고 인제스트가 `name` 으로 옮긴다. */\n description?: string\n version?: string\n}\n\nexport interface RefEquipment {\n id: string\n kind: string\n /** 사람이 부르는 이름 — 없으면 화면이 id 를 보인다(id 를 이름으로 베끼지 않는다). */\n name?: string\n /** 시험 결과 — 자격이 성립하는지는 이것이 말한다(§RefTestResult). */\n testResults?: RefTestResult[]\n description?: string\n homeLocationId: string\n mtbfMs?: number // 평균 고장 간격(확률적 고장) — 지정 시 커널이 breakdown 시뮬 → 주목(고장) 발생\n mttrMs?: number // 평균 수리 시간\n gs1Id?: string // 정식 GS1 정체성(§J) — 설비=GIAI.\n /**\n * 교대(가동시간) — 이 시간대에만 배정된다. `startHour <= endHour` 면 같은 날, 넘어가면 야간(22→6).\n * 미지정=24시간 가용. 교대 밖 자원은 고장·계획정지와 **구별되는 이유**로 쉰다(상태에 드러난다).\n */\n window?: { startHour: number; endHour: number }\n /**\n * 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 있으면 `window` 보다 이것을 본다.\n *\n * 축을 프레임워크 `@things-factory/work-shift` 의 `WorkShift`(`fromDate`·`fromTime`·`toDate`·`toTime`)와\n * 맞춰 두었다 — 그 엔티티를 교대의 정본으로 결선할 때 직선 매핑이 되게. **아직 결선하지 않았다**:\n * 그 엔티티에는 자원 연결과 비근무(휴일)가 없어 확장이 함께 필요하고, operato-twin 의 의존성 추가는\n * 앱 아키텍처 결정이다(커버리지 §4-7 에 결선 지점으로 기록).\n */\n workCalendar?: WorkCalendarEntry[]\n /**\n * 설비 속성(속도 등) — 없으면 **이동시간을 거리에서 계산할 수 없다**(속도 없이 거리만으로는 시간이\n * 안 나온다). 그때는 커널 상수로 떨어지고 그 사실이 `specCoverage()` 에 남는다.\n */\n properties?: RefResourceProperty[]\n /** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`(교정 등). */\n testSpecificationIds?: string[]\n /**\n * 유효 기간 — **표준 `EffectiveStartDate` / `EffectiveEndDate`**(개체 타입 셋 모두에 있다).\n *\n * 없으면 **폐기한 것이 영구히 살아 있다**: 배정 대상에 남고, 가용 수에 들어가고, 화면에 뜬다.\n * 도입 예정인 것은 반대로 오늘부터 있는 것처럼 보인다. 기간 밖이면 커널이 그 시각의 모델에서\n * 빼고 **이유를 달아 남긴다**(`effectivity: 'not-yet' | 'expired'`) — 조용히 지우지 않는다.\n */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 공정 모수 — 커널 `OperationParameter`(= ISA-95 `ParameterSpecification`/`ParameterType`) 와 **같은 모양**.\n * 이름을 바꾸지 않는다: 인제스트 경계에서 어휘가 갈리면 그 자리에서 방언이 생긴다.\n * ID 어휘의 정본은 커널 `OP_PARAM`(표준이 파라미터 ID 를 정하지 않으므로 우리가 한 곳에서 정한다).\n */\nexport interface RefOperationParameter {\n id: string\n /** ISA-95 `Value.ValueString` — 표준이 문자열로 싣는다. 기간 모수는 ISO 8601(`PT8M`). */\n value: string\n /** ISA-95 `Value.UnitOfMeasure`. 무차원(비율)이면 생략. */\n uom?: string\n}\n\n/**\n * 공정의 자재 요구·산출 — **ISA-95 `OperationsSegment.MaterialSpecification`** 과 같은 모양.\n * 인원·설비·자산 명세와 같은 자리에 있어야 4대 자원이 대칭이 된다(자재만 빠져 있었다).\n */\nexport interface RefOpMaterialSpecification {\n id?: string\n /** 품목 등급으로 요구 — 표준 `MaterialClassID`. */\n materialClass?: string\n /** 특정 품목으로 요구 — 표준 `MaterialDefinitionID`. */\n materialDefinition?: string\n /** 표준 `MaterialUse` — 이 공정에서의 쓰임. */\n use: 'consumed' | 'produced' | 'consumable'\n /** 표준 `Quantity`. 단위 미지정이면 개수. */\n quantity: number\n uom?: string\n}\n\n/** 소요시간 변동 — **표준 밖 확장**(ISA-95 Duration 은 스칼라). 커널 `DurationVariability` 와 동형. */\nexport interface RefOperationVariability {\n distribution: 'constant' | 'exponential' | 'uniform' | 'triangular'\n min?: string\n max?: string\n mode?: string\n}\n\n/**\n * 오퍼레이션 명세 — ISA-95 `OperationsSegment`. 커널 `OperationDef` 와 필드 이름이 1:1이다.\n *\n * **이것이 없으면 시뮬레이션의 시간은 우리가 코드에 고정한 상수다.** 원 시스템(또는 가상 레퍼런스)이\n * 공정별 소요·변동·수율을 말해 줘야 예측이 그 현장의 예측이 된다. 주지 않으면 커널 기본값으로\n * 굴러가고, **기본값을 썼다는 사실은 커널 `specCoverage()` 가 밝힌다**(조용히 넘어가지 않는다).\n */\nexport interface RefOperation {\n /** 작업 종류 키 — 커널 `FlowTask.kind` 와 같은 값이어야 명세가 그 작업에 붙는다. */\n key: string\n label: string\n intent: 'transport' | 'process' | 'dwell'\n locationType?: string\n resourceType?: string\n bizStep?: string\n /** ISA-95 `OperationsSegment.Duration` — ISO 8601 기간(`PT12M`). */\n duration?: string\n variability?: RefOperationVariability\n parameters?: RefOperationParameter[]\n /**\n * 필요 인원 — ISA-95 `PersonnelSpecification`(`PersonnelClassID` + `Quantity`) 1:1.\n * 없으면 사람 없이 도는 공정이다(설비만 잡는다).\n */\n personnelSpecification?: { personnelClass?: string; quantity: number }[]\n /** 필요 물리 자산 — ISA-95 `PhysicalAssetSpecification`(등급 + 수량). 빈 팔레트가 없으면 못 나간다. */\n physicalAssetSpecification?: { assetClass?: string; quantity: number }[]\n /**\n * 필요·산출 자재 — ISA-95 `MaterialSpecification`. **4대 자원 중 자재만 이 자리가 없었다** —\n * 그래서 부품이 떨어져 라인이 서는 상황이 예측에 나타나지 않았다.\n * BOM 의 \"몇 개\" 는 여기다(품목 정의의 조립 구조에는 수량이 없다 — 1차 출처 확인).\n */\n materialSpecification?: RefOpMaterialSpecification[]\n}\n\n/**\n * 사람 — **ISA-95 `Person`.** 설비와 별개 자원이다(고장·설비효율이 아니라 등급·교대로 산다).\n * `personnelClass` 가 ISA-95 `PersonnelClassID` — 공정은 특정인이 아니라 **등급으로** 인원을 요구한다.\n */\nexport interface RefPerson {\n id: string\n name?: string\n /** 시험 결과 — 자격이 성립하는지는 이것이 말한다(§RefTestResult). */\n testResults?: RefTestResult[]\n /**\n * 속한 등급들 — **표준 `Person.PersonnelClassID`, `maxOccurs=\"unbounded\"`**.\n * 복수인 것이 표준이고 그것이 자격 표현이다: 한 사람이 용접 자격과 지게차 자격을 함께 갖는다.\n */\n personnelClassIds?: string[]\n /** 교대 — 설비와 같은 모양. 미지정=24시간 가용. */\n window?: { startHour: number; endHour: number }\n /** 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 2교대·휴게·휴일을 함께 표현한다. */\n workCalendar?: WorkCalendarEntry[]\n /**\n * 기본 위치 — 표준 `Person.OperationalLocation`. 없으면 \"이 라인에 몇 명 있나\" 를 물을 수 없다.\n * 사람은 움직이므로 이 값은 **출발점**이고, 실제 위치는 관측(person.status)이 갱신한다.\n */\n homeLocationId?: string\n /**\n * 자원 속성 — 표준 `PersonProperty`. **자격증·숙련도가 여기 들어간다.**\n *\n * 표준에는 자격(Qualification)이라는 별도 요소가 없다(`B2MML-Personnel.xsd` 확인). 자격은\n * ① 등급 복수 소속으로(`personnelClassIds: ['welder-certified','forklift-licensed']`) ②\n * 증빙은 이 속성으로(자격증 번호·만료일), ③ 검증 시험은 표준 `TestSpecificationID`(아직 미구현).\n */\n properties?: RefResourceProperty[]\n /** 자격을 검증한 시험 명세들 — 표준 `TestSpecificationID`(참조만; 시험 명세·결과 모델은 아직 없다). */\n testSpecificationIds?: string[]\n /** 유효 기간 — 사람에게는 **입사 전·퇴사 후**다(자격 만료는 등급 쪽 기간이다: 사람은 남고 자격만 끊긴다). */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 물리 자산(반복사용) — **ISA-95 `PhysicalAsset`, GS1 `GRAI`.**\n * 팔레트·랙·용기처럼 **나갔다 돌아오는** 것. 물류단위(SSCC)와 다르다: SSCC 는 그 팔레트에 실린 화물\n * 한 덩어리이고, GRAI 는 팔레트 자체다(오늘은 이 화물, 내일은 저 화물을 싣는다).\n */\nexport interface RefAsset {\n id: string\n name?: string\n /** 시험 결과 — 자격이 성립하는지는 이것이 말한다(§RefTestResult). */\n testResults?: RefTestResult[]\n /**\n * 속한 등급들 — 표준 `PhysicalAssetClassID`(복수). 팔레트가 'euro-pallet' 이면서 'food-grade' 일 수 있다.\n * 공정의 요구는 등급 하나 + 수량이다(표준 `PhysicalAssetSpecification`).\n */\n assetClassIds?: string[]\n /** 기본 위치. */\n homeLocationId?: string\n /** 정식 GS1 정체성 — 반복사용 자산=GRAI. */\n gs1Id?: string\n /** 자원 속성 — 표준 `PhysicalAssetProperty`(적재 한도·규격 등). */\n properties?: RefResourceProperty[]\n /** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`. */\n testSpecificationIds?: string[]\n /** 유효 기간 — 자산에서는 **폐기한 팔레트가 풀에서 빠지는 것**이다(회수 대상 수가 부풀지 않게). */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 품목 정의 — **ISA-95 `MaterialDefinition`.**\n *\n * 없으면 로트만 관측으로 들어오고 **품목이 무엇인지는 아무 데도 없다.** 그래서 같은 로트를 100개로\n * 받아도 \"몇 kg 인가\" 에 답할 수 없다(커널 `quantityIn` 이 환산을 거부하는 근거).\n *\n * **표준에 단위 환산 요소는 없다**(1차 출처 전수 확인) — 속성 주머니뿐이다. 그래서 환산 계수는\n * 커널이 정한 속성 이름(`perBaseUnit`, `uom` 이 대상 단위)으로 `properties` 에 싣는다.\n */\nexport interface RefMaterialDefinition {\n /** 표준 `ID` — GTIN(`urn:epc:idpat:sgtin:…`)을 쓴다(정체성의 정본이 EPCIS 다). */\n id: string\n name?: string\n /** 속한 등급들 — 표준 `MaterialClassID`(복수). */\n materialClassIds?: string[]\n /** 품목 속성 — 표준 `MaterialDefinitionProperty`. 환산 계수가 여기 들어간다. */\n properties?: RefResourceProperty[]\n testSpecificationIds?: string[]\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * **이 마스터를 다시 만들 수 있는 방법** — 트윈이 자기 원본을 기억하는 자리.\n *\n * 트윈은 원본을 비출 뿐이므로(ADR-0028), 원본이 달라지면 **다시 읽어야** 합니다. 그런데 지금까지\n * 트윈은 \"내가 무엇에서 나왔나\" 를 남기지 않았다 — 어댑터로 들어온 트윈은 `TwinReference` 행이\n * 있었지만 템플릿으로 세운 트윈에는 아무것도 없었고, 그래서 **다시 읽을 대상이 없는 트윈**이\n * 만들어졌다. 원본을 잃은 트윈은 첫날의 모습에 영원히 머문다.\n *\n * 그래서 원천이 무엇이든 여기 한 자리에 남긴다. 다시 읽기는 이 값 하나만 보면 된다.\n */\nexport type MasterOrigin =\n /**\n * 등록된 레퍼런스(어댑터)에서 왔다 — 다시 읽기 = 그 어댑터로 마스터를 다시 가져온다.\n *\n * `source` 는 **레퍼런스 등록 이름**이고 그 참조가 보고하는 사이트 전부에 같다. 그래서 어느 사이트에서\n * 온 트윈인지는 `siteId` 가 있어야 안다 — 없으면 다시 읽기가 사이트를 찾지 못한다(2026-08-18 실측:\n * 사이트가 여러 개인 참조에서 온 트윈은 재동기가 전부 거절됐다. 참조 이름을 사이트별 마스터의\n * `source`(=인스턴스 id)와 견주고 있었다 — 서로 다른 것을 비교한 것이다).\n */\n | { kind: 'reference'; source: string; siteId?: string; spaceId?: string }\n /**\n * 템플릿 + 노브로 합성했다 — 다시 읽기 = 같은 노브로 다시 짓는다(합성은 결정적이다).\n *\n * `spaceId` 는 **만들 때 고른 현장**이다(합쳐 넣기). 기억하지 않으면 다시 읽을 때 트윈이 조용히 자기\n * 현장을 떠난다 — 템플릿이 낸 기본 공간으로 돌아가기 때문이다. 그러면 그 현장의 값(요금 등)도 함께\n * 잃는다(2026-08-18 실측: 한 현장에 합쳐 둔 EMS 트윈 여덟이 재동기 뒤 각자 공간으로 흩어졌다).\n */\n | { kind: 'template'; templateId: string; params: Record<string, unknown>; spaceId?: string }\n\nexport interface ReferenceMaster {\n source: string\n /**\n * 어느 종류의 트윈인가 — **커널 카탈로그가 정본**이다(`DomainSystem`).\n *\n * 예전에는 `'wms' | 'yms' | 'mes'` 를 여기 적어 뒀다. 커널에 종류가 하나 늘 때(에너지) 이 줄이\n * 함께 움직여야 하는데, 손으로 적힌 목록은 그것을 알려 주지 않는다 — 어휘의 집은 한 곳이어야 한다.\n */\n system: DomainSystem\n siteName: string\n /** 검사 명세 — 자원·등급의 `testSpecificationIds` 가 가리키는 대상. */\n testSpecifications?: RefTestSpecification[]\n /**\n * 이 마스터가 어디서 나왔나 — **다시 읽기의 유일한 근거.** 없으면 그 트윈은 다시 읽을 수 없고,\n * 조회 화면이 그 사실을 그대로 말한다(없는 것을 있는 척하지 않는다).\n */\n origin?: MasterOrigin\n /* spaceId 명시 시 그 공간을 참조(여러 트윈이 한 공간 공유, N:1). 없으면 source 에서 파생(1:1). */\n /**\n * 현장(공간). **시각 기준(`timezone`)이 여기 있는 이유**: 한 테넌트가 Rosarito(태평양)와 한국\n * 공장을 함께 가질 수 있다. 테넌트 단위(`Domain.timezone`)로 두면 **둘 중 하나는 반드시 틀린다.**\n * 시각 기준은 **물리 현장의 성질**이므로 공간이 그것을 갖는다(공간 중심 원칙).\n *\n * `timezone` 은 IANA 이름(`'America/Tijuana'`) — 오프셋 숫자가 아니다. 숫자로 두면 일광절약시간을\n * 표현할 수 없고, 나중에 절대 구간으로 옮길 때도 이름이 있어야 계산할 수 있다.\n */\n space: { spaceId?: string; width: number; depth: number; unit?: string; timezone?: string; geo?: { lat: number; lon: number; rotation?: number }; representations?: any[]; primaryId?: string }\n locations: RefLocation[]\n equipment?: RefEquipment[]\n /** 공정 명세(소요·변동·수율) — 없으면 커널 기본값으로 굴러간다(그 사실은 specCoverage 가 보고). */\n operations?: RefOperation[]\n /**\n * **생산 정의**(레시피·라우트·자재 바인딩) — 커널의 정의-구동 MES 모드 입구.\n *\n * 없으면 커널은 레거시(하드코딩) 흐름으로 동작한다. 즉 **현장 레시피를 적어도 이 자리에 실리지 않으면\n * 커널은 그것을 보지 못한다** — 선언과 실행이 어긋나는 지점이라 자리를 연다.\n */\n /**\n * 생산 선언 — `TwinModelDef.productionSpec` 으로 통과한다(ISA-95 OperationsSegment + BOM).\n *\n * 예전 이름은 `mesSpec` 이었다. 담긴 것은 MES 만의 것이 아니라 \"자재를 소비해 자재를 산출하는\n * 공정\" 이고, 이름이 MES 였기 때문에 창고 트윈은 이 자리를 쓸 생각조차 하지 못했다(유통가공 창고가\n * BOM 을 설명 문서에만 적어 둔 채 키트를 못 만드는 트윈을 만든 원인). 근거는 커널 계약 주석에.\n */\n productionSpec?: unknown\n /**\n * **이 원본이 식별자를 만드는 이름공간** — `TwinModelDef.identity` 로 통과한다.\n *\n * **선언이 없으면 커널이 거절한다** (0.7.45~). 예전에는 상수 프리픽스로 팔레트·트레일러·문서를\n * 만들었는데(`0614141` — GS1 이 규격 예제로 쓰다 버린 값이고 `06…` 은 실 배정 대역이다), 그 값은\n * 저널에 영원히 남으면서 지어낸 것이라고 말해 주는 데가 없었다. 그래서 커널이 만들지 않는다.\n *\n * 원본이 자기 도메인을 말하면 그 아래에서 만든다 — 그것이 표준 정합이다(CBV 2.0 §8.2.4·§8.5.5,\n * 「그 인터넷 도메인 소유자가 배정한다」). GS1 키를 쓰려면 `documentTypes` + `companyPrefix` 이고,\n * 그때 **자리 수가 맞아야 한다**(SGTIN 은 프리픽스 + 품목참조 = 13자리, GS1 TDS).\n *\n * 정체성은 생산과 무관하므로 `productionSpec` 이 아니라 **모델**이 갖는다 — 창고·야드는 아무것도\n * 생산하지 않으면서 팔레트·트레일러를 식별한다.\n */\n identity?: unknown\n /**\n * 사람 — 선언하지 않으면 **인원 제약이 없는 트윈**이 된다(설비만 있으면 언제나 가동되는 공장).\n * 공정이 인원을 요구하는데 사람이 없으면 그 작업은 시작되지 않는다 — 그것이 사실이므로 그대로 둔다.\n */\n persons?: RefPerson[]\n /**\n * 물리 자산(반복사용) — 선언하지 않으면 자산 제약이 없는 트윈이다.\n * 공정이 자산을 요구하는데 없으면 그 작업은 시작되지 않는다 — 그것이 사실이므로 그대로 둔다.\n */\n assets?: RefAsset[]\n /**\n * 등급 정의 — 표준 `PersonnelClass`·`EquipmentClass`·`PhysicalAssetClass`.\n * 안 실어도 트윈은 동작한다(소속 문자열 그대로 판정). 실으면 상속·유효기간이 살아난다.\n */\n personnelClasses?: RefResourceClass[]\n equipmentClasses?: RefResourceClass[]\n assetClasses?: RefResourceClass[]\n /** 품목 정의·등급 — 표준 `MaterialDefinition` / `MaterialClass`. 없으면 단위 환산을 할 수 없다. */\n materialDefinitions?: RefMaterialDefinition[]\n materialClasses?: RefResourceClass[]\n /**\n * 커넥터가 **옮기는 동안 알게 된 것.** 외부 시스템의 값을 우리 어휘로 못 옮겨 기본값을 넣었다면\n * 여기에 담아 보낸다 — 그러면 `masterToTwin` 의 경고와 한 줄기로 합쳐져 화면까지 간다.\n *\n * 이 자리가 없던 동안 커넥터는 할 말이 있어도 **말할 곳이 없었다.** 그래서 도크를 랙으로 바꿔\n * 놓고도 조용히 성공했다. 옮기는 사람만 아는 사실은 옮기는 자리에서 실어야 한다.\n */\n warnings?: IngestWarning[]\n /**\n * **원본이 가진 수** — 옮긴 수의 분모다.\n *\n * 총평은 도착한 모델만 볼 수 있어서 「레시피 1,261」까지만 말할 수 있었다. 그런데 사람이 묻는 것은\n * 「그게 전부인가」다 — 원본에 1,408개가 있었고 147개를 옮기지 못했다면 그 사실이 총평의 첫 줄에\n * 있어야 한다. 실제로 사용자가 품목 2,789 와 자재 1,789 의 차이를 화면에서 읽어 내지 못해 직접\n * 물었다(2026-08-22).\n *\n * 분모를 아는 것은 **커넥터뿐이다.** 그래서 커넥터가 실어 보낸다. 보내지 않으면 총평은 분모 없이\n * 옮긴 수만 말한다 — **지어내지 않는다.**\n *\n * 열쇠는 총평이 세는 이름과 같다(`recipes`·`materials`·`totalInputs`·…). 값은 **원본을 끝까지 읽어서\n * 센 것**이어야 한다. 잘린 읽기의 수를 여기 담으면 「전부 옮겼다」는 거짓이 된다.\n */\n originCounts?: Record<string, number>\n}\n\nexport interface IngestedTwin {\n model: any // TwinModelDef(+layout+spaceId) — 커널 소비\n spaceContent: any // TwinSpace content — 뷰 소비\n warnings: IngestWarning[]\n}\n\n/**\n * 마스터 → 트윈 구조 매핑(순수). 노드타입을 카탈로그로 검증(모르는 타입은 warning, 버리진 않음 — 반영 우선).\n * @param catalog DOMAIN_CATALOG (커널 SSOT). 없으면 검증 생략.\n */\n/**\n * IANA 시간대 이름 → **그 시각의** UTC 오프셋(분). 의존성 없이 `Intl` 로 푼다.\n *\n * `Intl.DateTimeFormat` 의 `longOffset` 은 `\"GMT-07:00\"` 처럼 나오고, 이것은 **일광절약시간을 반영한**\n * 값이다. 그래서 8월의 `America/Tijuana` 는 −420(PDT), 1월은 −480(PST)이 된다.\n *\n * **한계를 밝힌다**: 여기서 나오는 것은 **한 시점의** 오프셋이다. 커널은 고정 오프셋으로 하루 중 분을\n * 읽으므로, 모델을 만든 계절과 다른 계절을 길게 예측하면 한 시간 어긋난다. 표준에 충실한 끝 모습은\n * 호스트가 절대 구간(`StartDateTime`/`FinishDateTime`)을 계산해 넣는 것이고, 그때 이 함수는 사라진다.\n *\n * 모르는 이름에는 **0 을 답하지 않는다**(`undefined`) — 0 은 \"UTC 라고 선언했다\" 는 뜻이고, 모르는 것을\n * 그렇게 바꾸면 판정이 조용히 틀린다.\n */\nexport function utcOffsetOf(timezone?: string, at: Date = new Date()): number | undefined {\n if (!timezone) return undefined\n try {\n const parts = new Intl.DateTimeFormat('en-US', { timeZone: timezone, timeZoneName: 'longOffset' }).formatToParts(at)\n const name = parts.find(p => p.type === 'timeZoneName')?.value ?? ''\n if (name === 'GMT') return 0 // UTC 계열은 오프셋 표기가 없다\n const m = /^GMT([+-])(\\d{1,2})(?::(\\d{2}))?$/.exec(name)\n if (!m) return undefined\n const sign = m[1] === '-' ? -1 : 1\n return sign * (Number(m[2]) * 60 + Number(m[3] ?? 0))\n } catch {\n return undefined // 알 수 없는 이름 — Intl 이 오류를 낸다\n }\n}\n\n\n/* ── 인제스트 경고 ──────────────────────────────────────────────────────────\n * 인제스트는 구조에 문제가 있어도 트윈을 만들어 준다(막지 않는 것이 옳다 — 부분적으로라도 세우고\n * 고쳐 나가는 것이 이 시스템의 방식이다). 그래서 **경고가 유일한 신호**인데, 그 경고를 화면이 하나도\n * 보여주지 않고 있었다: 유통가공 창고 템플릿은 커널이 모르는 자리 타입(`vas-station`)으로 트윈을\n * 만들면서 경고를 냈지만, 위자드는 그것을 버렸고 사용자는 아무 말 없이 막히는 트윈을 받았다.\n *\n * 문장이 아니라 코드로 실어 보낸다. 예전 경고는 프로즈였고 그중 하나에는 한글이 섞여 있었다\n * (`— 카탈로그에 없음`) — 다섯 언어 화면에 그대로 내보내면 이번에 걷어낸 문제를 다시 만든다.\n * 이 프로젝트가 이미 쓰는 규약과 같은 모양이다: 주의 신호(kind+params)·커맨드 거절(errorCode+params)·\n * 만만함 판정(code+영어 canonical message).\n *\n * code 언어 중립 — 화면이 `twin.ingest.<code>` 로 사람 말을 만든다\n * params 문장에 끼울 값 — 번역이 언어마다 어순을 바꿀 수 있게\n * message 영어 canonical — 로그에 남고, 번역이 없을 때 그대로 보인다(빈칸보다 낫다)\n *\n * **여기 사는 이유**: 이 파일이 경고의 생산자이고, 테스트가 소스에서 직접 읽는 모듈이라\n * 런타임 상대 import 를 가질 수 없다(이 저장소의 규약 — 그런 모듈은 자기완결이다).\n * ────────────────────────────────────────────────────────────────────────── */\n/** 인제스트가 알리는 한 가지. */\nexport interface IngestWarning {\n /** 언어 중립 코드. 화면은 `twin.ingest.<code>` 로 렌더한다. */\n code: string\n /** 문장에 끼울 값. 번역이 어순을 바꿀 수 있게 이름으로 넘긴다. */\n params?: Record<string, string | number>\n /** 영어 canonical 문장 — 로그·폴백용. 번역이 없어도 읽을 수 있어야 한다. */\n message: string\n}\n\n/**\n * **시각을 모르는 채 시각으로 판정하려 한다** — 발전 형상은 하루의 시각을 읽는데 이 트윈의 공간이\n * 시간대를 말하지 않았다.\n *\n * ── 왜 경고인가 (2026-08-18) ────────────────────────────────────────────────\n * 커널은 시간대를 모르면 UTC 로 읽는다(계약에 밝혀 둔 규율 — 모르는 값을 지어내지 않는다). 그런데 발전\n * 형상은 **시각별 비율**이라, 시간대가 없으면 현장 정오가 아니라 UTC 정오에 피크가 선다(KST 현장이면\n * 밤 9시). 값이 그럴듯하게 나오므로 아무도 알아채지 못한다.\n *\n * 그래서 시간대를 **지어내지 않고**(서버 로컬이나 임의 기본값을 넣으면 그 트윈의 모든 시각 판정이 조용히\n * 그 가정 위에 선다) 대신 **그 조합을 말한다** — 공간에 시간대를 선언하면 사라지는 경고다.\n */\nexport const generationWithoutTimeZone = (spaceId: string, equipmentId: string): IngestWarning => ({\n code: 'generation-without-time-zone',\n params: { spaceId, equipmentId },\n message:\n `\"${equipmentId}\" declares a daily generation shape but space \"${spaceId}\" declares no time zone — the hourly fractions will be read in UTC, ` +\n 'so the generation peak will not land at local noon. Declare the space time zone (we do not guess one).'\n})\n\n/**\n * 아무도 선언하지 않은 자리 타입 — 그 자리는 흐름에 참여하지 못할 수 있다.\n *\n * **묻는 상대를 고쳤다 (2026-08-20).** 예전에는 내장 카탈로그에게만 물었다. 그런데 원본이 자기\n * `DomainDefinition.locationTypes` 를 함께 보내면 **그 선언이 정본**이다 — 카탈로그는 우리가 아는\n * 도메인들의 기본 어휘일 뿐이다. 물을 상대가 틀렸으니, 자기 어휘를 선언한 원본은 정상적인 타입마다\n * 경고를 받았다(F&B 라인을 붙이면 자리마다 하나씩). 소음이 되면 사람은 경고를 읽지 않게 되고, 그때\n * 진짜 경고도 함께 죽는다.\n */\nexport const unknownLocationType = (system: string, type: string, id: string): IngestWarning => ({\n code: 'unknown-location-type',\n params: { system, type, id },\n message:\n `\"${type}\" is declared nowhere for ${system} (${id}) — neither in the master's own locationTypes nor in the built-in ` +\n `catalog, so the kernel may not include this location in the flow`\n})\n\n/**\n * 원본이 함께 보낸 자리 타입 선언 — 있으면 **이것이 정본**이다.\n *\n * 커널 계약의 `DomainDefinition.locationTypes` 는 `{ key, label, … }` 목록이다. 읽을 수 없는 모양이면\n * `undefined` 를 돌려 **카탈로그로 떨어진다** — 빈 배열을 돌려주면 「아무 타입도 선언되지 않았다」가\n * 되어 모든 자리가 경고를 받는다(없는 것과 못 읽은 것은 다르다).\n */\nexport function declaredLocationTypes(productionSpec: unknown): string[] | undefined {\n const list = (productionSpec as any)?.definition?.locationTypes\n if (!Array.isArray(list) || !list.length) return undefined\n const keys = list.map((t: any) => (typeof t === 'string' ? t : t?.key)).filter((k: any) => typeof k === 'string' && k)\n return keys.length ? keys : undefined\n}\n\n/** 시간대 문자열을 못 읽었다 — 시각 해석이 UTC 로 떨어진다(조용히 현지 시각으로 가정하지 않는다). */\nexport const unknownTimezone = (timezone: string): IngestWarning => ({\n code: 'unknown-timezone',\n params: { timezone },\n message: `space timezone \"${timezone}\" is not a known IANA zone — the twin will read times as UTC`\n})\n\n/** 작업 정의에 소요시간·파라미터가 없다 — 커널 기본값으로 동작한다. */\nexport const operationWithoutDuration = (operation: string): IngestWarning => ({\n code: 'operation-without-duration',\n params: { operation },\n message: `operation \"${operation}\" has no duration or parameters — kernel defaults will be used`\n})\n\n/** 볼 수 있는 표현이 없다 — 현장이 화면에 뜨지 않는다. */\nexport const noViewableRepresentation = (): IngestWarning => ({\n code: 'no-viewable-representation',\n message: 'the space has no viewable representation (no representations and no geo) — add one, or provide geo/board'\n})\n\n/**\n * 구조 투영에서 **가리키는 대상을 못 찾았다.**\n *\n * 예전에는 이런 참조가 model JSON 안의 맨 문자열이라 화면만 경고할 수 있었고, 다른 경로로 들어온\n * 데이터는 아무도 안 봤다. 이제 행으로 풀면서 **인제스트가 직접 안다** — 그러니 인제스트가 말한다.\n * 막지는 않는다(트윈은 model 로 이미 동작한다). 다만 조용히 넘기지 않는다.\n */\nexport const unresolvedReference = (field: string, ref: string): IngestWarning => ({\n code: 'unresolved-reference',\n params: { field, ref },\n message: `${field} points at something that is not in this twin (${ref}) — the link was left empty`\n})\n\n/**\n * 구조 투영 자체가 실패했다 — 조회는 못 하지만 **트윈은 동작한다**(커널은 model 를 이미 받았다).\n * 행은 원본에서 다시 그릴 수 있으므로 인제스트를 실패로 만들지 않되, 침묵하지도 않는다.\n */\nexport const projectionFailed = (reason: string): IngestWarning => ({\n code: 'projection-failed',\n params: { reason },\n message: `structure projection failed (${reason}) — the twin runs, but model inspection will be stale until the next ingest`\n})\n\n/**\n * **동작 중에 공장을 전환했다** — 미러는 멈추지 않으므로, 무엇이 사라졌는지 여기서 말한다.\n *\n * 멈췄다 세우는 구조 전환이라면 사용자가 \"다시 세웠다\" 는 것을 안다. 미러는 그 마디가 없어서\n * 자리 하나·설비 한 대가 조용히 없어질 수 있다 — 자리가 수천인 현장에서 화면의 수를 세어\n * 눈치채기를 기대할 수는 없다. 늘어난 것도 함께 말한다(무엇이 반영되었나가 곧 인제스트의 답이다).\n */\nexport const structureAdopted = (rev: number, shift: { locationsAdded: number; locationsDropped: number; equipmentDropped: number; personsDropped: number; assetsDropped: number }): IngestWarning => ({\n code: 'structure-adopted',\n params: { rev, ...shift },\n message:\n `the running mirror adopted a new structure (revision ${rev}) — ` +\n `+${shift.locationsAdded} location(s), -${shift.locationsDropped} location(s), ` +\n `-${shift.equipmentDropped} equipment, -${shift.personsDropped} person(s), -${shift.assetsDropped} asset(s)`\n})\n\n/**\n * 커넥터가 외부 시스템의 값을 **우리 어휘로 옮기지 못해 기본값을 넣었다.**\n *\n * 예: Oracle WMS 가 우리 표에 없는 `locn_type` 을 주면 그 자리는 `storage` 가 된다 — 도크가 랙으로\n * 모델링되는 것이다. 인제스트를 통째로 실패시킬 일은 아니지만(로케이션 하나 때문에 현장 전체가\n * 안 들어오는 편이 더 나쁘다), **조용히 넘어가면 안 된다.** 트윈이 실물과 다르게 생겼는데\n * 아무도 그 사실을 모른 채 그 위에서 흐름을 논하게 된다.\n *\n * 값을 나열하는 이유: \"3건\" 만으로는 커넥터 매핑에 무엇을 더해야 하는지 알 수 없다.\n */\nexport const connectorMappingFallback = (field: string, fallback: string, values: readonly string[], count: number): IngestWarning => ({\n code: 'connector-mapping-fallback',\n params: { field, fallback, values: values.join(', '), count },\n message: `${count} ${field} value(s) had no mapping and fell back to \"${fallback}\" (${values.join(', ')}) — the twin's shape differs from the source system here`\n})\n\n\n/** 로그 한 줄 — 개발자가 읽는 것이므로 영어 canonical 문장을 쓴다. */\nexport const describeWarnings = (warnings: readonly IngestWarning[]): string =>\n warnings.map(w => `${w.code}: ${w.message}`).join(' | ')\n\n/**\n * 서버가 기본값으로 채운 노브들 — 사용자가 준 적 없는 값으로 트윈이 만들어졌음을 알린다.\n *\n * 조용히 채우면 나중에 \"왜 이 수치인가\" 를 아무도 답할 수 없다. 막을 일은 아니다(기본값은 템플릿이\n * 확신을 갖고 선언한 값이다) — 다만 밝힌다.\n */\nexport const knobDefaultsFilled = (keys: readonly string[]): IngestWarning => ({\n code: 'knob-defaults-filled',\n params: { keys: keys.join(', '), count: keys.length },\n message: `values you did not provide were filled from the template defaults: ${keys.join(', ')}`\n})\n\n/**\n * 이 현장의 설비를 노브 값으로 채웠다 — **조용히 채우지 않는다.**\n *\n * 에너지 트윈의 부하를 이웃 설비 id 로 선언할 때 쓴다. 몇 개를 가져왔는지 말해 주지 않으면, 사용자는\n * 자기가 고르지 않은 설비들이 부하로 잡힌 것을 나중에 화면에서야 알게 된다.\n */\nexport const siteEquipmentAdopted = (key: string, count: number): IngestWarning => ({\n code: 'site-equipment-adopted',\n params: { key, count },\n message: `${count} piece(s) of equipment already on this site were adopted as loads (knob \"${key}\") — set their coefficients before trusting the numbers`\n})\n\nexport function masterToTwin(master: ReferenceMaster, catalog?: any): IngestedTwin {\n /* 커넥터가 옮기며 알게 된 것을 먼저 싣는다 — 원인이 앞에 오는 편이 읽기 쉽다(대체가 먼저, 그 결과가 뒤). */\n const warnings: IngestWarning[] = [...(master.warnings ?? [])]\n /*\n * 원본이 자기 어휘를 선언했으면 **그것에 대조한다.** 내장 카탈로그는 그 선언이 없을 때의 기본이다 —\n * 상대를 바꾸지 않으면, 자기 어휘를 선언한 원본이 정상적인 자리마다 경고를 받는다.\n */\n const known: string[] = declaredLocationTypes(master.productionSpec) ?? catalog?.[master.system]?.locationTypes ?? []\n\n const locationEntries = master.locations.filter(l => l.role === 'location' || l.role === 'both')\n const areaLocs = master.locations.filter(l => l.role === 'area' || l.role === 'both')\n\n const locations = locationEntries.map(l => {\n if (known.length && !known.includes(l.type)) warnings.push(unknownLocationType(master.system, l.type, l.id))\n // gs1Id(정식 정체성)를 모델의 자리로 통과 — 결합(tag)/추적이 GS1 로(§J). 로컬 id 는 그대로 유지(name/xref).\n /*\n * **이름을 통과시킨다.** 여기서 버리고 있었다 — 그래서 모델에도, 투영한 행에도 이름이 없고\n * 화면은 자리 id 를 그대로 찍었다(`cut-station`). 목록·표·관계 그림에서 같은 결함이 따로\n * 보였던 이유가 이 한 줄이다. 이름 없는 원본은 이름 없이 통과한다(id 로 메우지 않는다).\n */\n /*\n * **속성도 통과시킨다** — 설비와 같은 규율(손대지 않고 나른다). 여기가 없으면 자리에 실은 값이\n * 커널에 닿지 않는다: 에너지의 계약전력(`contract.kW`)이 그 경로이고, 통과하지 않으면 커널이\n * 계약을 모르는 채로 서서 **판정을 아예 하지 않는다**(오류 없이 조용히).\n */\n return { id: l.id, type: l.type, ...(l.name ? { name: l.name } : {}), ...(l.description ? { description: l.description } : {}), capacity: l.capacity ?? 0, ...(l.parallelism !== undefined ? { parallelism: l.parallelism } : {}), ...(l.level ? { level: l.level } : {}), ...(l.properties?.length ? { properties: l.properties } : {}), parentId: l.parentId, ...(l.upstreamId ? { upstreamId: l.upstreamId } : {}), gs1Id: l.gs1Id }\n })\n\n const equipment = (master.equipment ?? []).map(e => ({\n id: e.id, kind: e.kind, ...(e.name ? { name: e.name } : {}), ...(e.description ? { description: e.description } : {}), ...(e.testResults?.length ? { testResults: e.testResults } : {}),\n homeLocation: e.homeLocationId, mtbfMs: e.mtbfMs, mttrMs: e.mttrMs, gs1Id: e.gs1Id,\n ...(e.window ? { window: e.window } : {}),\n /* 속성은 손대지 않고 통과 — 이동시간 추정기가 kind 별 속도를 여기서 읽는다(단위 코드 그대로). */\n ...(e.properties?.length ? { properties: e.properties } : {}), ...(e.workCalendar?.length ? { workCalendar: e.workCalendar } : {}), ...(e.testSpecificationIds?.length ? { testSpecificationIds: e.testSpecificationIds } : {}),\n /* 유효 기간도 손대지 않고 통과 — 판정은 커널이 자기 시계로 한다(호스트가 미리 자르지 않는다). */\n ...(e.effectiveStart ? { effectiveStart: e.effectiveStart } : {}), ...(e.effectiveEnd ? { effectiveEnd: e.effectiveEnd } : {})\n }))\n\n // 좌표(layout) — footprint 중심. 없으면 생략(coordinate-free 유지).\n const layout: Record<string, { x: number; y: number }> = {}\n for (const l of locationEntries) if (l.footprint) layout[l.id] = { x: l.footprint.x + l.footprint.w / 2, y: l.footprint.y + l.footprint.h / 2 }\n\n const spaceId = master.space.spaceId ?? `${master.source}-site`\n /*\n * 시각 기준을 커널이 읽을 형태로 푼다 — 커널은 zero-dep 이라 시간대 데이터베이스를 들이지 않고\n * **분 오프셋**만 받는다(계약 `TwinModelDef.utcOffsetMinutes`). 푸는 일은 호스트의 몫이다.\n *\n * 선언이 없으면 **필드를 만들지 않는다** — 그러면 커널이 UTC 로 읽고, 그 기본값은 계약에 밝혀져 있다.\n * 여기서 0 을 넣으면 \"UTC 라고 선언했다\" 와 \"선언하지 않았다\" 가 구별되지 않는다.\n */\n const utcOffsetMinutes = utcOffsetOf(master.space.timezone)\n if (master.space.timezone && utcOffsetMinutes === undefined) {\n warnings.push(unknownTimezone(String(master.space.timezone)))\n }\n /*\n * 오퍼레이션 명세를 model 에 통과시킨다 — 커널이 `loadOperations` 로 소비한다(TwinModelDef 확장 필드,\n * 모르는 커널은 무시). 여기서 값을 손대지 않는다: 기간은 표준 표기(ISO 8601)로 커널까지 그대로 간다.\n */\n const operations = master.operations\n if (operations?.length) {\n for (const o of operations) {\n if (!o.duration && !o.parameters?.length) warnings.push(operationWithoutDuration(o.key))\n }\n }\n /* 사람도 손대지 않고 통과 — 등급·교대 그대로 커널까지 간다(경계에서 어휘를 갈지 않는다). */\n const persons = master.persons?.map(p => ({ id: p.id, ...(p.name ? { name: p.name } : {}), ...(p.testResults?.length ? { testResults: p.testResults } : {}), personnelClassIds: p.personnelClassIds, ...(p.window ? { window: p.window } : {}), ...(p.workCalendar?.length ? { workCalendar: p.workCalendar } : {}), ...(p.homeLocationId ? { homeLocation: p.homeLocationId } : {}), ...(p.properties?.length ? { properties: p.properties } : {}), ...(p.testSpecificationIds?.length ? { testSpecificationIds: p.testSpecificationIds } : {}), ...(p.effectiveStart ? { effectiveStart: p.effectiveStart } : {}), ...(p.effectiveEnd ? { effectiveEnd: p.effectiveEnd } : {}) }))\n /* 자산도 손대지 않고 통과 — 등급·자리 그대로 커널까지. */\n const assets = master.assets?.map(a => ({ id: a.id, ...(a.name ? { name: a.name } : {}), ...(a.testResults?.length ? { testResults: a.testResults } : {}), assetClassIds: a.assetClassIds, homeLocation: a.homeLocationId, gs1Id: a.gs1Id, ...(a.properties?.length ? { properties: a.properties } : {}), ...(a.testSpecificationIds?.length ? { testSpecificationIds: a.testSpecificationIds } : {}), ...(a.effectiveStart ? { effectiveStart: a.effectiveStart } : {}), ...(a.effectiveEnd ? { effectiveEnd: a.effectiveEnd } : {}) }))\n /* 품목 정의·등급도 손대지 않고 통과 — 경계에서 어휘를 갈지 않는다(환산 계수는 커널이 읽는다). */\n const materialDefinitions = master.materialDefinitions?.map(d => ({\n id: d.id,\n /* 이름은 이름 자리에 — 예전에는 `name` 을 `description` 으로 옮겨 담았다(어휘가 뒤집혀 있었다). */\n ...(d.name ? { name: d.name } : {}),\n ...(d.materialClassIds?.length ? { materialClassIds: d.materialClassIds } : {}),\n ...(d.properties?.length ? { properties: d.properties } : {}),\n ...(d.testSpecificationIds?.length ? { testSpecificationIds: d.testSpecificationIds } : {}),\n ...(d.effectiveStart ? { effectiveStart: d.effectiveStart } : {}),\n ...(d.effectiveEnd ? { effectiveEnd: d.effectiveEnd } : {})\n }))\n /*\n * **등급의 표시명은 `name` 이다.**\n *\n * 표준 마스터는 등급의 표시명을 `description` 에 담는다(`RefResourceClass.description` = i18n 키).\n * 그것을 그대로 모델에 실으면 화면의 \"설명\" 열에 이름이 앉는다 — 사용자에게는 이름인데 머리글은\n * 설명이라, 같은 것을 축마다 다른 이름으로 부르게 된다.\n *\n * 그래서 **문 하나에서 어휘를 맞춘다**: 표시명은 어느 축이든 `name`. 원래의 `description` 은\n * 남겨 두지 않는다 — 두 자리에 같은 값이 있으면 어느 것이 정본인지 다시 헷갈린다.\n */\n const asName = (list?: RefResourceClass[]) =>\n list?.map(({ description, ...rest }) => ({ ...rest, ...(description ? { name: description } : {}) }))\n /*\n * 검사 명세도 **표시명은 `name`** 이다 — 등급과 같은 규칙을 쓴다(어휘를 하나로). 표준 마스터는\n * 표시명을 `description` 에 담으므로 문 하나에서 옮긴다. 그러지 않으면 화면의 \"설명\" 열에 이름이\n * 앉아 축마다 다른 이름으로 같은 것을 부르게 된다(등급에서 이미 겪었다).\n */\n const testSpecifications = master.testSpecifications?.map(({ description, ...rest }) => ({\n ...rest,\n ...(description ? { name: description } : {})\n }))\n const personnelClasses = asName(master.personnelClasses)\n const equipmentClasses = asName(master.equipmentClasses)\n const assetClasses = asName(master.assetClasses)\n const materialClasses = asName(master.materialClasses)\n\n const model = {\n locations, equipment, layout, spaceId,\n /* 생산 정의도 손대지 않고 통과 — 경계에서 어휘를 갈지 않는다(커널이 그대로 소비한다). */\n ...(master.productionSpec ? { productionSpec: master.productionSpec } : {}),\n /* 정체성 선언도 그대로 통과한다 — 없으면 만들지 않는다(커널이 레거시 상수로 떨어진다). */\n ...(master.identity ? { identity: master.identity } : {}),\n ...(materialDefinitions?.length ? { materialDefinitions } : {}),\n ...(materialClasses?.length ? { materialClasses } : {}),\n ...(utcOffsetMinutes !== undefined ? { utcOffsetMinutes } : {}),\n ...(operations?.length ? { operations } : {}),\n ...(testSpecifications?.length ? { testSpecifications } : {}),\n ...(personnelClasses?.length ? { personnelClasses } : {}),\n ...(equipmentClasses?.length ? { equipmentClasses } : {}),\n ...(assetClasses?.length ? { assetClasses } : {}),\n ...(persons?.length ? { persons } : {}),\n ...(assets?.length ? { assets } : {})\n }\n\n const areas = areaLocs.map(l => ({\n id: l.id, type: l.type, name: l.name ?? l.id, parentId: l.parentId, ...(l.upstreamId ? { upstreamId: l.upstreamId } : {}),\n x: l.footprint?.x ?? 0, y: l.footprint?.y ?? 0, w: l.footprint?.w ?? 10, h: l.footprint?.h ?? 10\n }))\n // 인제스트 현장 물질화(space facet #4) — 표현이 없고 geo 가 있으면 기본 map 표현 합성.\n // \"인제스트하면 알아볼 현장(내 place)이 뜬다\"는 공간 중심 정수 보장(zero-to-twin): 커넥터/템플릿이\n // 표현을 안 줘도 geo 만 있으면 위성 지도로 착지. (area 폴리곤 물질화는 좌표 투영 필요 → space #3.)\n const geo = master.space.geo\n const givenReps = master.space.representations ?? []\n const representations =\n givenReps.length > 0\n ? givenReps\n : geo\n ? [{ id: 'r-map', type: 'map', name: '위성 지도', primary: true, center: { lat: geo.lat, lon: geo.lon }, zoom: 17 }]\n : []\n if (representations.length === 0) {\n warnings.push(noViewableRepresentation())\n }\n const spaceContent = {\n width: master.space.width, depth: master.space.depth, unit: master.space.unit ?? 'm',\n geo, representations, primaryId: master.space.primaryId ?? representations[0]?.id,\n areas\n }\n\n return { model, spaceContent, warnings }\n}\n\n/**\n * 다시 읽을 사이트를 고른다 — **기억해 둔 사이트 하나만**.\n *\n * 못 찾으면 **첫 사이트로 때우지 않는다**: 다른 현장의 구조를 이 트윈에 덮어쓰는 일이고, 그것은 오류가\n * 아니라 **조용히 틀린 공장**으로 나타난다. 기억이 없는 옛 트윈은 `undefined` 를 돌려주고, 부르는 쪽이\n * 마스터의 정체(`source` = 인스턴스 id)로 찾는 길을 택한다.\n */\nexport function pickOriginSite<T extends { siteId: string }>(\n sites: readonly T[],\n origin: { siteId?: string } | undefined | null\n): { site?: T; reason?: 'no-remembered-site' | 'site-gone' } {\n const wanted = String(origin?.siteId ?? '').trim()\n if (!wanted) return { reason: 'no-remembered-site' }\n const site = sites.find(s => String(s.siteId) === wanted)\n return site ? { site } : { reason: 'site-gone' }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"reference-master.js","sourceRoot":"","sources":["../../../server/service/reference/reference-master.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Face2 마스터 인제스트(ADR-0018 반영 모드) — 레퍼런스 시스템(실 WMS/YMS/MES 또는 가상 레퍼런스)의\n * 마스터를 읽어 트윈 구조(TwinModelDef) + 공간(Space content)을 생성. 손배선이 아니라 인제스트로 엔티티가 흐른다.\n *\n * 규율(ADR-0018): 도메인 타입(location.type=노드용)은 커널 카탈로그 어휘를 그대로 반영(발명 금지).\n * location 은 role 로 노드(운영점)·area(공간영역)·both 를 구분 — 노드→TwinModelDef, area→Space.areas.\n * 좌표·representations 는 트윈 공간층(보완/추가). 실 시스템이 좌표를 안 주면 layout 없이 위상만 인제스트.\n */\n\nimport type { DomainSystem, EquipmentLevel, TestSpecificationCriterion, WorkCalendarEntry } from '@operato/twin-kernel'\n\nexport interface RefLocation {\n id: string\n type: string // 노드는 커널 노드타입(dock/storage/yard-slot…), area 는 공간 region 타입\n name?: string\n description?: string\n role: 'location' | 'area' | 'both'\n parentId?: string\n /**\n * **전기적으로 어디서 받는가** — 이 자리에 전기를 주는 상류 자리(커널 계약의 `upstreamId`).\n *\n * `parentId`(공간 포함)와 따로 두는 이유는 커널 계약에 적혀 있다. 여기서 빠뜨리면 마스터가 말한 계통이\n * 모델까지 오지 못하고, 뿌리 계량 판정이 분기 계량기까지 뿌리로 세어 **부하가 두 배로** 보인다.\n */\n upstreamId?: string\n /** 저장 용량 — 이 자리에 동시에 놓일 수 있는 물품 수. */\n capacity?: number\n /**\n * 동시 처리 수 — 이 자리에서 한 번에 진행될 수 있는 작업 수. **저장 용량과 다른 축**이다\n * (자재를 20개 쌓아 두는 절단 스테이션이 한 번에 한 대만 깎는다). 미지정=제약 없음.\n */\n parallelism?: number\n /**\n * **ISA-95 설비 계층 단계** — `EquipmentLevel` 열거값(`ProductionLine`·`StorageZone`·`WorkCenter`…).\n *\n * `type` 은 현장의 낱말(`paint-booth`)이고 이것은 **표준이 정한 역할**이다. 둘은 다른 축이라 함께 든다 —\n * 연동·집계는 표준 축으로, 화면은 현장 낱말로. 미지정이면 그 자리의 표준 역할을 아직 모른다는 뜻이고,\n * 짐작해 채우지 않는다.\n */\n level?: EquipmentLevel\n /**\n * 자리의 **열린 속성** — 표준 `OperationalLocationProperty`(설비·사람·자산과 같은 모양).\n *\n * 무엇이 여기 실리나: **자리의 물리량**이다. 에너지의 계약전력(`contract.kW`)이 첫 사례다 —\n * 그 값이 앉을 자리가 없어서 EMS 커널은 계약 대비 판정을 할 수 없었다(자리가 없으면 사실이\n * 들어오지 못한다). 커널은 자기가 **읽는** 속성 id 만 선언하고(`EMS_PROPERTY`) 나머지는 나른다.\n */\n properties?: RefResourceProperty[]\n footprint?: { x: number; y: number; w: number; h: number } // 있으면 area 기하 + 노드 중심좌표\n gs1Id?: string // 정식 GS1 정체성(§J) — 위치=SGLN. 결합(tag)/추적 canonical id. 받아들이기 경계서 부여(로컬 id=name/xref 로 보존).\n}\n\n/**\n * 설비 속성 — **ISA-95 `EquipmentSpecificationProperty`(`OpEquipmentSpecificationPropertyType`) 1:1.**\n * 그 타입도 `ID` + `Value`(`ValueString`·`DataType`·`UnitOfMeasure`) 이므로 공정 모수와 같은 모양이다.\n *\n * 단위 코드는 **UN/CEFACT 권고 20 공통코드**(EPCIS `uom` 과 같은 코드계): 속도는 `MTS`(metre per\n * second) 또는 `KMH`(kilometre per hour). 코드를 우리가 발명하지 않는다.\n */\n/**\n * 자원 속성 — **커널 `ResourceProperty` 와 같은 모양**이고, 그 모양은 ISA-95 가 세 자원에 똑같이\n * 정의한 것이다(`EquipmentPropertyType` · `PersonPropertyType` · `PhysicalAssetPropertyType`).\n *\n * 그래서 자원마다 다른 속성 타입을 두지 않는다 — 예전에는 설비만 속성을 가졌고(RefEquipmentProperty),\n * 인원·자산에 붙는 사실은 **실을 자리가 없어서 들어오지 못했다.**\n */\nexport interface RefResourceProperty {\n id: string\n description?: string\n value?: string\n /** 표준 `Value.DataType`. */\n dataType?: string\n /** 표준 `Value.UnitOfMeasure` — UN/CEFACT 공통코드. 물리량에 단위가 없으면 소비처가 거절한다. */\n uom?: string\n /** 하위 속성(재귀) — 표준 `<X>PropertyChild`. */\n children?: RefResourceProperty[]\n /** 이 개체 속성이 구체화하는 등급 속성 — 표준 `<X>ClassPropertyID`. */\n classPropertyId?: string\n}\n\n/** 옛 이름 — 설비 전용이던 시절의 별칭(소비처 호환). 신규 소싱은 `RefResourceProperty`. */\nexport type RefEquipmentProperty = RefResourceProperty\n\n/**\n * 자원 **등급 정의** — 커널 `ResourceClassDef` 와 같은 모양이고, 그 모양은 ISA-95 가 세 자원에 똑같이\n * 정의한 것이다(`PersonnelClassType`·`EquipmentClassType`·`PhysicalAssetClassType`).\n *\n * 왜 필요한가: 원 시스템은 등급 **체계**를 갖고 있다(\"6축 용접 자격자\" 는 \"용접 자격자\" 이고, 그것은\n * \"생산직\" 이다). 그 체계가 들어올 자리가 없으면 요구와 소속이 **문자열이 똑같을 때만** 맞아,\n * 현장에서는 되는 배정이 모델에서 막힌다.\n */\nexport interface RefResourceClass {\n id: string\n description?: string\n /** 상위 등급들 — 표준 `<X>ClassBaseID`(복수 = 다중 상속). */\n baseIds?: string[]\n properties?: RefResourceProperty[]\n testSpecificationIds?: string[]\n /** 유효 기간(ISO) — 표준 `EffectiveStartDate`/`EffectiveEndDate`. 밖이면 자격이 성립하지 않는다. */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 검사 명세 — **자원이 가리키는 대상**(ISA-95 `TestSpecification`).\n *\n * 표준 필드는 셋만 담는다(`ID`·`Description`·`Version`) — 재는 속성과 합격 기준은 아직 1차 출처와\n * 대조하지 않았다(커널 계약의 머리말이 그 사실을 적어 두었다).\n */\n/**\n * 시험 결과 — **\"이 개체가 그 시험을 통과했다\"** 는 기록(ISA-95 결과).\n *\n * 결과를 낳는 곳은 트윈이 아니다(인사·정비 시스템이 기록한다) — 그래서 관측이면서 **선언 경로로** 온다.\n * 커널은 이 값이 있을 때만 자격을 판정한다: 없으면 제약이 아니고(라인이 굶지 않는다), 있으면 만료·\n * 불합격이 배정을 막는다.\n */\nexport interface RefTestResult {\n specId: string\n result: 'pass' | 'fail'\n at?: string\n /** 없으면 무기한 — 짐작으로 기한을 만들면 멀쩡한 자격자가 조용히 사라진다. */\n expiresAt?: string\n}\n\nexport interface RefTestSpecification {\n id: string\n /** 표시명 — i18n 키일 수 있다. 읽는 어휘를 하나로 맞추려고 인제스트가 `name` 으로 옮긴다. */\n description?: string\n version?: string\n /**\n * 판정 기준들 — 커널 계약의 `TestSpecification.criteria` 를 그대로 나른다.\n *\n * **경계에서 어휘를 갈지 않는다.** 커널 타입을 그대로 쓰므로 `limit.{minimum,maximum,uom}` ·\n * `expression` · `evaluatedPropertyId` 가 원본에서 커널까지 한 이름으로 간다.\n *\n * 선언하지 않아도 명세는 성립한다(이름만으로 「무엇으로 검증했나」에 답한다). 선언하면 **판정에\n * 근거가 있는지**를 물을 수 있게 된다 — 커널의 `criterionSaysNothing` 이 그것을 센다.\n */\n criteria?: TestSpecificationCriterion[]\n}\n\nexport interface RefEquipment {\n id: string\n kind: string\n /** 사람이 부르는 이름 — 없으면 화면이 id 를 보인다(id 를 이름으로 베끼지 않는다). */\n name?: string\n /** 시험 결과 — 자격이 성립하는지는 이것이 말한다(§RefTestResult). */\n testResults?: RefTestResult[]\n description?: string\n homeLocationId: string\n mtbfMs?: number // 평균 고장 간격(확률적 고장) — 지정 시 커널이 breakdown 시뮬 → 주목(고장) 발생\n mttrMs?: number // 평균 수리 시간\n gs1Id?: string // 정식 GS1 정체성(§J) — 설비=GIAI.\n /**\n * 교대(가동시간) — 이 시간대에만 배정된다. `startHour <= endHour` 면 같은 날, 넘어가면 야간(22→6).\n * 미지정=24시간 가용. 교대 밖 자원은 고장·계획정지와 **구별되는 이유**로 쉰다(상태에 드러난다).\n */\n window?: { startHour: number; endHour: number }\n /**\n * 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 있으면 `window` 보다 이것을 본다.\n *\n * 축을 프레임워크 `@things-factory/work-shift` 의 `WorkShift`(`fromDate`·`fromTime`·`toDate`·`toTime`)와\n * 맞춰 두었다 — 그 엔티티를 교대의 정본으로 결선할 때 직선 매핑이 되게. **아직 결선하지 않았다**:\n * 그 엔티티에는 자원 연결과 비근무(휴일)가 없어 확장이 함께 필요하고, operato-twin 의 의존성 추가는\n * 앱 아키텍처 결정이다(커버리지 §4-7 에 결선 지점으로 기록).\n */\n workCalendar?: WorkCalendarEntry[]\n /**\n * 설비 속성(속도 등) — 없으면 **이동시간을 거리에서 계산할 수 없다**(속도 없이 거리만으로는 시간이\n * 안 나온다). 그때는 커널 상수로 떨어지고 그 사실이 `specCoverage()` 에 남는다.\n */\n properties?: RefResourceProperty[]\n /** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`(교정 등). */\n testSpecificationIds?: string[]\n /**\n * 유효 기간 — **표준 `EffectiveStartDate` / `EffectiveEndDate`**(개체 타입 셋 모두에 있다).\n *\n * 없으면 **폐기한 것이 영구히 살아 있다**: 배정 대상에 남고, 가용 수에 들어가고, 화면에 뜬다.\n * 도입 예정인 것은 반대로 오늘부터 있는 것처럼 보인다. 기간 밖이면 커널이 그 시각의 모델에서\n * 빼고 **이유를 달아 남긴다**(`effectivity: 'not-yet' | 'expired'`) — 조용히 지우지 않는다.\n */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 공정 모수 — 커널 `OperationParameter`(= ISA-95 `ParameterSpecification`/`ParameterType`) 와 **같은 모양**.\n * 이름을 바꾸지 않는다: 인제스트 경계에서 어휘가 갈리면 그 자리에서 방언이 생긴다.\n * ID 어휘의 정본은 커널 `OP_PARAM`(표준이 파라미터 ID 를 정하지 않으므로 우리가 한 곳에서 정한다).\n */\nexport interface RefOperationParameter {\n id: string\n /** ISA-95 `Value.ValueString` — 표준이 문자열로 싣는다. 기간 모수는 ISO 8601(`PT8M`). */\n value: string\n /** ISA-95 `Value.UnitOfMeasure`. 무차원(비율)이면 생략. */\n uom?: string\n}\n\n/**\n * 공정의 자재 요구·산출 — **ISA-95 `OperationsSegment.MaterialSpecification`** 과 같은 모양.\n * 인원·설비·자산 명세와 같은 자리에 있어야 4대 자원이 대칭이 된다(자재만 빠져 있었다).\n */\nexport interface RefOpMaterialSpecification {\n id?: string\n /** 품목 등급으로 요구 — 표준 `MaterialClassID`. */\n materialClass?: string\n /** 특정 품목으로 요구 — 표준 `MaterialDefinitionID`. */\n materialDefinition?: string\n /** 표준 `MaterialUse` — 이 공정에서의 쓰임. */\n use: 'consumed' | 'produced' | 'consumable'\n /** 표준 `Quantity`. 단위 미지정이면 개수. */\n quantity: number\n uom?: string\n}\n\n/** 소요시간 변동 — **표준 밖 확장**(ISA-95 Duration 은 스칼라). 커널 `DurationVariability` 와 동형. */\nexport interface RefOperationVariability {\n distribution: 'constant' | 'exponential' | 'uniform' | 'triangular'\n min?: string\n max?: string\n mode?: string\n}\n\n/**\n * 오퍼레이션 명세 — ISA-95 `OperationsSegment`. 커널 `OperationDef` 와 필드 이름이 1:1이다.\n *\n * **이것이 없으면 시뮬레이션의 시간은 우리가 코드에 고정한 상수다.** 원 시스템(또는 가상 레퍼런스)이\n * 공정별 소요·변동·수율을 말해 줘야 예측이 그 현장의 예측이 된다. 주지 않으면 커널 기본값으로\n * 굴러가고, **기본값을 썼다는 사실은 커널 `specCoverage()` 가 밝힌다**(조용히 넘어가지 않는다).\n */\nexport interface RefOperation {\n /** 작업 종류 키 — 커널 `FlowTask.kind` 와 같은 값이어야 명세가 그 작업에 붙는다. */\n key: string\n label: string\n intent: 'transport' | 'process' | 'dwell'\n locationType?: string\n resourceType?: string\n bizStep?: string\n /** ISA-95 `OperationsSegment.Duration` — ISO 8601 기간(`PT12M`). */\n duration?: string\n variability?: RefOperationVariability\n parameters?: RefOperationParameter[]\n /**\n * 필요 인원 — ISA-95 `PersonnelSpecification`(`PersonnelClassID` + `Quantity`) 1:1.\n * 없으면 사람 없이 도는 공정이다(설비만 잡는다).\n */\n personnelSpecification?: { personnelClass?: string; quantity: number }[]\n /** 필요 물리 자산 — ISA-95 `PhysicalAssetSpecification`(등급 + 수량). 빈 팔레트가 없으면 못 나간다. */\n physicalAssetSpecification?: { assetClass?: string; quantity: number }[]\n /**\n * 필요·산출 자재 — ISA-95 `MaterialSpecification`. **4대 자원 중 자재만 이 자리가 없었다** —\n * 그래서 부품이 떨어져 라인이 서는 상황이 예측에 나타나지 않았다.\n * BOM 의 \"몇 개\" 는 여기다(품목 정의의 조립 구조에는 수량이 없다 — 1차 출처 확인).\n */\n materialSpecification?: RefOpMaterialSpecification[]\n}\n\n/**\n * 사람 — **ISA-95 `Person`.** 설비와 별개 자원이다(고장·설비효율이 아니라 등급·교대로 산다).\n * `personnelClass` 가 ISA-95 `PersonnelClassID` — 공정은 특정인이 아니라 **등급으로** 인원을 요구한다.\n */\nexport interface RefPerson {\n id: string\n name?: string\n /** 시험 결과 — 자격이 성립하는지는 이것이 말한다(§RefTestResult). */\n testResults?: RefTestResult[]\n /**\n * 속한 등급들 — **표준 `Person.PersonnelClassID`, `maxOccurs=\"unbounded\"`**.\n * 복수인 것이 표준이고 그것이 자격 표현이다: 한 사람이 용접 자격과 지게차 자격을 함께 갖는다.\n */\n personnelClassIds?: string[]\n /** 교대 — 설비와 같은 모양. 미지정=24시간 가용. */\n window?: { startHour: number; endHour: number }\n /** 근무 캘린더 — 표준 `WorkCalendarEntry`(복수). 2교대·휴게·휴일을 함께 표현한다. */\n workCalendar?: WorkCalendarEntry[]\n /**\n * 기본 위치 — 표준 `Person.OperationalLocation`. 없으면 \"이 라인에 몇 명 있나\" 를 물을 수 없다.\n * 사람은 움직이므로 이 값은 **출발점**이고, 실제 위치는 관측(person.status)이 갱신한다.\n */\n homeLocationId?: string\n /**\n * 자원 속성 — 표준 `PersonProperty`. **자격증·숙련도가 여기 들어간다.**\n *\n * 표준에는 자격(Qualification)이라는 별도 요소가 없다(`B2MML-Personnel.xsd` 확인). 자격은\n * ① 등급 복수 소속으로(`personnelClassIds: ['welder-certified','forklift-licensed']`) ②\n * 증빙은 이 속성으로(자격증 번호·만료일), ③ 검증 시험은 표준 `TestSpecificationID`(아직 미구현).\n */\n properties?: RefResourceProperty[]\n /** 자격을 검증한 시험 명세들 — 표준 `TestSpecificationID`(참조만; 시험 명세·결과 모델은 아직 없다). */\n testSpecificationIds?: string[]\n /** 유효 기간 — 사람에게는 **입사 전·퇴사 후**다(자격 만료는 등급 쪽 기간이다: 사람은 남고 자격만 끊긴다). */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 물리 자산(반복사용) — **ISA-95 `PhysicalAsset`, GS1 `GRAI`.**\n * 팔레트·랙·용기처럼 **나갔다 돌아오는** 것. 물류단위(SSCC)와 다르다: SSCC 는 그 팔레트에 실린 화물\n * 한 덩어리이고, GRAI 는 팔레트 자체다(오늘은 이 화물, 내일은 저 화물을 싣는다).\n */\nexport interface RefAsset {\n id: string\n name?: string\n /** 시험 결과 — 자격이 성립하는지는 이것이 말한다(§RefTestResult). */\n testResults?: RefTestResult[]\n /**\n * 속한 등급들 — 표준 `PhysicalAssetClassID`(복수). 팔레트가 'euro-pallet' 이면서 'food-grade' 일 수 있다.\n * 공정의 요구는 등급 하나 + 수량이다(표준 `PhysicalAssetSpecification`).\n */\n assetClassIds?: string[]\n /** 기본 위치. */\n homeLocationId?: string\n /** 정식 GS1 정체성 — 반복사용 자산=GRAI. */\n gs1Id?: string\n /** 자원 속성 — 표준 `PhysicalAssetProperty`(적재 한도·규격 등). */\n properties?: RefResourceProperty[]\n /** 적격을 검증한 시험 명세들 — 표준 `TestSpecificationID`. */\n testSpecificationIds?: string[]\n /** 유효 기간 — 자산에서는 **폐기한 팔레트가 풀에서 빠지는 것**이다(회수 대상 수가 부풀지 않게). */\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * 품목 정의 — **ISA-95 `MaterialDefinition`.**\n *\n * 없으면 로트만 관측으로 들어오고 **품목이 무엇인지는 아무 데도 없다.** 그래서 같은 로트를 100개로\n * 받아도 \"몇 kg 인가\" 에 답할 수 없다(커널 `quantityIn` 이 환산을 거부하는 근거).\n *\n * **표준에 단위 환산 요소는 없다**(1차 출처 전수 확인) — 속성 주머니뿐이다. 그래서 환산 계수는\n * 커널이 정한 속성 이름(`perBaseUnit`, `uom` 이 대상 단위)으로 `properties` 에 싣는다.\n */\nexport interface RefMaterialDefinition {\n /** 표준 `ID` — GTIN(`urn:epc:idpat:sgtin:…`)을 쓴다(정체성의 정본이 EPCIS 다). */\n id: string\n name?: string\n /** 속한 등급들 — 표준 `MaterialClassID`(복수). */\n materialClassIds?: string[]\n /** 품목 속성 — 표준 `MaterialDefinitionProperty`. 환산 계수가 여기 들어간다. */\n properties?: RefResourceProperty[]\n testSpecificationIds?: string[]\n effectiveStart?: string\n effectiveEnd?: string\n}\n\n/**\n * **이 마스터를 다시 만들 수 있는 방법** — 트윈이 자기 원본을 기억하는 자리.\n *\n * 트윈은 원본을 비출 뿐이므로(ADR-0028), 원본이 달라지면 **다시 읽어야** 합니다. 그런데 지금까지\n * 트윈은 \"내가 무엇에서 나왔나\" 를 남기지 않았다 — 어댑터로 들어온 트윈은 `TwinReference` 행이\n * 있었지만 템플릿으로 세운 트윈에는 아무것도 없었고, 그래서 **다시 읽을 대상이 없는 트윈**이\n * 만들어졌다. 원본을 잃은 트윈은 첫날의 모습에 영원히 머문다.\n *\n * 그래서 원천이 무엇이든 여기 한 자리에 남긴다. 다시 읽기는 이 값 하나만 보면 된다.\n */\nexport type MasterOrigin =\n /**\n * 등록된 레퍼런스(어댑터)에서 왔다 — 다시 읽기 = 그 어댑터로 마스터를 다시 가져온다.\n *\n * `source` 는 **레퍼런스 등록 이름**이고 그 참조가 보고하는 사이트 전부에 같다. 그래서 어느 사이트에서\n * 온 트윈인지는 `siteId` 가 있어야 안다 — 없으면 다시 읽기가 사이트를 찾지 못한다(2026-08-18 실측:\n * 사이트가 여러 개인 참조에서 온 트윈은 재동기가 전부 거절됐다. 참조 이름을 사이트별 마스터의\n * `source`(=인스턴스 id)와 비교하고 있었다 — 서로 다른 것을 비교한 것이다).\n */\n | { kind: 'reference'; source: string; siteId?: string; spaceId?: string }\n /**\n * 템플릿 + 노브로 합성했다 — 다시 읽기 = 같은 노브로 다시 짓는다(합성은 결정적이다).\n *\n * `spaceId` 는 **만들 때 고른 현장**이다(합쳐 넣기). 기억하지 않으면 다시 읽을 때 트윈이 조용히 자기\n * 현장을 떠난다 — 템플릿이 낸 기본 공간으로 돌아가기 때문이다. 그러면 그 현장의 값(요금 등)도 함께\n * 잃는다(2026-08-18 실측: 한 현장에 합쳐 둔 EMS 트윈 여덟이 재동기 뒤 각자 공간으로 흩어졌다).\n */\n | { kind: 'template'; templateId: string; params: Record<string, unknown>; spaceId?: string }\n\nexport interface ReferenceMaster {\n source: string\n /**\n * 어느 종류의 트윈인가 — **커널 카탈로그가 정본**이다(`DomainSystem`).\n *\n * 예전에는 `'wms' | 'yms' | 'mes'` 를 여기 적어 뒀다. 커널에 종류가 하나 늘 때(에너지) 이 줄이\n * 함께 움직여야 하는데, 손으로 적힌 목록은 그것을 알려 주지 않는다 — 어휘의 집은 한 곳이어야 한다.\n */\n system: DomainSystem\n siteName: string\n /** 검사 명세 — 자원·등급의 `testSpecificationIds` 가 가리키는 대상. */\n testSpecifications?: RefTestSpecification[]\n /**\n * 이 마스터가 어디서 나왔나 — **다시 읽기의 유일한 근거.** 없으면 그 트윈은 다시 읽을 수 없고,\n * 조회 화면이 그 사실을 그대로 말한다(없는 것을 있는 척하지 않는다).\n */\n origin?: MasterOrigin\n /* spaceId 명시 시 그 공간을 참조(여러 트윈이 한 공간 공유, N:1). 없으면 source 에서 파생(1:1). */\n /**\n * 현장(공간). **시각 기준(`timezone`)이 여기 있는 이유**: 한 테넌트가 Rosarito(태평양)와 한국\n * 공장을 함께 가질 수 있다. 테넌트 단위(`Domain.timezone`)로 두면 **둘 중 하나는 반드시 틀린다.**\n * 시각 기준은 **물리 현장의 성질**이므로 공간이 그것을 갖는다(공간 중심 원칙).\n *\n * `timezone` 은 IANA 이름(`'America/Tijuana'`) — 오프셋 숫자가 아니다. 숫자로 두면 일광절약시간을\n * 표현할 수 없고, 나중에 절대 구간으로 옮길 때도 이름이 있어야 계산할 수 있다.\n */\n space: { spaceId?: string; width: number; depth: number; unit?: string; timezone?: string; geo?: { lat: number; lon: number; rotation?: number }; representations?: any[]; primaryId?: string }\n locations: RefLocation[]\n equipment?: RefEquipment[]\n /** 공정 명세(소요·변동·수율) — 없으면 커널 기본값으로 굴러간다(그 사실은 specCoverage 가 보고). */\n operations?: RefOperation[]\n /**\n * **생산 정의**(레시피·라우트·자재 바인딩) — 커널의 정의-구동 MES 모드 입구.\n *\n * 없으면 커널은 레거시(하드코딩) 흐름으로 동작한다. 즉 **현장 레시피를 적어도 이 자리에 실리지 않으면\n * 커널은 그것을 보지 못한다** — 선언과 실행이 어긋나는 지점이라 자리를 연다.\n */\n /**\n * 생산 선언 — `TwinModelDef.productionSpec` 으로 통과한다(ISA-95 OperationsSegment + BOM).\n *\n * 예전 이름은 `mesSpec` 이었다. 담긴 것은 MES 만의 것이 아니라 \"자재를 소비해 자재를 산출하는\n * 공정\" 이고, 이름이 MES 였기 때문에 창고 트윈은 이 자리를 쓸 생각조차 하지 못했다(유통가공 창고가\n * BOM 을 설명 문서에만 적어 둔 채 키트를 못 만드는 트윈을 만든 원인). 근거는 커널 계약 주석에.\n */\n productionSpec?: unknown\n /**\n * **이 원본이 식별자를 만드는 이름공간** — `TwinModelDef.identity` 로 통과한다.\n *\n * **선언이 없으면 커널이 거절한다** (0.7.45~). 예전에는 상수 프리픽스로 팔레트·트레일러·문서를\n * 만들었는데(`0614141` — GS1 이 규격 예제로 쓰다 버린 값이고 `06…` 은 실 배정 대역이다), 그 값은\n * 저널에 영원히 남으면서 지어낸 것이라고 말해 주는 데가 없었다. 그래서 커널이 만들지 않는다.\n *\n * 원본이 자기 도메인을 말하면 그 아래에서 만든다 — 그것이 표준 정합이다(CBV 2.0 §8.2.4·§8.5.5,\n * 「그 인터넷 도메인 소유자가 배정한다」). GS1 키를 쓰려면 `documentTypes` + `companyPrefix` 이고,\n * 그때 **자리 수가 맞아야 한다**(SGTIN 은 프리픽스 + 품목참조 = 13자리, GS1 TDS).\n *\n * 정체성은 생산과 무관하므로 `productionSpec` 이 아니라 **모델**이 갖는다 — 창고·야드는 아무것도\n * 생산하지 않으면서 팔레트·트레일러를 식별한다.\n */\n identity?: unknown\n /**\n * 사람 — 선언하지 않으면 **인원 제약이 없는 트윈**이 된다(설비만 있으면 언제나 가동되는 공장).\n * 공정이 인원을 요구하는데 사람이 없으면 그 작업은 시작되지 않는다 — 그것이 사실이므로 그대로 둔다.\n */\n persons?: RefPerson[]\n /**\n * 물리 자산(반복사용) — 선언하지 않으면 자산 제약이 없는 트윈이다.\n * 공정이 자산을 요구하는데 없으면 그 작업은 시작되지 않는다 — 그것이 사실이므로 그대로 둔다.\n */\n assets?: RefAsset[]\n /**\n * 등급 정의 — 표준 `PersonnelClass`·`EquipmentClass`·`PhysicalAssetClass`.\n * 안 실어도 트윈은 동작한다(소속 문자열 그대로 판정). 실으면 상속·유효기간이 살아난다.\n */\n personnelClasses?: RefResourceClass[]\n equipmentClasses?: RefResourceClass[]\n assetClasses?: RefResourceClass[]\n /** 품목 정의·등급 — 표준 `MaterialDefinition` / `MaterialClass`. 없으면 단위 환산을 할 수 없다. */\n materialDefinitions?: RefMaterialDefinition[]\n materialClasses?: RefResourceClass[]\n /**\n * 커넥터가 **옮기는 동안 알게 된 것.** 외부 시스템의 값을 우리 어휘로 못 옮겨 기본값을 넣었다면\n * 여기에 담아 보낸다 — 그러면 `masterToTwin` 의 경고와 한 줄기로 합쳐져 화면까지 간다.\n *\n * 이 자리가 없던 동안 커넥터는 할 말이 있어도 **말할 곳이 없었다.** 그래서 도크를 랙으로 바꿔\n * 놓고도 조용히 성공했다. 옮기는 사람만 아는 사실은 옮기는 자리에서 실어야 한다.\n */\n warnings?: IngestWarning[]\n /**\n * **원본이 가진 수** — 옮긴 수의 분모다.\n *\n * 총평은 도착한 모델만 볼 수 있어서 「레시피 1,261」까지만 말할 수 있었다. 그런데 사람이 묻는 것은\n * 「그게 전부인가」다 — 원본에 1,408개가 있었고 147개를 옮기지 못했다면 그 사실이 총평의 첫 줄에\n * 있어야 한다. 실제로 사용자가 품목 2,789 와 자재 1,789 의 차이를 화면에서 읽어 내지 못해 직접\n * 물었다(2026-08-22).\n *\n * 분모를 아는 것은 **커넥터뿐이다.** 그래서 커넥터가 실어 보낸다. 보내지 않으면 총평은 분모 없이\n * 옮긴 수만 말한다 — **지어내지 않는다.**\n *\n * 열쇠는 총평이 세는 이름과 같다(`recipes`·`materials`·`totalInputs`·…). 값은 **원본을 끝까지 읽어서\n * 센 것**이어야 한다. 잘린 읽기의 수를 여기 담으면 「전부 옮겼다」는 거짓이 된다.\n */\n originCounts?: Record<string, number>\n}\n\nexport interface IngestedTwin {\n model: any // TwinModelDef(+layout+spaceId) — 커널 소비\n spaceContent: any // TwinSpace content — 뷰 소비\n warnings: IngestWarning[]\n}\n\n/**\n * 마스터 → 트윈 구조 매핑(순수). 노드타입을 카탈로그로 검증(모르는 타입은 warning, 버리진 않음 — 반영 우선).\n * @param catalog DOMAIN_CATALOG (커널 SSOT). 없으면 검증 생략.\n */\n/**\n * IANA 시간대 이름 → **그 시각의** UTC 오프셋(분). 의존성 없이 `Intl` 로 푼다.\n *\n * `Intl.DateTimeFormat` 의 `longOffset` 은 `\"GMT-07:00\"` 처럼 나오고, 이것은 **일광절약시간을 반영한**\n * 값이다. 그래서 8월의 `America/Tijuana` 는 −420(PDT), 1월은 −480(PST)이 된다.\n *\n * **한계를 밝힌다**: 여기서 나오는 것은 **한 시점의** 오프셋이다. 커널은 고정 오프셋으로 하루 중 분을\n * 읽으므로, 모델을 만든 계절과 다른 계절을 길게 예측하면 한 시간 어긋난다. 표준에 충실한 끝 모습은\n * 호스트가 절대 구간(`StartDateTime`/`FinishDateTime`)을 계산해 넣는 것이고, 그때 이 함수는 사라진다.\n *\n * 모르는 이름에는 **0 을 답하지 않는다**(`undefined`) — 0 은 \"UTC 라고 선언했다\" 는 뜻이고, 모르는 것을\n * 그렇게 바꾸면 판정이 조용히 틀린다.\n */\nexport function utcOffsetOf(timezone?: string, at: Date = new Date()): number | undefined {\n if (!timezone) return undefined\n try {\n const parts = new Intl.DateTimeFormat('en-US', { timeZone: timezone, timeZoneName: 'longOffset' }).formatToParts(at)\n const name = parts.find(p => p.type === 'timeZoneName')?.value ?? ''\n if (name === 'GMT') return 0 // UTC 계열은 오프셋 표기가 없다\n const m = /^GMT([+-])(\\d{1,2})(?::(\\d{2}))?$/.exec(name)\n if (!m) return undefined\n const sign = m[1] === '-' ? -1 : 1\n return sign * (Number(m[2]) * 60 + Number(m[3] ?? 0))\n } catch {\n return undefined // 알 수 없는 이름 — Intl 이 오류를 낸다\n }\n}\n\n\n/* ── 인제스트 경고 ──────────────────────────────────────────────────────────\n * 인제스트는 구조에 문제가 있어도 트윈을 만들어 준다(막지 않는 것이 옳다 — 부분적으로라도 세우고\n * 고쳐 나가는 것이 이 시스템의 방식이다). 그래서 **경고가 유일한 신호**인데, 그 경고를 화면이 하나도\n * 보여주지 않고 있었다: 유통가공 창고 템플릿은 커널이 모르는 자리 타입(`vas-station`)으로 트윈을\n * 만들면서 경고를 냈지만, 위자드는 그것을 버렸고 사용자는 아무 말 없이 막히는 트윈을 받았다.\n *\n * 문장이 아니라 코드로 실어 보낸다. 예전 경고는 프로즈였고 그중 하나에는 한글이 섞여 있었다\n * (`— 카탈로그에 없음`) — 다섯 언어 화면에 그대로 내보내면 이번에 걷어낸 문제를 다시 만든다.\n * 이 프로젝트가 이미 쓰는 규약과 같은 모양이다: 주의 신호(kind+params)·커맨드 거절(errorCode+params)·\n * 만만함 판정(code+영어 canonical message).\n *\n * code 언어 중립 — 화면이 `twin.ingest.<code>` 로 사람 말을 만든다\n * params 문장에 끼울 값 — 번역이 언어마다 어순을 바꿀 수 있게\n * message 영어 canonical — 로그에 남고, 번역이 없을 때 그대로 보인다(빈칸보다 낫다)\n *\n * **여기 사는 이유**: 이 파일이 경고의 생산자이고, 테스트가 소스에서 직접 읽는 모듈이라\n * 런타임 상대 import 를 가질 수 없다(이 저장소의 규약 — 그런 모듈은 자기완결이다).\n * ────────────────────────────────────────────────────────────────────────── */\n/** 인제스트가 알리는 한 가지. */\nexport interface IngestWarning {\n /** 언어 중립 코드. 화면은 `twin.ingest.<code>` 로 렌더한다. */\n code: string\n /** 문장에 끼울 값. 번역이 어순을 바꿀 수 있게 이름으로 넘긴다. */\n params?: Record<string, string | number>\n /** 영어 canonical 문장 — 로그·폴백용. 번역이 없어도 읽을 수 있어야 한다. */\n message: string\n}\n\n/**\n * **시각을 모르는 채 시각으로 판정하려 한다** — 발전 형상은 하루의 시각을 읽는데 이 트윈의 공간이\n * 시간대를 말하지 않았다.\n *\n * ── 왜 경고인가 (2026-08-18) ────────────────────────────────────────────────\n * 커널은 시간대를 모르면 UTC 로 읽는다(계약에 밝혀 둔 규율 — 모르는 값을 지어내지 않는다). 그런데 발전\n * 형상은 **시각별 비율**이라, 시간대가 없으면 현장 정오가 아니라 UTC 정오에 피크가 선다(KST 현장이면\n * 밤 9시). 값이 그럴듯하게 나오므로 아무도 알아채지 못한다.\n *\n * 그래서 시간대를 **지어내지 않고**(서버 로컬이나 임의 기본값을 넣으면 그 트윈의 모든 시각 판정이 조용히\n * 그 가정 위에 선다) 대신 **그 조합을 말한다** — 공간에 시간대를 선언하면 사라지는 경고다.\n */\nexport const generationWithoutTimeZone = (spaceId: string, equipmentId: string): IngestWarning => ({\n code: 'generation-without-time-zone',\n params: { spaceId, equipmentId },\n message:\n `\"${equipmentId}\" declares a daily generation shape but space \"${spaceId}\" declares no time zone — the hourly fractions will be read in UTC, ` +\n 'so the generation peak will not land at local noon. Declare the space time zone (we do not guess one).'\n})\n\n/**\n * 아무도 선언하지 않은 자리 타입 — 그 자리는 흐름에 참여하지 못할 수 있다.\n *\n * **묻는 상대를 고쳤다 (2026-08-20).** 예전에는 내장 카탈로그에게만 물었다. 그런데 원본이 자기\n * `DomainDefinition.locationTypes` 를 함께 보내면 **그 선언이 정본**이다 — 카탈로그는 우리가 아는\n * 도메인들의 기본 어휘일 뿐이다. 물을 상대가 틀렸으니, 자기 어휘를 선언한 원본은 정상적인 타입마다\n * 경고를 받았다(F&B 라인을 붙이면 자리마다 하나씩). 소음이 되면 사람은 경고를 읽지 않게 되고, 그때\n * 진짜 경고도 함께 죽는다.\n */\nexport const unknownLocationType = (system: string, type: string, id: string): IngestWarning => ({\n code: 'unknown-location-type',\n params: { system, type, id },\n message:\n `\"${type}\" is declared nowhere for ${system} (${id}) — neither in the master's own locationTypes nor in the built-in ` +\n `catalog, so the kernel may not include this location in the flow`\n})\n\n/**\n * 원본이 함께 보낸 자리 타입 선언 — 있으면 **이것이 정본**이다.\n *\n * 커널 계약의 `DomainDefinition.locationTypes` 는 `{ key, label, … }` 목록이다. 읽을 수 없는 모양이면\n * `undefined` 를 돌려 **카탈로그로 떨어진다** — 빈 배열을 돌려주면 「아무 타입도 선언되지 않았다」가\n * 되어 모든 자리가 경고를 받는다(없는 것과 못 읽은 것은 다르다).\n */\nexport function declaredLocationTypes(productionSpec: unknown): string[] | undefined {\n const list = (productionSpec as any)?.definition?.locationTypes\n if (!Array.isArray(list) || !list.length) return undefined\n const keys = list.map((t: any) => (typeof t === 'string' ? t : t?.key)).filter((k: any) => typeof k === 'string' && k)\n return keys.length ? keys : undefined\n}\n\n/** 시간대 문자열을 못 읽었다 — 시각 해석이 UTC 로 떨어진다(조용히 현지 시각으로 가정하지 않는다). */\nexport const unknownTimezone = (timezone: string): IngestWarning => ({\n code: 'unknown-timezone',\n params: { timezone },\n message: `space timezone \"${timezone}\" is not a known IANA zone — the twin will read times as UTC`\n})\n\n/** 작업 정의에 소요시간·파라미터가 없다 — 커널 기본값으로 동작한다. */\nexport const operationWithoutDuration = (operation: string): IngestWarning => ({\n code: 'operation-without-duration',\n params: { operation },\n message: `operation \"${operation}\" has no duration or parameters — kernel defaults will be used`\n})\n\n/** 볼 수 있는 표현이 없다 — 현장이 화면에 뜨지 않는다. */\nexport const noViewableRepresentation = (): IngestWarning => ({\n code: 'no-viewable-representation',\n message: 'the space has no viewable representation (no representations and no geo) — add one, or provide geo/board'\n})\n\n/**\n * 구조 투영에서 **가리키는 대상을 못 찾았다.**\n *\n * 예전에는 이런 참조가 model JSON 안의 맨 문자열이라 화면만 경고할 수 있었고, 다른 경로로 들어온\n * 데이터는 아무도 안 봤다. 이제 행으로 풀면서 **인제스트가 직접 안다** — 그러니 인제스트가 말한다.\n * 막지는 않는다(트윈은 model 로 이미 동작한다). 다만 조용히 넘기지 않는다.\n */\nexport const unresolvedReference = (field: string, ref: string): IngestWarning => ({\n code: 'unresolved-reference',\n params: { field, ref },\n message: `${field} points at something that is not in this twin (${ref}) — the link was left empty`\n})\n\n/**\n * 구조 투영 자체가 실패했다 — 조회는 못 하지만 **트윈은 동작한다**(커널은 model 를 이미 받았다).\n * 행은 원본에서 다시 그릴 수 있으므로 인제스트를 실패로 만들지 않되, 침묵하지도 않는다.\n */\nexport const projectionFailed = (reason: string): IngestWarning => ({\n code: 'projection-failed',\n params: { reason },\n message: `structure projection failed (${reason}) — the twin runs, but model inspection will be stale until the next ingest`\n})\n\n/**\n * **동작 중에 공장을 전환했다** — 미러는 멈추지 않으므로, 무엇이 사라졌는지 여기서 말한다.\n *\n * 멈췄다 세우는 구조 전환이라면 사용자가 \"다시 세웠다\" 는 것을 안다. 미러는 그 마디가 없어서\n * 자리 하나·설비 한 대가 조용히 없어질 수 있다 — 자리가 수천인 현장에서 화면의 수를 세어\n * 눈치채기를 기대할 수는 없다. 늘어난 것도 함께 말한다(무엇이 반영되었나가 곧 인제스트의 답이다).\n */\nexport const structureAdopted = (rev: number, shift: { locationsAdded: number; locationsDropped: number; equipmentDropped: number; personsDropped: number; assetsDropped: number }): IngestWarning => ({\n code: 'structure-adopted',\n params: { rev, ...shift },\n message:\n `the running mirror adopted a new structure (revision ${rev}) — ` +\n `+${shift.locationsAdded} location(s), -${shift.locationsDropped} location(s), ` +\n `-${shift.equipmentDropped} equipment, -${shift.personsDropped} person(s), -${shift.assetsDropped} asset(s)`\n})\n\n/**\n * 커넥터가 외부 시스템의 값을 **우리 어휘로 옮기지 못해 기본값을 넣었다.**\n *\n * 예: Oracle WMS 가 우리 표에 없는 `locn_type` 을 주면 그 자리는 `storage` 가 된다 — 도크가 랙으로\n * 모델링되는 것이다. 인제스트를 통째로 실패시킬 일은 아니지만(로케이션 하나 때문에 현장 전체가\n * 안 들어오는 편이 더 나쁘다), **조용히 넘어가면 안 된다.** 트윈이 실물과 다르게 생겼는데\n * 아무도 그 사실을 모른 채 그 위에서 흐름을 논하게 된다.\n *\n * 값을 나열하는 이유: \"3건\" 만으로는 커넥터 매핑에 무엇을 더해야 하는지 알 수 없다.\n */\nexport const connectorMappingFallback = (field: string, fallback: string, values: readonly string[], count: number): IngestWarning => ({\n code: 'connector-mapping-fallback',\n params: { field, fallback, values: values.join(', '), count },\n message: `${count} ${field} value(s) had no mapping and fell back to \"${fallback}\" (${values.join(', ')}) — the twin's shape differs from the source system here`\n})\n\n\n/** 로그 한 줄 — 개발자가 읽는 것이므로 영어 canonical 문장을 쓴다. */\nexport const describeWarnings = (warnings: readonly IngestWarning[]): string =>\n warnings.map(w => `${w.code}: ${w.message}`).join(' | ')\n\n/**\n * 서버가 기본값으로 채운 노브들 — 사용자가 준 적 없는 값으로 트윈이 만들어졌음을 알린다.\n *\n * 조용히 채우면 나중에 \"왜 이 수치인가\" 를 아무도 답할 수 없다. 막을 일은 아니다(기본값은 템플릿이\n * 확신을 갖고 선언한 값이다) — 다만 밝힌다.\n */\nexport const knobDefaultsFilled = (keys: readonly string[]): IngestWarning => ({\n code: 'knob-defaults-filled',\n params: { keys: keys.join(', '), count: keys.length },\n message: `values you did not provide were filled from the template defaults: ${keys.join(', ')}`\n})\n\n/**\n * 이 현장의 설비를 노브 값으로 채웠다 — **조용히 채우지 않는다.**\n *\n * 에너지 트윈의 부하를 이웃 설비 id 로 선언할 때 쓴다. 몇 개를 가져왔는지 말해 주지 않으면, 사용자는\n * 자기가 고르지 않은 설비들이 부하로 잡힌 것을 나중에 화면에서야 알게 된다.\n */\nexport const siteEquipmentAdopted = (key: string, count: number): IngestWarning => ({\n code: 'site-equipment-adopted',\n params: { key, count },\n message: `${count} piece(s) of equipment already on this site were adopted as loads (knob \"${key}\") — set their coefficients before trusting the numbers`\n})\n\nexport function masterToTwin(master: ReferenceMaster, catalog?: any): IngestedTwin {\n /* 커넥터가 옮기며 알게 된 것을 먼저 싣는다 — 원인이 앞에 오는 편이 읽기 쉽다(대체가 먼저, 그 결과가 뒤). */\n const warnings: IngestWarning[] = [...(master.warnings ?? [])]\n /*\n * 원본이 자기 어휘를 선언했으면 **그것에 대조한다.** 내장 카탈로그는 그 선언이 없을 때의 기본이다 —\n * 상대를 바꾸지 않으면, 자기 어휘를 선언한 원본이 정상적인 자리마다 경고를 받는다.\n */\n const known: string[] = declaredLocationTypes(master.productionSpec) ?? catalog?.[master.system]?.locationTypes ?? []\n\n const locationEntries = master.locations.filter(l => l.role === 'location' || l.role === 'both')\n const areaLocs = master.locations.filter(l => l.role === 'area' || l.role === 'both')\n\n const locations = locationEntries.map(l => {\n if (known.length && !known.includes(l.type)) warnings.push(unknownLocationType(master.system, l.type, l.id))\n // gs1Id(정식 정체성)를 모델의 자리로 통과 — 결합(tag)/추적이 GS1 로(§J). 로컬 id 는 그대로 유지(name/xref).\n /*\n * **이름을 통과시킨다.** 여기서 버리고 있었다 — 그래서 모델에도, 투영한 행에도 이름이 없고\n * 화면은 자리 id 를 그대로 찍었다(`cut-station`). 목록·표·관계 그림에서 같은 결함이 따로\n * 보였던 이유가 이 한 줄이다. 이름 없는 원본은 이름 없이 통과한다(id 로 메우지 않는다).\n */\n /*\n * **속성도 통과시킨다** — 설비와 같은 규율(손대지 않고 나른다). 여기가 없으면 자리에 실은 값이\n * 커널에 닿지 않는다: 에너지의 계약전력(`contract.kW`)이 그 경로이고, 통과하지 않으면 커널이\n * 계약을 모르는 채로 서서 **판정을 아예 하지 않는다**(오류 없이 조용히).\n */\n return { id: l.id, type: l.type, ...(l.name ? { name: l.name } : {}), ...(l.description ? { description: l.description } : {}), capacity: l.capacity ?? 0, ...(l.parallelism !== undefined ? { parallelism: l.parallelism } : {}), ...(l.level ? { level: l.level } : {}), ...(l.properties?.length ? { properties: l.properties } : {}), parentId: l.parentId, ...(l.upstreamId ? { upstreamId: l.upstreamId } : {}), gs1Id: l.gs1Id }\n })\n\n const equipment = (master.equipment ?? []).map(e => ({\n id: e.id, kind: e.kind, ...(e.name ? { name: e.name } : {}), ...(e.description ? { description: e.description } : {}), ...(e.testResults?.length ? { testResults: e.testResults } : {}),\n homeLocation: e.homeLocationId, mtbfMs: e.mtbfMs, mttrMs: e.mttrMs, gs1Id: e.gs1Id,\n ...(e.window ? { window: e.window } : {}),\n /* 속성은 손대지 않고 통과 — 이동시간 추정기가 kind 별 속도를 여기서 읽는다(단위 코드 그대로). */\n ...(e.properties?.length ? { properties: e.properties } : {}), ...(e.workCalendar?.length ? { workCalendar: e.workCalendar } : {}), ...(e.testSpecificationIds?.length ? { testSpecificationIds: e.testSpecificationIds } : {}),\n /* 유효 기간도 손대지 않고 통과 — 판정은 커널이 자기 시계로 한다(호스트가 미리 자르지 않는다). */\n ...(e.effectiveStart ? { effectiveStart: e.effectiveStart } : {}), ...(e.effectiveEnd ? { effectiveEnd: e.effectiveEnd } : {})\n }))\n\n // 좌표(layout) — footprint 중심. 없으면 생략(coordinate-free 유지).\n const layout: Record<string, { x: number; y: number }> = {}\n for (const l of locationEntries) if (l.footprint) layout[l.id] = { x: l.footprint.x + l.footprint.w / 2, y: l.footprint.y + l.footprint.h / 2 }\n\n const spaceId = master.space.spaceId ?? `${master.source}-site`\n /*\n * 시각 기준을 커널이 읽을 형태로 푼다 — 커널은 zero-dep 이라 시간대 데이터베이스를 들이지 않고\n * **분 오프셋**만 받는다(계약 `TwinModelDef.utcOffsetMinutes`). 푸는 일은 호스트의 몫이다.\n *\n * 선언이 없으면 **필드를 만들지 않는다** — 그러면 커널이 UTC 로 읽고, 그 기본값은 계약에 밝혀져 있다.\n * 여기서 0 을 넣으면 \"UTC 라고 선언했다\" 와 \"선언하지 않았다\" 가 구별되지 않는다.\n */\n const utcOffsetMinutes = utcOffsetOf(master.space.timezone)\n if (master.space.timezone && utcOffsetMinutes === undefined) {\n warnings.push(unknownTimezone(String(master.space.timezone)))\n }\n /*\n * 오퍼레이션 명세를 model 에 통과시킨다 — 커널이 `loadOperations` 로 소비한다(TwinModelDef 확장 필드,\n * 모르는 커널은 무시). 여기서 값을 손대지 않는다: 기간은 표준 표기(ISO 8601)로 커널까지 그대로 간다.\n */\n const operations = master.operations\n if (operations?.length) {\n for (const o of operations) {\n if (!o.duration && !o.parameters?.length) warnings.push(operationWithoutDuration(o.key))\n }\n }\n /* 사람도 손대지 않고 통과 — 등급·교대 그대로 커널까지 간다(경계에서 어휘를 갈지 않는다). */\n const persons = master.persons?.map(p => ({ id: p.id, ...(p.name ? { name: p.name } : {}), ...(p.testResults?.length ? { testResults: p.testResults } : {}), personnelClassIds: p.personnelClassIds, ...(p.window ? { window: p.window } : {}), ...(p.workCalendar?.length ? { workCalendar: p.workCalendar } : {}), ...(p.homeLocationId ? { homeLocation: p.homeLocationId } : {}), ...(p.properties?.length ? { properties: p.properties } : {}), ...(p.testSpecificationIds?.length ? { testSpecificationIds: p.testSpecificationIds } : {}), ...(p.effectiveStart ? { effectiveStart: p.effectiveStart } : {}), ...(p.effectiveEnd ? { effectiveEnd: p.effectiveEnd } : {}) }))\n /* 자산도 손대지 않고 통과 — 등급·자리 그대로 커널까지. */\n const assets = master.assets?.map(a => ({ id: a.id, ...(a.name ? { name: a.name } : {}), ...(a.testResults?.length ? { testResults: a.testResults } : {}), assetClassIds: a.assetClassIds, homeLocation: a.homeLocationId, gs1Id: a.gs1Id, ...(a.properties?.length ? { properties: a.properties } : {}), ...(a.testSpecificationIds?.length ? { testSpecificationIds: a.testSpecificationIds } : {}), ...(a.effectiveStart ? { effectiveStart: a.effectiveStart } : {}), ...(a.effectiveEnd ? { effectiveEnd: a.effectiveEnd } : {}) }))\n /* 품목 정의·등급도 손대지 않고 통과 — 경계에서 어휘를 갈지 않는다(환산 계수는 커널이 읽는다). */\n const materialDefinitions = master.materialDefinitions?.map(d => ({\n id: d.id,\n /* 이름은 이름 자리에 — 예전에는 `name` 을 `description` 으로 옮겨 담았다(어휘가 뒤집혀 있었다). */\n ...(d.name ? { name: d.name } : {}),\n ...(d.materialClassIds?.length ? { materialClassIds: d.materialClassIds } : {}),\n ...(d.properties?.length ? { properties: d.properties } : {}),\n ...(d.testSpecificationIds?.length ? { testSpecificationIds: d.testSpecificationIds } : {}),\n ...(d.effectiveStart ? { effectiveStart: d.effectiveStart } : {}),\n ...(d.effectiveEnd ? { effectiveEnd: d.effectiveEnd } : {})\n }))\n /*\n * **등급의 표시명은 `name` 이다.**\n *\n * 표준 마스터는 등급의 표시명을 `description` 에 담는다(`RefResourceClass.description` = i18n 키).\n * 그것을 그대로 모델에 실으면 화면의 \"설명\" 열에 이름이 앉는다 — 사용자에게는 이름인데 머리글은\n * 설명이라, 같은 것을 축마다 다른 이름으로 부르게 된다.\n *\n * 그래서 **문 하나에서 어휘를 맞춘다**: 표시명은 어느 축이든 `name`. 원래의 `description` 은\n * 남겨 두지 않는다 — 두 자리에 같은 값이 있으면 어느 것이 정본인지 다시 헷갈린다.\n */\n const asName = (list?: RefResourceClass[]) =>\n list?.map(({ description, ...rest }) => ({ ...rest, ...(description ? { name: description } : {}) }))\n /*\n * 검사 명세도 **표시명은 `name`** 이다 — 등급과 같은 규칙을 쓴다(어휘를 하나로). 표준 마스터는\n * 표시명을 `description` 에 담으므로 문 하나에서 옮긴다. 그러지 않으면 화면의 \"설명\" 열에 이름이\n * 앉아 축마다 다른 이름으로 같은 것을 부르게 된다(등급에서 이미 겪었다).\n */\n const testSpecifications = master.testSpecifications?.map(({ description, ...rest }) => ({\n ...rest,\n ...(description ? { name: description } : {})\n }))\n const personnelClasses = asName(master.personnelClasses)\n const equipmentClasses = asName(master.equipmentClasses)\n const assetClasses = asName(master.assetClasses)\n const materialClasses = asName(master.materialClasses)\n\n const model = {\n locations, equipment, layout, spaceId,\n /* 생산 정의도 손대지 않고 통과 — 경계에서 어휘를 갈지 않는다(커널이 그대로 소비한다). */\n ...(master.productionSpec ? { productionSpec: master.productionSpec } : {}),\n /* 정체성 선언도 그대로 통과한다 — 없으면 만들지 않는다(커널이 레거시 상수로 떨어진다). */\n ...(master.identity ? { identity: master.identity } : {}),\n ...(materialDefinitions?.length ? { materialDefinitions } : {}),\n ...(materialClasses?.length ? { materialClasses } : {}),\n ...(utcOffsetMinutes !== undefined ? { utcOffsetMinutes } : {}),\n ...(operations?.length ? { operations } : {}),\n ...(testSpecifications?.length ? { testSpecifications } : {}),\n ...(personnelClasses?.length ? { personnelClasses } : {}),\n ...(equipmentClasses?.length ? { equipmentClasses } : {}),\n ...(assetClasses?.length ? { assetClasses } : {}),\n ...(persons?.length ? { persons } : {}),\n ...(assets?.length ? { assets } : {})\n }\n\n const areas = areaLocs.map(l => ({\n id: l.id, type: l.type, name: l.name ?? l.id, parentId: l.parentId, ...(l.upstreamId ? { upstreamId: l.upstreamId } : {}),\n x: l.footprint?.x ?? 0, y: l.footprint?.y ?? 0, w: l.footprint?.w ?? 10, h: l.footprint?.h ?? 10\n }))\n // 인제스트 현장 물질화(space facet #4) — 표현이 없고 geo 가 있으면 기본 map 표현 합성.\n // \"인제스트하면 알아볼 현장(내 place)이 뜬다\"는 공간 중심 정수 보장(zero-to-twin): 커넥터/템플릿이\n // 표현을 안 줘도 geo 만 있으면 위성 지도로 착지. (area 폴리곤 물질화는 좌표 투영 필요 → space #3.)\n const geo = master.space.geo\n const givenReps = master.space.representations ?? []\n const representations =\n givenReps.length > 0\n ? givenReps\n : geo\n ? [{ id: 'r-map', type: 'map', name: '위성 지도', primary: true, center: { lat: geo.lat, lon: geo.lon }, zoom: 17 }]\n : []\n if (representations.length === 0) {\n warnings.push(noViewableRepresentation())\n }\n const spaceContent = {\n width: master.space.width, depth: master.space.depth, unit: master.space.unit ?? 'm',\n geo, representations, primaryId: master.space.primaryId ?? representations[0]?.id,\n areas\n }\n\n return { model, spaceContent, warnings }\n}\n\n/**\n * 다시 읽을 사이트를 고른다 — **기억해 둔 사이트 하나만**.\n *\n * 못 찾으면 **첫 사이트로 때우지 않는다**: 다른 현장의 구조를 이 트윈에 덮어쓰는 일이고, 그것은 오류가\n * 아니라 **조용히 틀린 공장**으로 나타난다. 기억이 없는 옛 트윈은 `undefined` 를 돌려주고, 부르는 쪽이\n * 마스터의 정체(`source` = 인스턴스 id)로 찾는 길을 택한다.\n */\nexport function pickOriginSite<T extends { siteId: string }>(\n sites: readonly T[],\n origin: { siteId?: string } | undefined | null\n): { site?: T; reason?: 'no-remembered-site' | 'site-gone' } {\n const wanted = String(origin?.siteId ?? '').trim()\n if (!wanted) return { reason: 'no-remembered-site' }\n const site = sites.find(s => String(s.siteId) === wanted)\n return site ? { site } : { reason: 'site-gone' }\n}\n"]}
|
|
@@ -315,7 +315,30 @@ let TwinForecastQuery = class TwinForecastQuery {
|
|
|
315
315
|
calibrated: !!cal,
|
|
316
316
|
/* 학습해 뒀지만 모델이 바뀌어 적용하지 않았다 — 사용자가 다시 배울 수 있게 알린다. */
|
|
317
317
|
...(stale ? { calibrationStale: true } : {}),
|
|
318
|
-
...(specCoverage ? { specCoverage } : {})
|
|
318
|
+
...(specCoverage ? { specCoverage } : {}),
|
|
319
|
+
/*
|
|
320
|
+
* **계보에 구멍이 몇 칸 생겼나** — 개체를 잇지 못한 채 넘어간 공정 수.
|
|
321
|
+
*
|
|
322
|
+
* ── 왜 이 값을 예측 응답에 싣나 (2026-08-24) ──────────────────────────────
|
|
323
|
+
* 미러의 오더는 확보분을 갖지 않는다(원본이 「어느 개체가 잡혀 있나」를 말하지 않는 것이 그
|
|
324
|
+
* 시스템의 정상이다). 커널은 그 자리에서 **던지지 않고 넘긴다** — 단계를 넘는 것은 공정의
|
|
325
|
+
* 진행이고 개체 번호가 필요 없다. 대신 넘긴 횟수를 센다.
|
|
326
|
+
*
|
|
327
|
+
* 그런데 그 카운터는 **fork 안에** 있고 fork 는 버려진다. 여기서 싣지 않으면 아무에게도 닿지
|
|
328
|
+
* 않는다 — 리듀서가 오래 세어 온 값을 스냅샷이 떨어뜨려 그것을 읽도록 쓰인 검사가 영원히
|
|
329
|
+
* 조용했던 것과 **같은 실수**다. 세어 놓고 내보내지 않으면 안 센 것과 같다.
|
|
330
|
+
*
|
|
331
|
+
* 왜 화면이 이것을 알아야 하나: 계보는 **회수 범위**를 정하는 사슬이다. 구멍이 있는데 온전한
|
|
332
|
+
* 것처럼 보이면 회수가 조용히 좁아진다. 식품에서 그것은 되돌릴 수 없다.
|
|
333
|
+
*
|
|
334
|
+
* 두 곳에서 읽어 **큰 값**을 낸다 — 분포(회차 R개의 최대)와 궤적 fork 는 **둘 다 실제로 실행됐다**.
|
|
335
|
+
* 한쪽만 보면 다른 쪽이 겪은 구멍을 잃는다.
|
|
336
|
+
*/
|
|
337
|
+
...(() => {
|
|
338
|
+
const gaps = Math.max(distribution?.stepsWithoutMaterial ?? 0, fc?.getSnapshot?.()?.stepsWithoutMaterial ?? 0);
|
|
339
|
+
/* 0 은 싣지 않는다 — 「구멍이 없다」와 「이 축을 모른다」를 화면이 구별할 수 있게. */
|
|
340
|
+
return gaps > 0 ? { stepsWithoutMaterial: gaps } : {};
|
|
341
|
+
})()
|
|
319
342
|
};
|
|
320
343
|
}
|
|
321
344
|
/*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twin-forecast-query.js","sourceRoot":"","sources":["../../../server/service/twin-forecast/twin-forecast-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { twinLog } from '../../engine/log.js'\nimport { Arg, Ctx, Int, Mutation, Query, Resolver, Directive} from 'type-graphql'\n\nimport { getRepository, ScalarObject } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\nimport { readSpecCoverage } from '../../engine/spec-coverage.js'\nimport { describeModelBasis, modelBasisChanged, type ModelBasis } from '../../engine/model-basis.js'\nimport { TwinEvent } from '../twin-event/twin-event.js'\nimport { GAP_SAMPLES, aggregatePoints, computeSpc, gapMetrics, learnedCalibration, type GapPoint } from './gap-analytics.js'\nimport { FORECAST_METRICS, type MetricFn } from './forecast-metrics.js'\nimport { forecastStepMs } from './forecast-tuning.js'\n\n/* 커널 예측 함수 — CJS require(twin-engine 과 동일 방식). */\nconst { monteCarloForecastAsync } = require('@operato/twin-kernel')\n\n/**\n * 이벤트 루프에 자리를 내주는 방법 — **커널이 아니라 여기가 안다.**\n *\n * `setImmediate` 는 대기 중인 I/O 다음 차례에 돌아온다. 마이크로태스크(`Promise.resolve()`)로는\n * 자리가 가지 않는다 — 그러면 예측이 도는 동안 라이브 틱도 HTTP 도 여전히 멈춘다.\n *\n * ── 내준 시간은 **부하가 아니다** ───────────────────────────────────────────\n * 부하 계기는 「이 트윈이 루프를 얼마나 붙잡았나」를 재는 자다. 양보를 넣은 뒤로 벽시계 시간에는\n * 남에게 넘긴 시간이 섞이는데, 그것까지 부하로 적으면 계기가 거짓말을 한다 — 하필 이 문제를 짚어낸\n * 그 계기다. 그래서 내준 시간을 따로 세어 빼고 적는다.\n */\nfunction breather() {\n let yieldedMs = 0\n return {\n fn: () =>\n new Promise<void>(resolve => {\n const t = performance.now()\n setImmediate(() => {\n yieldedMs += performance.now() - t\n resolve()\n })\n }),\n get ms() {\n return yieldedMs\n }\n }\n}\n\n/*\n * 트윈 예측 채널 — 상향 query. 트윈의 \"예측(predict)\" 면(설계 §3.5, ADR-0009).\n * 커널 monteCarloForecast(현재에서 N fork → 지표 분포) + 단일 fork 예측 궤적. 원본 live 무간섭(fork 격리).\n * metric 은 도메인-일반(스냅샷 파생), scenario 는 미래 자극(기본=계속 입고, 실 시스템은 인스턴스/설정 파생).\n */\nconst GTIN = 'urn:epc:idpat:sgtin:0614141.012345.*'\nconst DEFAULT_SCENARIO = {\n seed: 7,\n generators: [\n { kind: 'inbound-arrival', rate: { distribution: 'poisson', meanPerHour: 60 }, content: { skuMix: [{ gtin: GTIN, weight: 1 }], qtyPerLine: { min: 4, max: 12 } } }\n ]\n}\n/*\n * 지표 표는 `forecast-metrics.ts` 가 소유한다 — 여기 있던 것을 옮겼다(시험이 이 파일을 불러올 수 없다).\n *\n * **모르는 지표를 `items` 로 갈음하지 않는다.** 예전에는 `METRICS[metric] ?? METRICS.items` 였다.\n * 에너지 트윈에서 `peakKW` 를 물으면 재고 곡선이 그려지고, 재고가 없으니 **0 이 평평하게** 나왔다 —\n * 화면은 그것을 「피크가 0」으로 읽는다. 지어낸 답보다 모른다고 말하는 편이 낫다.\n */\nfunction metricFn(metric?: string): MetricFn | null {\n if (!metric) return FORECAST_METRICS.items\n return FORECAST_METRICS[metric] ?? null\n}\n\n/* 학습된 보정 저장(P3.2, 인메모리) — key=domain:instance:metric:horizon → {biasShift, halfWidth, …}.\n twinLearnCalibration 이 갭에서 학습해 저장하고, twinForecast 가 있으면 예측에 상시 적용(라이브 보정).\n 데모 트윈은 재부팅 시 저널 리셋이라 세션 스코프로 충분 — DB 영속(TwinForecastScore)은 후속. */\nconst CALIBRATION = new Map<\n string,\n { biasShift: number; halfWidth: number; at: string; samples: number; accBefore: number; accAfter: number; basis?: ModelBasis }\n>()\nconst calKey = (d: string, i: string, m: string, h: number) => `${d}:${i}:${m}:${h}`\n\n/**\n * 이 트윈이 백테스트할 수 있는 구간 — [첫 이벤트, 마지막 이벤트 − H].\n * 지평 H 만큼 앞을 봐야 \"그때 예측한 것\"과 \"실제로 벌어진 것\"을 맞대 볼 수 있으므로 끝에서 H 를 뺀다.\n */\nasync function gapWindow(domainId: string, instanceId: string, H: number): Promise<{ t0: number; lastVantage: number } | null> {\n const where = { domain: { id: domainId }, instanceId }\n const repo = getRepository(TwinEvent)\n const first = await repo.findOne({ where, order: { eventTime: 'ASC' } })\n const last = await repo.findOne({ where, order: { eventTime: 'DESC' } })\n if (!first?.eventTime || !last?.eventTime) return null\n const t0 = first.eventTime.getTime()\n const lastVantage = last.eventTime.getTime() - H\n return lastVantage > t0 ? { t0, lastVantage } : null\n}\n\n/**\n * 갭 point 계산(공용) — 주어진 vantage 각각에서 forecast-at-T vs actual-at-T+H(결정적 백테스트).\n *\n * **vantage 는 밖에서 준다.** 예전에는 이 함수가 자기 저널의 첫·마지막 이벤트로 격자를 스스로 만들었다.\n * 그러면 트윈마다 격자가 달라져, 공동배치 합산에서 `points[3]` 끼리 더할 때 **서로 다른 시각의 값**이\n * 더해졌다(자세한 사연은 `aggregatePoints` 주석).\n *\n * 반환 배열은 격자와 **같은 길이**이고, 그 vantage 를 재지 못했으면 그 자리에 `null` 을 둔다.\n * 건너뛰고 밀어 담으면 배열 인덱스가 시간축이기를 그만둔다.\n */\nasync function gapPoints(domainId: string, instanceId: string, m: (s: any) => number, H: number, grid: number[], scen: any): Promise<(any | null)[]> {\n const points: (any | null)[] = []\n for (const T of grid) {\n const k = await TwinEngine.buildForecastKernelAt(domainId, instanceId, new Date(T).toISOString()).catch(() => null)\n if (!k) { points.push(null); continue }\n const dist = await monteCarloForecastAsync(k, { runs: 12, horizonMs: H, scenario: scen, tickMs: forecastStepMs(H), metric: m, yieldFn: breather().fn })\n const naive = m(k.getSnapshot())\n const st = await TwinEngine.recover(domainId, instanceId, undefined, new Date(T + H).toISOString()).catch(() => null)\n if (!st) { points.push(null); continue }\n const a = m(st)\n const p50 = dist?.p50 ?? naive\n points.push({ t: T, p50, min: dist?.min ?? p50, max: dist?.max ?? p50, actual: a, error: a - p50, inBand: a >= (dist?.min ?? -Infinity) && a <= (dist?.max ?? Infinity), naiveAbsError: Math.abs(a - naive), modelAbsError: Math.abs(a - p50) })\n }\n return points\n}\n\n/**\n * 여러 트윈이 **함께** 백테스트할 수 있는 구간과 그 위의 vantage 격자.\n *\n * 겹치는 구간은 각자의 구간을 교집합한 것 — 시작은 가장 늦은 t0, 끝은 가장 이른 lastVantage.\n * 한 트윈이라도 그 시각에 저널이 없으면 공장 총량을 말할 수 없기 때문이다.\n * 겹치는 구간이 없으면 `null` — \"0\" 이 아니라 \"잴 수 없음\" 이다.\n */\nfunction sharedGrid(windows: { t0: number; lastVantage: number }[], N: number): number[] | null {\n if (!windows.length || N < 2) return null\n const t0 = Math.max(...windows.map(w => w.t0))\n const lastVantage = Math.min(...windows.map(w => w.lastVantage))\n if (!(lastVantage > t0)) return null\n return Array.from({ length: N }, (_, i) => t0 + ((lastVantage - t0) * i) / (N - 1))\n}\n\n@Resolver()\nexport class TwinForecastQuery {\n @Query(returns => ScalarObject, {\n nullable: true,\n description: 'Monte-Carlo forecast of a metric over a horizon (twin prediction faculty) + a single-fork predicted trajectory. Live instance is untouched (forks are isolated).'\n })\n async twinForecast(\n @Arg('instanceId') instanceId: string,\n @Arg('metric', { nullable: true }) metric: string,\n @Arg('runs', type => Int, { nullable: true }) runs: number,\n @Arg('horizonMs', type => Int, { nullable: true }) horizonMs: number,\n @Arg('scenario', type => ScalarObject, { nullable: true }) scenario: any,\n @Arg('at', { nullable: true }) at: string,\n @Ctx() context: ResolverContext\n ): Promise<any> {\n /* vantage(at) 기준 예측 — 히스토리 노브가 과거 T 면 \"그때 서서 본 미래\"(백테스트).\n at 있으면 T 시점 커널을 저널로 재구성한다(그건 우회가 아니라 시간여행의 본질).\n 없으면 **지금 그 커널**을 쓴다 — sim 이든 live 든 인스턴스가 커널이다(통합 P2). */\n const domainId = context.state.domain.id\n const k = at\n ? await TwinEngine.buildForecastKernelAt(domainId, instanceId, at)\n : TwinEngine.owns(domainId, instanceId)\n ? TwinEngine.kernel(domainId, instanceId)\n : undefined\n if (!k) return null\n\n const m = metricFn(metric)\n /* 이 트윈이 못 세는 지표 — 빈 예측이 아니라 「그 지표는 여기 없다」고 말한다. */\n if (!m) return { metric, unsupportedMetric: true, distribution: null, trajectory: [] }\n const H = horizonMs ?? 300000\n const R = runs ?? 30\n /*\n * 틱 크기는 **지평선이 정한다**(`forecast-tuning`). 1초 고정이던 시절 5분 지평선이 회차마다\n * 300틱이었고, 30회차면 9,000틱·28.5초였다 — 실측으로 5초 단위와 답이 같다.\n */\n const step = forecastStepMs(H)\n const scen = scenario ?? DEFAULT_SCENARIO\n\n /*\n * 분포: N개 확률적 미래 표본(fork 마다 seed 변주, 원본 보존).\n *\n * **이것이 요청 처리 중 메인 이벤트 루프에서 동기로 수행된다.** 기본값으로 fork 30개 × 300틱 = 9,000틱.\n * 계기판에 남겨 두지 않으면 \"왜 서버가 굳었나\" 를 나중에 짚을 수 없다(실제로 한 번 겪었다).\n */\n const tMc = performance.now()\n const mcBreath = breather()\n const distribution = await monteCarloForecastAsync(k, { runs: R, horizonMs: H, scenario: scen, tickMs: step, metric: m, yieldFn: mcBreath.fn })\n TwinEngine.recordLoad(domainId, instanceId, 'forecast', performance.now() - tMc - mcBreath.ms)\n TwinEngine.countForks(domainId, instanceId, R)\n\n /* 학습된 보정 상시 적용(P3.2) — twinLearnCalibration 이 저장한 편향·밴드가 있으면 예측을 현실로 보정(라이브가 실제로 나아짐).\n 갭트렌드(gapPoints)는 raw 모델을 그대로 측정(학습 신호 보존) — 보정은 표시용 forecast 에만. */\n const stored = CALIBRATION.get(calKey(domainId, instanceId, metric ?? 'items', H))\n /*\n * **배운 모델에만 적용한다.** 보정은 \"그때 모델이 이만큼 치우쳐 있다\" 를 배운 것이다. 소요시간\n * 추정기(실측·거리)나 공정 명세가 바뀌어 모델이 달라지면 그 편향은 더 이상 사실이 아니고, 그대로\n * 밀어 주면 이중 보정이 된다. 그래서 지문을 비교하고, 다르면 적용하지 않되 **그 사실을 응답에\n * 싣는다** — 조용히 사라지면 사용자는 학습이 왜 안 듣는지 알 수 없다(다시 배우면 된다).\n * 지문을 알 수 없을 때는 비교하지 않는다(모르는 것을 근거로 보정을 끊지 않는다).\n */\n const basis = await TwinEngine.modelBasis(domainId, instanceId)\n const stale = !!stored && modelBasisChanged(stored.basis, basis)\n if (stale) {\n twinLog(\n `[twin-forecast] \"${instanceId}\": calibration not applied — model changed since it was learned.` +\n ` learned on: ${describeModelBasis(stored!.basis)} / now: ${describeModelBasis(basis)}`\n )\n }\n /*\n * 분위수 — 표본에서 직접 뽑는다.\n *\n * 커널이 표본 배열을 그대로 돌려주므로(forecast.ts) 여기서 정렬해 p05·p95 를 낸다. 이것이 없으면\n * 화면이 보여 줄 수 있는 폭은 관측 **최소·최대** 뿐인데, 그건 신뢰구간이 아니라 극단값 두 개다.\n * \"운이 나쁜 5% 에서는 이만큼\" 이라고 말하려면 분위수여야 한다. 표본이 적으면(runs 가 작으면)\n * 분위수도 거칠다 — 그래서 표본 수를 함께 싣는다(sampleCount). 숫자만 주고 근거를 감추지 않는다.\n */\n if (distribution) {\n const samples: number[] = Array.isArray(distribution.samples) ? distribution.samples : []\n if (samples.length) {\n const sorted = [...samples].sort((a, b) => a - b)\n const pct = (p: number) => sorted[Math.min(sorted.length - 1, Math.floor(p * sorted.length))]\n distribution.p05 = pct(0.05)\n distribution.p95 = pct(0.95)\n }\n distribution.sampleCount = samples.length\n }\n\n const cal = stored && !stale ? stored : undefined\n if (cal && distribution) {\n /*\n * `p50` 은 커널 계약상 항상 있다(`MonteCarloResult.p50: number`). 그래도 메우지 않는다 —\n * 예전에는 `?? 0` 이었는데, 만약 비면 보정된 p50 이 **편향값 그 자체**가 되고 min·max 는\n * 그 둘레로 그려진다. 예측이 아닌 숫자가 예측의 자리에 앉는데 아무 신호가 없다.\n */\n if (typeof distribution.p50 !== 'number') throw new Error('forecast distribution has no p50 — kernel contract broken')\n const p50c = distribution.p50 + cal.biasShift\n distribution.p50 = Math.round(p50c)\n distribution.min = Math.round(p50c - cal.halfWidth)\n distribution.max = Math.round(p50c + cal.halfWidth)\n /*\n * 분위수는 **옮기기만** 한다. 보정이 배운 것은 \"모델이 이만큼 치우쳐 있다\"(위치)이지 분포의\n * 모양이 아니다. p50 처럼 밀어 두어야 p05~p95 폭이 표본이 말한 그대로 남는다.\n * (min·max 는 위에서 학습 밴드로 대체된다 — 보정이 걸리면 그 둘의 뜻이 '관측 극단' 에서\n * '학습 밴드' 로 바뀐다는 뜻이다. 화면이 둘을 같은 것으로 그리면 안 된다.)\n */\n if (distribution.p05 !== undefined) distribution.p05 = Math.round(distribution.p05 + cal.biasShift)\n if (distribution.p95 !== undefined) distribution.p95 = Math.round(distribution.p95 + cal.biasShift)\n if (distribution.p90 !== undefined) distribution.p90 = Math.round(distribution.p90 + cal.biasShift)\n distribution.calibrated = true\n }\n\n /*\n * 예측 궤적(예측선): 단일 fork 를 horizon 까지 실행하며 지표 기록. 이것도 잰다.\n *\n * 여기도 자리를 내준다 — 실측으로 이 한 회차가 1.3초였다(300틱 + 스냅샷 300번). 분포만큼은\n * 아니지만, 라이브 틱 간격(1초)보다 길게 붙잡으면 그 사이 트윈들이 한 박자씩 밀린다.\n */\n const tTraj = performance.now()\n const trajBreath = breather()\n const fc = k.fork()\n fc.scenario.load({ ...scen, seed: 1 })\n fc.scenario.start()\n const trajectory: { t: number; value: number }[] = []\n for (let t = 0; t < H; t += step) {\n fc.tick(step)\n trajectory.push({ t: t + step, value: m(fc.getSnapshot()) })\n /* 틱마다 내주면 양보 자체가 비용이 된다 — 라이브 틱 간격보다 훨씬 짧은 단위로 끊는다. */\n if (trajectory.length % 25 === 0) await trajBreath.fn()\n }\n\n TwinEngine.recordLoad(domainId, instanceId, 'forkTick', performance.now() - tTraj - trajBreath.ms)\n TwinEngine.countForks(domainId, instanceId, 1)\n\n const current = m(k.getSnapshot()) // vantage T 의 실제값(= naive '그대로 유지' 예측의 기준)\n\n /* 실측 오버레이 + 채점(P1) — vantage 가 과거(at)일 때만: T 이후 실제로 일어난 metric 을 T→min(now,T+H) 로 샘플.\n 현재 이후는 실측이 없음(미래). recover(T+t) 로 각 샘플 상태 재구성해 metric 추출. 비용=샘플 수 만큼 replay. */\n const actual: { t: number; value: number }[] = []\n let scored: any = null\n if (at) {\n const T = Date.parse(at)\n const endMs = Math.min(Date.now(), T + H)\n const span = endMs - T\n if (Number.isFinite(T) && span > 0) {\n const SAMPLES = 8\n const dt = span / SAMPLES\n for (let i = 1; i <= SAMPLES; i++) {\n const st = await TwinEngine.recover(domainId, instanceId, undefined, new Date(T + i * dt).toISOString()).catch(() => null)\n if (st) actual.push({ t: i * dt, value: m(st) })\n }\n /* 지평선이 지났으면(T+H ≤ now) 그 지점 실측 vs 예측 채점(단일 vantage). p50 대비 오차·밴드 적중·FVA(naive=현재값 유지 대비). */\n if (T + H <= Date.now() && actual.length) {\n const a = actual[actual.length - 1].value\n const p50 = distribution?.p50 ?? current\n scored = {\n actual: a,\n p50,\n error: a - p50, // 부호 = 편향 방향(+ 실제가 더 많음)\n inBand: a >= (distribution?.min ?? -Infinity) && a <= (distribution?.max ?? Infinity),\n modelAbsError: Math.abs(a - p50),\n naiveAbsError: Math.abs(a - current) // naive = \"그대로 유지\"(현재값). FVA = naive - model\n }\n }\n }\n }\n\n /*\n * 시뮬 명세 자기보고를 함께 낸다 — **상수로 계산한 예측과 현장 명세로 계산한 예측이 화면에서 같아\n * 보이지 않게.** 코드·수치만 싣고 사람이 읽는 문장은 클라가 만든다(언어 중립).\n * 커널이 이 API 를 갖지 않은 버전이면 키를 만들지 않는다(모르는 것을 \"전부 기본값\" 이라 단정 금지).\n */\n /* 보고는 **실제로 실행한 fork** 에서 읽는다 — 부모 커널(k)은 tick 하지 않으므로(예측은 fork 에서\n * 수행된다) 부모에서 읽으면 씨앗으로 세운 예측에서는 늘 비어 있다. 숫자를 낸 주체가 보고해야 한다. */\n const specCoverage = readSpecCoverage(fc) ?? readSpecCoverage(k)\n return {\n metric: metric ?? 'items', horizonMs: H, runs: R, current, distribution, trajectory, actual, scored,\n /* 얼마나 잘게 굴렸나 — 숫자만 주고 근거를 감추지 않는다(같은 규율로 `specCoverage` 도 싣는다). */\n tickMs: step,\n /*\n * **가정이 어떻게 됐나** — 걸린 개입·거절된 개입·덮어쓴 선언.\n *\n * 이것을 감추면 두 가지가 조용해진다: 걸리지 않은 가정(「걸었는데 왜 안 바뀌나」)과, 덮어쓴\n * 선언(사용자가 what-if 결과를 트윈의 현재 상태로 읽는다). 실제로 실행한 fork(`fc`)에서 읽는다 —\n * 가정을 겪은 것은 그쪽이다.\n */\n ...(typeof (fc as any).interventionOutcomes === 'function'\n ? (() => {\n const outcomes = (fc as any).interventionOutcomes()\n return outcomes.length ? { interventions: outcomes } : {}\n })()\n : {}),\n ...(typeof (fc as any).declarationOverrides === 'function'\n ? (() => {\n const overrides = (fc as any).declarationOverrides()\n return overrides.length ? { overrides } : {}\n })()\n : {}),\n calibrated: !!cal,\n /* 학습해 뒀지만 모델이 바뀌어 적용하지 않았다 — 사용자가 다시 배울 수 있게 알린다. */\n ...(stale ? { calibrationStale: true } : {}),\n ...(specCoverage ? { specCoverage } : {})\n }\n }\n\n /*\n * 갭 트렌드(P2) — N개 과거 vantage 각각에서 \"그때 예측 vs 그 지평선의 실제\"를 재계산(결정적 백테스트).\n * 지평선이 이미 지난 vantage 만([t0, tLast−H]) → actual 확정. 온디맨드 재계산(누적/영속은 긴 기간용 후속 최적화).\n * 비용 = N × (buildForecastKernelAt replay + monteCarlo + recover replay). N·runs 를 낮게 bound. per-instance(클라가 co-located 합산).\n */\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'Forecast-accuracy gap diagnosis (canonical, server-computed): for N past vantages recompute forecast-at-T vs actual-at-T+h (deterministic backtest), aggregate co-located instances, and return points + SPC verdict (stability/bias) + metrics (accuracy/coverage/FVA) + learned-calibration projection. Client only renders.'\n })\n async twinForecastGapTrend(\n @Arg('instanceId') instanceId: string,\n @Arg('metric', { nullable: true }) metric: string,\n @Arg('horizonMs', type => Int, { nullable: true }) horizonMs: number,\n @Arg('samples', type => Int, { nullable: true }) samples: number,\n @Arg('scenario', type => ScalarObject, { nullable: true }) scenario: any,\n @Ctx() context: ResolverContext,\n @Arg('instanceIds', type => [String], { nullable: true }) instanceIds?: string[]\n ): Promise<any> {\n const domainId = context.state.domain.id\n const m = metricFn(metric)\n const H = horizonMs ?? 300000\n if (!m) return { metric, unsupportedMetric: true, horizonMs: H, samples: 0, points: [], spc: computeSpc([]), metrics: null, learn: null }\n const N = Math.min(30, Math.max(2, samples ?? GAP_SAMPLES)) // 표본은 서버가 관장(SPC 신뢰 하한 ~20).\n const scen = scenario ?? DEFAULT_SCENARIO\n // 공동배치(co-located) 집계도 서버에서 — 공장 단위 진단이 canonical. 미지정 시 단건.\n const ids = instanceIds?.length ? instanceIds : [instanceId]\n\n /*\n * 격자를 **먼저 하나** 만든다. 트윈마다 자기 저널로 격자를 만들면 같은 인덱스가 서로 다른\n * 시각이 되어, 합산이 다른 시점의 값을 더한다(`aggregatePoints` 주석 참조).\n *\n * 저널이 없거나 지평 H 를 담을 만큼 길지 않은 트윈은 **백테스트에 참여할 수 없다.** 그런 트윈은\n * 빼되 누구를 뺐는지 돌려준다 — 조용히 빼면 화면은 그 트윈까지 포함한 공장 총량으로 읽는다.\n */\n const windows = await Promise.all(ids.map(async id => ({ id, w: await gapWindow(domainId, id, H) })))\n const usable = windows.filter(x => x.w)\n const excluded = windows.filter(x => !x.w).map(x => x.id)\n const grid = sharedGrid(usable.map(x => x.w!), N)\n if (!grid) {\n /* 겹치는 구간이 없다 — 잴 수 없는 것이지 정확도가 0 인 것이 아니다. */\n return { metric: metric ?? 'items', horizonMs: H, samples: N, points: [], spc: computeSpc([]), metrics: null, learn: null,\n coverage: { instances: ids.length, measured: 0, excluded, vantages: N, used: 0, dropped: 0, reason: 'no-common-window' } }\n }\n\n const per = await Promise.all(usable.map(x => gapPoints(domainId, x.id, m, H, grid, scen)))\n const { points, droppedVantages } = aggregatePoints(per as (GapPoint | null)[][])\n const metrics = points.length ? gapMetrics(points) : null\n const spc = computeSpc(points.map(p => p.error))\n const learn = metrics ? learnedCalibration(points, metrics) : null\n return { metric: metric ?? 'items', horizonMs: H, samples: N, points, spc, metrics, learn,\n /* 이 숫자가 **무엇으로 계산됐는지**. 24점 중 18점이면 화면이 그렇게 말해야 한다. */\n coverage: { instances: ids.length, measured: usable.length, excluded, vantages: N, used: points.length, dropped: droppedVantages } }\n }\n\n /*\n * 학습(P3.2) — 갭에서 편향·밴드를 학습해 저장하면, 이후 twinForecast 가 상시 적용(라이브 예측이 실제로 보정됨).\n * assimilation-lite: 잔차(innovation)를 되먹여 추정치를 현실로. per-instance(클라가 co-located 합산).\n * 반환: 학습된 보정 + before/after 정확도(효과 정량화). 저장은 인메모리(세션) — DB 영속은 후속.\n */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => ScalarObject, { description: 'Learn a forecast calibration (bias + band) from the gap trend and persist it so future forecasts apply it. Returns the calibration + before/after accuracy.' })\n async twinLearnCalibration(\n @Arg('instanceId') instanceId: string,\n @Arg('metric', { nullable: true }) metric: string,\n @Arg('horizonMs', type => Int, { nullable: true }) horizonMs: number,\n @Ctx() context: ResolverContext,\n @Arg('clear', type => Boolean, { nullable: true }) clear?: boolean\n ): Promise<any> {\n const domainId = context.state.domain.id\n if (!TwinEngine.owns(domainId, instanceId)) return { ok: false } // 테넌트 격리\n const m = metricFn(metric)\n if (!m) return { ok: false, reason: 'unsupported-metric', metric }\n const H = horizonMs ?? 300000\n // 보정 해제 — 저장된 calibration 제거(이후 예측은 raw 로 복귀). 학습을 되돌리는 정직한 off 스위치.\n if (clear) {\n CALIBRATION.delete(calKey(domainId, instanceId, metric ?? 'items', H))\n return { ok: true, cleared: true, metric: metric ?? 'items', horizonMs: H }\n }\n // 표본·계산을 gap-analytics 로 일원화(gapTrend 진단과 동일 N·동일 수식 → 카드 예상치와 실제 보정 일치).\n /* 학습은 단건이라 격자도 이 트윈의 구간 하나다. 저널이 지평 H 를 담을 만큼 길지 않으면 배울 것이 없다. */\n const w = await gapWindow(domainId, instanceId, H)\n const grid = w ? sharedGrid([w], GAP_SAMPLES) : null\n if (!grid) return { ok: false, reason: 'insufficient-history' }\n /* 못 잰 vantage 는 `null` 로 온다 — 0 으로 세면 편향 학습이 그만큼 끌려간다. */\n const pts = (await gapPoints(domainId, instanceId, m, H, grid, DEFAULT_SCENARIO)).filter(Boolean) as GapPoint[]\n if (pts.length < 3) return { ok: false, reason: 'insufficient-history' }\n const learn = learnedCalibration(pts, gapMetrics(pts))\n /* 학습한 모델의 지문을 함께 남긴다 — 이후 모델이 바뀌면 이 보정은 적용되지 않는다. */\n const basis = await TwinEngine.modelBasis(domainId, instanceId)\n const cal = {\n biasShift: learn.biasShift,\n halfWidth: learn.halfWidth,\n at: '',\n samples: pts.length,\n accBefore: learn.accBefore,\n accAfter: learn.accAfter,\n ...(basis ? { basis } : {})\n }\n CALIBRATION.set(calKey(domainId, instanceId, metric ?? 'items', H), cal)\n return { ok: true, metric: metric ?? 'items', horizonMs: H, biasShift: cal.biasShift, halfWidth: cal.halfWidth, samples: pts.length, accBefore: cal.accBefore, accAfter: cal.accAfter }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"twin-forecast-query.js","sourceRoot":"","sources":["../../../server/service/twin-forecast/twin-forecast-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { twinLog } from '../../engine/log.js'\nimport { Arg, Ctx, Int, Mutation, Query, Resolver, Directive} from 'type-graphql'\n\nimport { getRepository, ScalarObject } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\nimport { readSpecCoverage } from '../../engine/spec-coverage.js'\nimport { describeModelBasis, modelBasisChanged, type ModelBasis } from '../../engine/model-basis.js'\nimport { TwinEvent } from '../twin-event/twin-event.js'\nimport { GAP_SAMPLES, aggregatePoints, computeSpc, gapMetrics, learnedCalibration, type GapPoint } from './gap-analytics.js'\nimport { FORECAST_METRICS, type MetricFn } from './forecast-metrics.js'\nimport { forecastStepMs } from './forecast-tuning.js'\n\n/* 커널 예측 함수 — CJS require(twin-engine 과 동일 방식). */\nconst { monteCarloForecastAsync } = require('@operato/twin-kernel')\n\n/**\n * 이벤트 루프에 자리를 내주는 방법 — **커널이 아니라 여기가 안다.**\n *\n * `setImmediate` 는 대기 중인 I/O 다음 차례에 돌아온다. 마이크로태스크(`Promise.resolve()`)로는\n * 자리가 가지 않는다 — 그러면 예측이 도는 동안 라이브 틱도 HTTP 도 여전히 멈춘다.\n *\n * ── 내준 시간은 **부하가 아니다** ───────────────────────────────────────────\n * 부하 계기는 「이 트윈이 루프를 얼마나 붙잡았나」를 재는 자다. 양보를 넣은 뒤로 벽시계 시간에는\n * 남에게 넘긴 시간이 섞이는데, 그것까지 부하로 적으면 계기가 거짓말을 한다 — 하필 이 문제를 짚어낸\n * 그 계기다. 그래서 내준 시간을 따로 세어 빼고 적는다.\n */\nfunction breather() {\n let yieldedMs = 0\n return {\n fn: () =>\n new Promise<void>(resolve => {\n const t = performance.now()\n setImmediate(() => {\n yieldedMs += performance.now() - t\n resolve()\n })\n }),\n get ms() {\n return yieldedMs\n }\n }\n}\n\n/*\n * 트윈 예측 채널 — 상향 query. 트윈의 \"예측(predict)\" 면(설계 §3.5, ADR-0009).\n * 커널 monteCarloForecast(현재에서 N fork → 지표 분포) + 단일 fork 예측 궤적. 원본 live 무간섭(fork 격리).\n * metric 은 도메인-일반(스냅샷 파생), scenario 는 미래 자극(기본=계속 입고, 실 시스템은 인스턴스/설정 파생).\n */\nconst GTIN = 'urn:epc:idpat:sgtin:0614141.012345.*'\nconst DEFAULT_SCENARIO = {\n seed: 7,\n generators: [\n { kind: 'inbound-arrival', rate: { distribution: 'poisson', meanPerHour: 60 }, content: { skuMix: [{ gtin: GTIN, weight: 1 }], qtyPerLine: { min: 4, max: 12 } } }\n ]\n}\n/*\n * 지표 표는 `forecast-metrics.ts` 가 소유한다 — 여기 있던 것을 옮겼다(시험이 이 파일을 불러올 수 없다).\n *\n * **모르는 지표를 `items` 로 갈음하지 않는다.** 예전에는 `METRICS[metric] ?? METRICS.items` 였다.\n * 에너지 트윈에서 `peakKW` 를 물으면 재고 곡선이 그려지고, 재고가 없으니 **0 이 평평하게** 나왔다 —\n * 화면은 그것을 「피크가 0」으로 읽는다. 지어낸 답보다 모른다고 말하는 편이 낫다.\n */\nfunction metricFn(metric?: string): MetricFn | null {\n if (!metric) return FORECAST_METRICS.items\n return FORECAST_METRICS[metric] ?? null\n}\n\n/* 학습된 보정 저장(P3.2, 인메모리) — key=domain:instance:metric:horizon → {biasShift, halfWidth, …}.\n twinLearnCalibration 이 갭에서 학습해 저장하고, twinForecast 가 있으면 예측에 상시 적용(라이브 보정).\n 데모 트윈은 재부팅 시 저널 리셋이라 세션 스코프로 충분 — DB 영속(TwinForecastScore)은 후속. */\nconst CALIBRATION = new Map<\n string,\n { biasShift: number; halfWidth: number; at: string; samples: number; accBefore: number; accAfter: number; basis?: ModelBasis }\n>()\nconst calKey = (d: string, i: string, m: string, h: number) => `${d}:${i}:${m}:${h}`\n\n/**\n * 이 트윈이 백테스트할 수 있는 구간 — [첫 이벤트, 마지막 이벤트 − H].\n * 지평 H 만큼 앞을 봐야 \"그때 예측한 것\"과 \"실제로 벌어진 것\"을 맞대 볼 수 있으므로 끝에서 H 를 뺀다.\n */\nasync function gapWindow(domainId: string, instanceId: string, H: number): Promise<{ t0: number; lastVantage: number } | null> {\n const where = { domain: { id: domainId }, instanceId }\n const repo = getRepository(TwinEvent)\n const first = await repo.findOne({ where, order: { eventTime: 'ASC' } })\n const last = await repo.findOne({ where, order: { eventTime: 'DESC' } })\n if (!first?.eventTime || !last?.eventTime) return null\n const t0 = first.eventTime.getTime()\n const lastVantage = last.eventTime.getTime() - H\n return lastVantage > t0 ? { t0, lastVantage } : null\n}\n\n/**\n * 갭 point 계산(공용) — 주어진 vantage 각각에서 forecast-at-T vs actual-at-T+H(결정적 백테스트).\n *\n * **vantage 는 밖에서 준다.** 예전에는 이 함수가 자기 저널의 첫·마지막 이벤트로 격자를 스스로 만들었다.\n * 그러면 트윈마다 격자가 달라져, 공동배치 합산에서 `points[3]` 끼리 더할 때 **서로 다른 시각의 값**이\n * 더해졌다(자세한 사연은 `aggregatePoints` 주석).\n *\n * 반환 배열은 격자와 **같은 길이**이고, 그 vantage 를 재지 못했으면 그 자리에 `null` 을 둔다.\n * 건너뛰고 밀어 담으면 배열 인덱스가 시간축이기를 그만둔다.\n */\nasync function gapPoints(domainId: string, instanceId: string, m: (s: any) => number, H: number, grid: number[], scen: any): Promise<(any | null)[]> {\n const points: (any | null)[] = []\n for (const T of grid) {\n const k = await TwinEngine.buildForecastKernelAt(domainId, instanceId, new Date(T).toISOString()).catch(() => null)\n if (!k) { points.push(null); continue }\n const dist = await monteCarloForecastAsync(k, { runs: 12, horizonMs: H, scenario: scen, tickMs: forecastStepMs(H), metric: m, yieldFn: breather().fn })\n const naive = m(k.getSnapshot())\n const st = await TwinEngine.recover(domainId, instanceId, undefined, new Date(T + H).toISOString()).catch(() => null)\n if (!st) { points.push(null); continue }\n const a = m(st)\n const p50 = dist?.p50 ?? naive\n points.push({ t: T, p50, min: dist?.min ?? p50, max: dist?.max ?? p50, actual: a, error: a - p50, inBand: a >= (dist?.min ?? -Infinity) && a <= (dist?.max ?? Infinity), naiveAbsError: Math.abs(a - naive), modelAbsError: Math.abs(a - p50) })\n }\n return points\n}\n\n/**\n * 여러 트윈이 **함께** 백테스트할 수 있는 구간과 그 위의 vantage 격자.\n *\n * 겹치는 구간은 각자의 구간을 교집합한 것 — 시작은 가장 늦은 t0, 끝은 가장 이른 lastVantage.\n * 한 트윈이라도 그 시각에 저널이 없으면 공장 총량을 말할 수 없기 때문이다.\n * 겹치는 구간이 없으면 `null` — \"0\" 이 아니라 \"잴 수 없음\" 이다.\n */\nfunction sharedGrid(windows: { t0: number; lastVantage: number }[], N: number): number[] | null {\n if (!windows.length || N < 2) return null\n const t0 = Math.max(...windows.map(w => w.t0))\n const lastVantage = Math.min(...windows.map(w => w.lastVantage))\n if (!(lastVantage > t0)) return null\n return Array.from({ length: N }, (_, i) => t0 + ((lastVantage - t0) * i) / (N - 1))\n}\n\n@Resolver()\nexport class TwinForecastQuery {\n @Query(returns => ScalarObject, {\n nullable: true,\n description: 'Monte-Carlo forecast of a metric over a horizon (twin prediction faculty) + a single-fork predicted trajectory. Live instance is untouched (forks are isolated).'\n })\n async twinForecast(\n @Arg('instanceId') instanceId: string,\n @Arg('metric', { nullable: true }) metric: string,\n @Arg('runs', type => Int, { nullable: true }) runs: number,\n @Arg('horizonMs', type => Int, { nullable: true }) horizonMs: number,\n @Arg('scenario', type => ScalarObject, { nullable: true }) scenario: any,\n @Arg('at', { nullable: true }) at: string,\n @Ctx() context: ResolverContext\n ): Promise<any> {\n /* vantage(at) 기준 예측 — 히스토리 노브가 과거 T 면 \"그때 서서 본 미래\"(백테스트).\n at 있으면 T 시점 커널을 저널로 재구성한다(그건 우회가 아니라 시간여행의 본질).\n 없으면 **지금 그 커널**을 쓴다 — sim 이든 live 든 인스턴스가 커널이다(통합 P2). */\n const domainId = context.state.domain.id\n const k = at\n ? await TwinEngine.buildForecastKernelAt(domainId, instanceId, at)\n : TwinEngine.owns(domainId, instanceId)\n ? TwinEngine.kernel(domainId, instanceId)\n : undefined\n if (!k) return null\n\n const m = metricFn(metric)\n /* 이 트윈이 못 세는 지표 — 빈 예측이 아니라 「그 지표는 여기 없다」고 말한다. */\n if (!m) return { metric, unsupportedMetric: true, distribution: null, trajectory: [] }\n const H = horizonMs ?? 300000\n const R = runs ?? 30\n /*\n * 틱 크기는 **지평선이 정한다**(`forecast-tuning`). 1초 고정이던 시절 5분 지평선이 회차마다\n * 300틱이었고, 30회차면 9,000틱·28.5초였다 — 실측으로 5초 단위와 답이 같다.\n */\n const step = forecastStepMs(H)\n const scen = scenario ?? DEFAULT_SCENARIO\n\n /*\n * 분포: N개 확률적 미래 표본(fork 마다 seed 변주, 원본 보존).\n *\n * **이것이 요청 처리 중 메인 이벤트 루프에서 동기로 수행된다.** 기본값으로 fork 30개 × 300틱 = 9,000틱.\n * 계기판에 남겨 두지 않으면 \"왜 서버가 굳었나\" 를 나중에 짚을 수 없다(실제로 한 번 겪었다).\n */\n const tMc = performance.now()\n const mcBreath = breather()\n const distribution = await monteCarloForecastAsync(k, { runs: R, horizonMs: H, scenario: scen, tickMs: step, metric: m, yieldFn: mcBreath.fn })\n TwinEngine.recordLoad(domainId, instanceId, 'forecast', performance.now() - tMc - mcBreath.ms)\n TwinEngine.countForks(domainId, instanceId, R)\n\n /* 학습된 보정 상시 적용(P3.2) — twinLearnCalibration 이 저장한 편향·밴드가 있으면 예측을 현실로 보정(라이브가 실제로 나아짐).\n 갭트렌드(gapPoints)는 raw 모델을 그대로 측정(학습 신호 보존) — 보정은 표시용 forecast 에만. */\n const stored = CALIBRATION.get(calKey(domainId, instanceId, metric ?? 'items', H))\n /*\n * **배운 모델에만 적용한다.** 보정은 \"그때 모델이 이만큼 치우쳐 있다\" 를 배운 것이다. 소요시간\n * 추정기(실측·거리)나 공정 명세가 바뀌어 모델이 달라지면 그 편향은 더 이상 사실이 아니고, 그대로\n * 밀어 주면 이중 보정이 된다. 그래서 지문을 비교하고, 다르면 적용하지 않되 **그 사실을 응답에\n * 싣는다** — 조용히 사라지면 사용자는 학습이 왜 안 듣는지 알 수 없다(다시 배우면 된다).\n * 지문을 알 수 없을 때는 비교하지 않는다(모르는 것을 근거로 보정을 끊지 않는다).\n */\n const basis = await TwinEngine.modelBasis(domainId, instanceId)\n const stale = !!stored && modelBasisChanged(stored.basis, basis)\n if (stale) {\n twinLog(\n `[twin-forecast] \"${instanceId}\": calibration not applied — model changed since it was learned.` +\n ` learned on: ${describeModelBasis(stored!.basis)} / now: ${describeModelBasis(basis)}`\n )\n }\n /*\n * 분위수 — 표본에서 직접 뽑는다.\n *\n * 커널이 표본 배열을 그대로 돌려주므로(forecast.ts) 여기서 정렬해 p05·p95 를 낸다. 이것이 없으면\n * 화면이 보여 줄 수 있는 폭은 관측 **최소·최대** 뿐인데, 그건 신뢰구간이 아니라 극단값 두 개다.\n * \"운이 나쁜 5% 에서는 이만큼\" 이라고 말하려면 분위수여야 한다. 표본이 적으면(runs 가 작으면)\n * 분위수도 거칠다 — 그래서 표본 수를 함께 싣는다(sampleCount). 숫자만 주고 근거를 감추지 않는다.\n */\n if (distribution) {\n const samples: number[] = Array.isArray(distribution.samples) ? distribution.samples : []\n if (samples.length) {\n const sorted = [...samples].sort((a, b) => a - b)\n const pct = (p: number) => sorted[Math.min(sorted.length - 1, Math.floor(p * sorted.length))]\n distribution.p05 = pct(0.05)\n distribution.p95 = pct(0.95)\n }\n distribution.sampleCount = samples.length\n }\n\n const cal = stored && !stale ? stored : undefined\n if (cal && distribution) {\n /*\n * `p50` 은 커널 계약상 항상 있다(`MonteCarloResult.p50: number`). 그래도 메우지 않는다 —\n * 예전에는 `?? 0` 이었는데, 만약 비면 보정된 p50 이 **편향값 그 자체**가 되고 min·max 는\n * 그 둘레로 그려진다. 예측이 아닌 숫자가 예측의 자리에 앉는데 아무 신호가 없다.\n */\n if (typeof distribution.p50 !== 'number') throw new Error('forecast distribution has no p50 — kernel contract broken')\n const p50c = distribution.p50 + cal.biasShift\n distribution.p50 = Math.round(p50c)\n distribution.min = Math.round(p50c - cal.halfWidth)\n distribution.max = Math.round(p50c + cal.halfWidth)\n /*\n * 분위수는 **옮기기만** 한다. 보정이 배운 것은 \"모델이 이만큼 치우쳐 있다\"(위치)이지 분포의\n * 모양이 아니다. p50 처럼 밀어 두어야 p05~p95 폭이 표본이 말한 그대로 남는다.\n * (min·max 는 위에서 학습 밴드로 대체된다 — 보정이 걸리면 그 둘의 뜻이 '관측 극단' 에서\n * '학습 밴드' 로 바뀐다는 뜻이다. 화면이 둘을 같은 것으로 그리면 안 된다.)\n */\n if (distribution.p05 !== undefined) distribution.p05 = Math.round(distribution.p05 + cal.biasShift)\n if (distribution.p95 !== undefined) distribution.p95 = Math.round(distribution.p95 + cal.biasShift)\n if (distribution.p90 !== undefined) distribution.p90 = Math.round(distribution.p90 + cal.biasShift)\n distribution.calibrated = true\n }\n\n /*\n * 예측 궤적(예측선): 단일 fork 를 horizon 까지 실행하며 지표 기록. 이것도 잰다.\n *\n * 여기도 자리를 내준다 — 실측으로 이 한 회차가 1.3초였다(300틱 + 스냅샷 300번). 분포만큼은\n * 아니지만, 라이브 틱 간격(1초)보다 길게 붙잡으면 그 사이 트윈들이 한 박자씩 밀린다.\n */\n const tTraj = performance.now()\n const trajBreath = breather()\n const fc = k.fork()\n fc.scenario.load({ ...scen, seed: 1 })\n fc.scenario.start()\n const trajectory: { t: number; value: number }[] = []\n for (let t = 0; t < H; t += step) {\n fc.tick(step)\n trajectory.push({ t: t + step, value: m(fc.getSnapshot()) })\n /* 틱마다 내주면 양보 자체가 비용이 된다 — 라이브 틱 간격보다 훨씬 짧은 단위로 끊는다. */\n if (trajectory.length % 25 === 0) await trajBreath.fn()\n }\n\n TwinEngine.recordLoad(domainId, instanceId, 'forkTick', performance.now() - tTraj - trajBreath.ms)\n TwinEngine.countForks(domainId, instanceId, 1)\n\n const current = m(k.getSnapshot()) // vantage T 의 실제값(= naive '그대로 유지' 예측의 기준)\n\n /* 실측 오버레이 + 채점(P1) — vantage 가 과거(at)일 때만: T 이후 실제로 일어난 metric 을 T→min(now,T+H) 로 샘플.\n 현재 이후는 실측이 없음(미래). recover(T+t) 로 각 샘플 상태 재구성해 metric 추출. 비용=샘플 수 만큼 replay. */\n const actual: { t: number; value: number }[] = []\n let scored: any = null\n if (at) {\n const T = Date.parse(at)\n const endMs = Math.min(Date.now(), T + H)\n const span = endMs - T\n if (Number.isFinite(T) && span > 0) {\n const SAMPLES = 8\n const dt = span / SAMPLES\n for (let i = 1; i <= SAMPLES; i++) {\n const st = await TwinEngine.recover(domainId, instanceId, undefined, new Date(T + i * dt).toISOString()).catch(() => null)\n if (st) actual.push({ t: i * dt, value: m(st) })\n }\n /* 지평선이 지났으면(T+H ≤ now) 그 지점 실측 vs 예측 채점(단일 vantage). p50 대비 오차·밴드 적중·FVA(naive=현재값 유지 대비). */\n if (T + H <= Date.now() && actual.length) {\n const a = actual[actual.length - 1].value\n const p50 = distribution?.p50 ?? current\n scored = {\n actual: a,\n p50,\n error: a - p50, // 부호 = 편향 방향(+ 실제가 더 많음)\n inBand: a >= (distribution?.min ?? -Infinity) && a <= (distribution?.max ?? Infinity),\n modelAbsError: Math.abs(a - p50),\n naiveAbsError: Math.abs(a - current) // naive = \"그대로 유지\"(현재값). FVA = naive - model\n }\n }\n }\n }\n\n /*\n * 시뮬 명세 자기보고를 함께 낸다 — **상수로 계산한 예측과 현장 명세로 계산한 예측이 화면에서 같아\n * 보이지 않게.** 코드·수치만 싣고 사람이 읽는 문장은 클라가 만든다(언어 중립).\n * 커널이 이 API 를 갖지 않은 버전이면 키를 만들지 않는다(모르는 것을 \"전부 기본값\" 이라 단정 금지).\n */\n /* 보고는 **실제로 실행한 fork** 에서 읽는다 — 부모 커널(k)은 tick 하지 않으므로(예측은 fork 에서\n * 수행된다) 부모에서 읽으면 씨앗으로 세운 예측에서는 늘 비어 있다. 숫자를 낸 주체가 보고해야 한다. */\n const specCoverage = readSpecCoverage(fc) ?? readSpecCoverage(k)\n return {\n metric: metric ?? 'items', horizonMs: H, runs: R, current, distribution, trajectory, actual, scored,\n /* 얼마나 잘게 굴렸나 — 숫자만 주고 근거를 감추지 않는다(같은 규율로 `specCoverage` 도 싣는다). */\n tickMs: step,\n /*\n * **가정이 어떻게 됐나** — 걸린 개입·거절된 개입·덮어쓴 선언.\n *\n * 이것을 감추면 두 가지가 조용해진다: 걸리지 않은 가정(「걸었는데 왜 안 바뀌나」)과, 덮어쓴\n * 선언(사용자가 what-if 결과를 트윈의 현재 상태로 읽는다). 실제로 실행한 fork(`fc`)에서 읽는다 —\n * 가정을 겪은 것은 그쪽이다.\n */\n ...(typeof (fc as any).interventionOutcomes === 'function'\n ? (() => {\n const outcomes = (fc as any).interventionOutcomes()\n return outcomes.length ? { interventions: outcomes } : {}\n })()\n : {}),\n ...(typeof (fc as any).declarationOverrides === 'function'\n ? (() => {\n const overrides = (fc as any).declarationOverrides()\n return overrides.length ? { overrides } : {}\n })()\n : {}),\n calibrated: !!cal,\n /* 학습해 뒀지만 모델이 바뀌어 적용하지 않았다 — 사용자가 다시 배울 수 있게 알린다. */\n ...(stale ? { calibrationStale: true } : {}),\n ...(specCoverage ? { specCoverage } : {}),\n /*\n * **계보에 구멍이 몇 칸 생겼나** — 개체를 잇지 못한 채 넘어간 공정 수.\n *\n * ── 왜 이 값을 예측 응답에 싣나 (2026-08-24) ──────────────────────────────\n * 미러의 오더는 확보분을 갖지 않는다(원본이 「어느 개체가 잡혀 있나」를 말하지 않는 것이 그\n * 시스템의 정상이다). 커널은 그 자리에서 **던지지 않고 넘긴다** — 단계를 넘는 것은 공정의\n * 진행이고 개체 번호가 필요 없다. 대신 넘긴 횟수를 센다.\n *\n * 그런데 그 카운터는 **fork 안에** 있고 fork 는 버려진다. 여기서 싣지 않으면 아무에게도 닿지\n * 않는다 — 리듀서가 오래 세어 온 값을 스냅샷이 떨어뜨려 그것을 읽도록 쓰인 검사가 영원히\n * 조용했던 것과 **같은 실수**다. 세어 놓고 내보내지 않으면 안 센 것과 같다.\n *\n * 왜 화면이 이것을 알아야 하나: 계보는 **회수 범위**를 정하는 사슬이다. 구멍이 있는데 온전한\n * 것처럼 보이면 회수가 조용히 좁아진다. 식품에서 그것은 되돌릴 수 없다.\n *\n * 두 곳에서 읽어 **큰 값**을 낸다 — 분포(회차 R개의 최대)와 궤적 fork 는 **둘 다 실제로 실행됐다**.\n * 한쪽만 보면 다른 쪽이 겪은 구멍을 잃는다.\n */\n ...(() => {\n const gaps = Math.max(\n (distribution as any)?.stepsWithoutMaterial ?? 0,\n (fc as any)?.getSnapshot?.()?.stepsWithoutMaterial ?? 0\n )\n /* 0 은 싣지 않는다 — 「구멍이 없다」와 「이 축을 모른다」를 화면이 구별할 수 있게. */\n return gaps > 0 ? { stepsWithoutMaterial: gaps } : {}\n })()\n }\n }\n\n /*\n * 갭 트렌드(P2) — N개 과거 vantage 각각에서 \"그때 예측 vs 그 지평선의 실제\"를 재계산(결정적 백테스트).\n * 지평선이 이미 지난 vantage 만([t0, tLast−H]) → actual 확정. 온디맨드 재계산(누적/영속은 긴 기간용 후속 최적화).\n * 비용 = N × (buildForecastKernelAt replay + monteCarlo + recover replay). N·runs 를 낮게 bound. per-instance(클라가 co-located 합산).\n */\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'Forecast-accuracy gap diagnosis (canonical, server-computed): for N past vantages recompute forecast-at-T vs actual-at-T+h (deterministic backtest), aggregate co-located instances, and return points + SPC verdict (stability/bias) + metrics (accuracy/coverage/FVA) + learned-calibration projection. Client only renders.'\n })\n async twinForecastGapTrend(\n @Arg('instanceId') instanceId: string,\n @Arg('metric', { nullable: true }) metric: string,\n @Arg('horizonMs', type => Int, { nullable: true }) horizonMs: number,\n @Arg('samples', type => Int, { nullable: true }) samples: number,\n @Arg('scenario', type => ScalarObject, { nullable: true }) scenario: any,\n @Ctx() context: ResolverContext,\n @Arg('instanceIds', type => [String], { nullable: true }) instanceIds?: string[]\n ): Promise<any> {\n const domainId = context.state.domain.id\n const m = metricFn(metric)\n const H = horizonMs ?? 300000\n if (!m) return { metric, unsupportedMetric: true, horizonMs: H, samples: 0, points: [], spc: computeSpc([]), metrics: null, learn: null }\n const N = Math.min(30, Math.max(2, samples ?? GAP_SAMPLES)) // 표본은 서버가 관장(SPC 신뢰 하한 ~20).\n const scen = scenario ?? DEFAULT_SCENARIO\n // 공동배치(co-located) 집계도 서버에서 — 공장 단위 진단이 canonical. 미지정 시 단건.\n const ids = instanceIds?.length ? instanceIds : [instanceId]\n\n /*\n * 격자를 **먼저 하나** 만든다. 트윈마다 자기 저널로 격자를 만들면 같은 인덱스가 서로 다른\n * 시각이 되어, 합산이 다른 시점의 값을 더한다(`aggregatePoints` 주석 참조).\n *\n * 저널이 없거나 지평 H 를 담을 만큼 길지 않은 트윈은 **백테스트에 참여할 수 없다.** 그런 트윈은\n * 빼되 누구를 뺐는지 돌려준다 — 조용히 빼면 화면은 그 트윈까지 포함한 공장 총량으로 읽는다.\n */\n const windows = await Promise.all(ids.map(async id => ({ id, w: await gapWindow(domainId, id, H) })))\n const usable = windows.filter(x => x.w)\n const excluded = windows.filter(x => !x.w).map(x => x.id)\n const grid = sharedGrid(usable.map(x => x.w!), N)\n if (!grid) {\n /* 겹치는 구간이 없다 — 잴 수 없는 것이지 정확도가 0 인 것이 아니다. */\n return { metric: metric ?? 'items', horizonMs: H, samples: N, points: [], spc: computeSpc([]), metrics: null, learn: null,\n coverage: { instances: ids.length, measured: 0, excluded, vantages: N, used: 0, dropped: 0, reason: 'no-common-window' } }\n }\n\n const per = await Promise.all(usable.map(x => gapPoints(domainId, x.id, m, H, grid, scen)))\n const { points, droppedVantages } = aggregatePoints(per as (GapPoint | null)[][])\n const metrics = points.length ? gapMetrics(points) : null\n const spc = computeSpc(points.map(p => p.error))\n const learn = metrics ? learnedCalibration(points, metrics) : null\n return { metric: metric ?? 'items', horizonMs: H, samples: N, points, spc, metrics, learn,\n /* 이 숫자가 **무엇으로 계산됐는지**. 24점 중 18점이면 화면이 그렇게 말해야 한다. */\n coverage: { instances: ids.length, measured: usable.length, excluded, vantages: N, used: points.length, dropped: droppedVantages } }\n }\n\n /*\n * 학습(P3.2) — 갭에서 편향·밴드를 학습해 저장하면, 이후 twinForecast 가 상시 적용(라이브 예측이 실제로 보정됨).\n * assimilation-lite: 잔차(innovation)를 되먹여 추정치를 현실로. per-instance(클라가 co-located 합산).\n * 반환: 학습된 보정 + before/after 정확도(효과 정량화). 저장은 인메모리(세션) — DB 영속은 후속.\n */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => ScalarObject, { description: 'Learn a forecast calibration (bias + band) from the gap trend and persist it so future forecasts apply it. Returns the calibration + before/after accuracy.' })\n async twinLearnCalibration(\n @Arg('instanceId') instanceId: string,\n @Arg('metric', { nullable: true }) metric: string,\n @Arg('horizonMs', type => Int, { nullable: true }) horizonMs: number,\n @Ctx() context: ResolverContext,\n @Arg('clear', type => Boolean, { nullable: true }) clear?: boolean\n ): Promise<any> {\n const domainId = context.state.domain.id\n if (!TwinEngine.owns(domainId, instanceId)) return { ok: false } // 테넌트 격리\n const m = metricFn(metric)\n if (!m) return { ok: false, reason: 'unsupported-metric', metric }\n const H = horizonMs ?? 300000\n // 보정 해제 — 저장된 calibration 제거(이후 예측은 raw 로 복귀). 학습을 되돌리는 정직한 off 스위치.\n if (clear) {\n CALIBRATION.delete(calKey(domainId, instanceId, metric ?? 'items', H))\n return { ok: true, cleared: true, metric: metric ?? 'items', horizonMs: H }\n }\n // 표본·계산을 gap-analytics 로 일원화(gapTrend 진단과 동일 N·동일 수식 → 카드 예상치와 실제 보정 일치).\n /* 학습은 단건이라 격자도 이 트윈의 구간 하나다. 저널이 지평 H 를 담을 만큼 길지 않으면 배울 것이 없다. */\n const w = await gapWindow(domainId, instanceId, H)\n const grid = w ? sharedGrid([w], GAP_SAMPLES) : null\n if (!grid) return { ok: false, reason: 'insufficient-history' }\n /* 못 잰 vantage 는 `null` 로 온다 — 0 으로 세면 편향 학습이 그만큼 끌려간다. */\n const pts = (await gapPoints(domainId, instanceId, m, H, grid, DEFAULT_SCENARIO)).filter(Boolean) as GapPoint[]\n if (pts.length < 3) return { ok: false, reason: 'insufficient-history' }\n const learn = learnedCalibration(pts, gapMetrics(pts))\n /* 학습한 모델의 지문을 함께 남긴다 — 이후 모델이 바뀌면 이 보정은 적용되지 않는다. */\n const basis = await TwinEngine.modelBasis(domainId, instanceId)\n const cal = {\n biasShift: learn.biasShift,\n halfWidth: learn.halfWidth,\n at: '',\n samples: pts.length,\n accBefore: learn.accBefore,\n accAfter: learn.accAfter,\n ...(basis ? { basis } : {})\n }\n CALIBRATION.set(calKey(domainId, instanceId, metric ?? 'items', H), cal)\n return { ok: true, metric: metric ?? 'items', horizonMs: H, biasShift: cal.biasShift, halfWidth: cal.halfWidth, samples: pts.length, accBefore: cal.accBefore, accAfter: cal.accAfter }\n }\n}\n"]}
|