@operato/twin-kernel 0.6.12 → 0.6.13

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.
@@ -37,8 +37,19 @@ export interface OperationCapability {
37
37
  * `Math.max(0, …)` 하나로 덮는 일이 실제로 이 트윈에서 여러 번 있었다.
38
38
  */
39
39
  available: number | null;
40
- /** 무엇이 묶고 있나 — 축과 등급(용량 분석이 이미 낸다). */
40
+ /** 무엇이 **천장**을 묶고 있나 — 축과 등급(용량 분석이 이미 낸다). 선언 기준이다. */
41
41
  constraint?: CapacityRequirement;
42
+ /**
43
+ * **남은 능력을 묶는 축** — 달성불가를 되찾으려면 여기부터다. `unattainable > 0` 일 때만 있다.
44
+ *
45
+ * 천장을 묶는 축과 **다를 수 있고**, 그 차이가 곧 사람이 헛걸음하는 자리다: 용접기 2대와 용접
46
+ * 자격자 2명이 같은 천장을 낼 때 천장은 어느 쪽으로도 묶이지만, 자격 하나가 만료돼 잃은 몫의
47
+ * 원인은 인원이다. 화면이 천장 쪽만 보이면 사람이 **설비를 보러 간다**(실제로 그랬다).
48
+ *
49
+ * 값은 이미 손에 있다 — 달성불가를 내려고 가용 자원 기준으로 같은 계산을 한 번 더 돌리기 때문이다.
50
+ * 그 계산이 묶는 축이 곧 이 답이고, 따로 판정을 만들지 않는다.
51
+ */
52
+ unattainableBy?: CapacityRequirement;
42
53
  }
43
54
  export interface OperationsCapabilityReport {
44
55
  /** 표준 `OperationsType` — 지금은 생산뿐이다(정비·품질 능력은 별도 보고서가 될 자리). */
@@ -90,9 +90,14 @@ export function operationsCapabilityOf(input) {
90
90
  * 아닌 입력을 받은 것이므로 값을 지어내는 대신 아래 `available` 이 어긋남을 드러낸다.
91
91
  */
92
92
  let unattainable = null;
93
+ let unattainableBy;
93
94
  if (usable) {
94
- const usableAmount = amountOf(usable.operations.find(o => o.operation === op.operation)?.perHour ?? NaN);
95
+ const left = usable.operations.find(o => o.operation === op.operation);
96
+ const usableAmount = amountOf(left?.perHour ?? NaN);
95
97
  unattainable = total != null && usableAmount != null ? Math.max(0, total - usableAmount) : null;
98
+ /* 잃은 몫이 있을 때만 원인을 말한다 — 잃은 것이 없는데 "여기부터" 라고 하면 없는 일을 만든다. */
99
+ if (unattainable != null && unattainable > 0 && left?.constraint)
100
+ unattainableBy = left.constraint;
96
101
  }
97
102
  const available = total == null || unattainable == null ? null : total - committed - unattainable;
98
103
  return {
@@ -101,7 +106,8 @@ export function operationsCapabilityOf(input) {
101
106
  committed,
102
107
  unattainable,
103
108
  available,
104
- ...(op.constraint ? { constraint: op.constraint } : {})
109
+ ...(op.constraint ? { constraint: op.constraint } : {}),
110
+ ...(unattainableBy ? { unattainableBy } : {})
105
111
  };
106
112
  });
107
113
  return {
@@ -2276,9 +2276,12 @@ function operationsCapabilityOf(input) {
2276
2276
  const total = amountOf(op.perHour);
2277
2277
  const committed = committedOf(op.operation);
2278
2278
  let unattainable = null;
2279
+ let unattainableBy;
2279
2280
  if (usable) {
2280
- const usableAmount = amountOf(usable.operations.find((o) => o.operation === op.operation)?.perHour ?? NaN);
2281
+ const left = usable.operations.find((o) => o.operation === op.operation);
2282
+ const usableAmount = amountOf(left?.perHour ?? NaN);
2281
2283
  unattainable = total != null && usableAmount != null ? Math.max(0, total - usableAmount) : null;
2284
+ if (unattainable != null && unattainable > 0 && left?.constraint) unattainableBy = left.constraint;
2282
2285
  }
2283
2286
  const available = total == null || unattainable == null ? null : total - committed - unattainable;
2284
2287
  return {
@@ -2287,7 +2290,8 @@ function operationsCapabilityOf(input) {
2287
2290
  committed,
2288
2291
  unattainable,
2289
2292
  available,
2290
- ...op.constraint ? { constraint: op.constraint } : {}
2293
+ ...op.constraint ? { constraint: op.constraint } : {},
2294
+ ...unattainableBy ? { unattainableBy } : {}
2291
2295
  };
2292
2296
  });
2293
2297
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/twin-kernel",
3
- "version": "0.6.12",
3
+ "version": "0.6.13",
4
4
  "type": "module",
5
5
  "description": "Twin Domain Kernel — framework-agnostic, zero-dep (domain + sim + 3-channel contract). WMS/YMS/MES, EPCIS 2.0 · ISA-95.",
6
6
  "publishConfig": {