@things-factory/headless-twin 10.0.12 → 10.0.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.
- package/dist-server/engine/canonical-ingest.d.ts +57 -2
- package/dist-server/engine/canonical-ingest.js +43 -2
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/command-routing.js +1 -1
- package/dist-server/engine/command-routing.js.map +1 -1
- package/dist-server/engine/index.d.ts +3 -0
- package/dist-server/engine/index.js +4 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/ingest-health.d.ts +335 -0
- package/dist-server/engine/ingest-health.js +434 -0
- package/dist-server/engine/ingest-health.js.map +1 -0
- package/dist-server/engine/integration-coverage.d.ts +76 -0
- package/dist-server/engine/integration-coverage.js +73 -0
- package/dist-server/engine/integration-coverage.js.map +1 -0
- package/dist-server/engine/integration-probes.d.ts +65 -0
- package/dist-server/engine/integration-probes.js +100 -0
- package/dist-server/engine/integration-probes.js.map +1 -0
- package/dist-server/engine/integration-runner.d.ts +45 -0
- package/dist-server/engine/integration-runner.js +59 -0
- package/dist-server/engine/integration-runner.js.map +1 -0
- package/dist-server/engine/integration-target-profile.d.ts +57 -0
- package/dist-server/engine/integration-target-profile.js +79 -0
- package/dist-server/engine/integration-target-profile.js.map +1 -0
- package/dist-server/engine/kpi-fold.d.ts +27 -0
- package/dist-server/engine/kpi-fold.js +47 -4
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.js +128 -14
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/live-feed-registry.js +2 -16
- package/dist-server/engine/live-feed-registry.js.map +1 -1
- package/dist-server/engine/load-meter.d.ts +1 -1
- package/dist-server/engine/load-meter.js +12 -3
- package/dist-server/engine/load-meter.js.map +1 -1
- package/dist-server/engine/log.d.ts +18 -0
- package/dist-server/engine/log.js +80 -0
- package/dist-server/engine/log.js.map +1 -0
- package/dist-server/engine/loop-lag.d.ts +54 -0
- package/dist-server/engine/loop-lag.js +87 -0
- package/dist-server/engine/loop-lag.js.map +1 -0
- package/dist-server/engine/model-gap.d.ts +108 -0
- package/dist-server/engine/model-gap.js +95 -0
- package/dist-server/engine/model-gap.js.map +1 -0
- package/dist-server/engine/restart-policy.d.ts +3 -3
- package/dist-server/engine/restart-policy.js +4 -4
- package/dist-server/engine/restart-policy.js.map +1 -1
- package/dist-server/engine/runtime-key.js +1 -1
- package/dist-server/engine/runtime-key.js.map +1 -1
- package/dist-server/engine/stage-path.d.ts +83 -0
- package/dist-server/engine/stage-path.js +118 -0
- package/dist-server/engine/stage-path.js.map +1 -0
- package/dist-server/engine/twin-engine.d.ts +352 -16
- package/dist-server/engine/twin-engine.js +1037 -166
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/index.js +34 -2
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.d.ts +5 -0
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +58 -0
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -0
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.d.ts +5 -0
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +55 -0
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -0
- package/dist-server/migrations/index.js +7 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/service/index.d.ts +5 -2
- package/dist-server/service/index.js +18 -7
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/discovery-result.d.ts +1 -1
- package/dist-server/service/reference/discovery-result.js +1 -1
- package/dist-server/service/reference/discovery-result.js.map +1 -1
- package/dist-server/service/reference/index.d.ts +4 -1
- package/dist-server/service/reference/index.js +6 -1
- package/dist-server/service/reference/index.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +184 -3
- package/dist-server/service/reference/reference-adapter.js +28 -4
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-assessment.d.ts +68 -0
- package/dist-server/service/reference/reference-assessment.js +136 -0
- package/dist-server/service/reference/reference-assessment.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +12 -1
- package/dist-server/service/reference/reference-live.js +116 -11
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +47 -1
- package/dist-server/service/reference/reference-master.js +34 -4
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-probe.d.ts +92 -0
- package/dist-server/service/reference/reference-probe.js +186 -0
- package/dist-server/service/reference/reference-probe.js.map +1 -0
- package/dist-server/service/reference/reference-progress-subscription.d.ts +17 -0
- package/dist-server/service/reference/reference-progress-subscription.js +94 -0
- package/dist-server/service/reference/reference-progress-subscription.js.map +1 -0
- package/dist-server/service/reference/reference-progress.d.ts +38 -0
- package/dist-server/service/reference/reference-progress.js +71 -0
- package/dist-server/service/reference/reference-progress.js.map +1 -0
- package/dist-server/service/reference/reference-resolver.js +210 -43
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/twin-reference.d.ts +32 -0
- package/dist-server/service/reference/twin-reference.js +10 -0
- package/dist-server/service/reference/twin-reference.js.map +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.js +4 -3
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/journal-count.d.ts +21 -0
- package/dist-server/service/twin-event/journal-count.js +26 -0
- package/dist-server/service/twin-event/journal-count.js.map +1 -0
- package/dist-server/service/twin-event/twin-event-keys.d.ts +9 -0
- package/dist-server/service/twin-event/twin-event-keys.js +15 -18
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-type.d.ts +3 -1
- package/dist-server/service/twin-event/twin-event-type.js +18 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event.js +9 -1
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +2 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/index.d.ts +5 -0
- package/dist-server/service/twin-ingest-window/index.js +10 -0
- package/dist-server/service/twin-ingest-window/index.js.map +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +12 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +123 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.d.ts +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +51 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window.d.ts +16 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window.js +108 -0
- package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +1 -0
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/twin-journal-query.js +58 -5
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +14 -0
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +72 -2
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.d.ts +1 -0
- package/dist-server/service/twin-metrics/twin-metrics-query.js +78 -3
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.d.ts +2 -0
- package/dist-server/service/twin-model/axis-journal-evidence.js +89 -0
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -0
- package/dist-server/service/twin-model/epcis-coverage.d.ts +1 -1
- package/dist-server/service/twin-model/epcis-coverage.js +19 -7
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.d.ts +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js +18 -7
- package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
- package/dist-server/service/twin-model/isa95-coverage.d.ts +14 -0
- package/dist-server/service/twin-model/isa95-coverage.js +23 -9
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
- package/dist-server/service/twin-model/item-ref.d.ts +24 -0
- package/dist-server/service/twin-model/item-ref.js +88 -0
- package/dist-server/service/twin-model/item-ref.js.map +1 -0
- package/dist-server/service/twin-model/name-index.d.ts +36 -0
- package/dist-server/service/twin-model/name-index.js +116 -0
- package/dist-server/service/twin-model/name-index.js.map +1 -0
- package/dist-server/service/twin-model/project-structure.js +1 -1
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/status-tally.d.ts +9 -0
- package/dist-server/service/twin-model/status-tally.js +37 -0
- package/dist-server/service/twin-model/status-tally.js.map +1 -0
- package/dist-server/service/twin-model/twin-lineage-query.js +40 -12
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.js +38 -39
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-query.js +156 -4
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js +7 -0
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-readiness/index.d.ts +2 -0
- package/dist-server/service/twin-readiness/index.js +6 -0
- package/dist-server/service/twin-readiness/index.js.map +1 -0
- package/dist-server/service/twin-readiness/twin-readiness-query.d.ts +3 -0
- package/dist-server/service/twin-readiness/twin-readiness-query.js +103 -0
- package/dist-server/service/twin-readiness/twin-readiness-query.js.map +1 -0
- package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
- package/dist-server/service/twin-space/twin-space-resolver.js +5 -4
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-shared/entity-delta.d.ts +23 -3
- package/dist-shared/entity-delta.js +12 -8
- package/dist-shared/entity-delta.js.map +1 -1
- package/dist-shared/kpi-broadcast.js +1 -1
- package/dist-shared/kpi-broadcast.js.map +1 -1
- package/dist-shared/touched-items.d.ts +7 -0
- package/dist-shared/touched-items.js +80 -0
- package/dist-shared/touched-items.js.map +1 -0
- package/package.json +7 -7
- package/server/engine/canonical-ingest.ts +92 -7
- package/server/engine/command-routing.ts +1 -1
- package/server/engine/index.ts +4 -0
- package/server/engine/ingest-health.ts +704 -0
- package/server/engine/integration-coverage.ts +147 -0
- package/server/engine/integration-probes.ts +144 -0
- package/server/engine/integration-runner.ts +95 -0
- package/server/engine/integration-target-profile.ts +103 -0
- package/server/engine/kpi-fold.ts +72 -4
- package/server/engine/kpi-query.ts +129 -15
- package/server/engine/live-feed-registry.ts +2 -1
- package/server/engine/load-meter.ts +12 -3
- package/server/engine/log.ts +72 -0
- package/server/engine/loop-lag.ts +120 -0
- package/server/engine/model-gap.ts +168 -0
- package/server/engine/restart-policy.ts +4 -4
- package/server/engine/runtime-key.ts +1 -1
- package/server/engine/stage-path.ts +172 -0
- package/server/engine/twin-engine.ts +1135 -162
- package/server/index.ts +35 -2
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +54 -0
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +53 -0
- package/server/migrations/index.ts +7 -1
- package/server/service/index.ts +11 -0
- package/server/service/reference/discovery-result.ts +1 -1
- package/server/service/reference/index.ts +6 -1
- package/server/service/reference/reference-adapter.ts +197 -5
- package/server/service/reference/reference-assessment.ts +215 -0
- package/server/service/reference/reference-live.ts +123 -11
- package/server/service/reference/reference-master.ts +64 -4
- package/server/service/reference/reference-probe.ts +264 -0
- package/server/service/reference/reference-progress-subscription.ts +73 -0
- package/server/service/reference/reference-progress.ts +95 -0
- package/server/service/reference/reference-resolver.ts +200 -12
- package/server/service/reference/twin-reference.ts +39 -1
- package/server/service/twin-control/twin-control-mutation.ts +4 -3
- package/server/service/twin-event/journal-count.ts +53 -0
- package/server/service/twin-event/twin-event-keys.ts +16 -1
- package/server/service/twin-event/twin-event-type.ts +26 -2
- package/server/service/twin-event/twin-event.ts +7 -0
- package/server/service/twin-forecast/twin-forecast-query.ts +2 -1
- package/server/service/twin-ingest-window/index.ts +7 -0
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +125 -0
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +57 -0
- package/server/service/twin-ingest-window/twin-ingest-window.ts +113 -0
- package/server/service/twin-instance/twin-instance.ts +1 -1
- package/server/service/twin-journal/twin-journal-query.ts +59 -5
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +77 -3
- package/server/service/twin-metrics/twin-metrics-query.ts +81 -3
- package/server/service/twin-model/axis-journal-evidence.ts +57 -0
- package/server/service/twin-model/epcis-coverage.ts +2 -8
- package/server/service/twin-model/iec61850-coverage.ts +2 -8
- package/server/service/twin-model/isa95-coverage.ts +22 -9
- package/server/service/twin-model/item-ref.ts +80 -0
- package/server/service/twin-model/name-index.ts +94 -0
- package/server/service/twin-model/project-structure.ts +1 -1
- package/server/service/twin-model/status-tally.ts +37 -0
- package/server/service/twin-model/twin-lineage-query.ts +38 -9
- package/server/service/twin-model/twin-model-item-query.ts +26 -27
- package/server/service/twin-model/twin-model-query.ts +155 -4
- package/server/service/twin-model/twin-model-tree-query.ts +7 -0
- package/server/service/twin-readiness/index.ts +3 -0
- package/server/service/twin-readiness/twin-readiness-query.ts +96 -0
- package/server/service/twin-space/twin-space-resolver.ts +5 -4
- package/shared/entity-delta.ts +31 -7
- package/shared/kpi-broadcast.ts +1 -1
- package/shared/touched-items.ts +73 -0
- package/test/axis-journal-evidence.test.ts +71 -0
- package/test/axis-read.test.ts +101 -1
- package/test/boot-resume.test.ts +92 -26
- package/test/broadcast-cost-baseline.test.ts +200 -0
- package/test/broadcast-period.test.ts +58 -0
- package/test/canonical-ingest-vocabularies.test.ts +36 -1
- package/test/canonical-quantity-door.test.ts +61 -0
- package/test/command-routing.test.ts +1 -1
- package/test/declared-location-types.test.ts +89 -0
- package/test/discovery-result.test.ts +1 -1
- package/test/duration-estimators.test.ts +1 -1
- package/test/entity-delta.test.ts +2 -2
- package/test/ingest-bench.test.ts +3 -3
- package/test/ingest-health-engine.test.ts +248 -0
- package/test/ingest-health-wiring.test.ts +119 -0
- package/test/ingest-health.test.ts +306 -0
- package/test/ingest-history.test.ts +247 -0
- package/test/integration-probes.test.ts +103 -0
- package/test/integration-runner.test.ts +95 -0
- package/test/item-ref.test.ts +78 -0
- package/test/journal-read-discipline.test.ts +177 -0
- package/test/journal-retention.test.ts +133 -0
- package/test/journal-sort-axis.test.ts +142 -0
- package/test/journal-write-door.test.ts +110 -0
- package/test/journal-write-trend.test.ts +115 -0
- package/test/kernel-kind-guard.test.ts +4 -4
- package/test/kpi-fold.test.ts +90 -3
- package/test/kpi-query-bench.test.ts +2 -2
- package/test/lineage-survives-restart.test.ts +17 -1
- package/test/live-cursor-wiring.test.ts +87 -0
- package/test/live-feed-registry.test.ts +9 -1
- package/test/live-kernel-facts.test.ts +7 -7
- package/test/live-mirror-parity.test.ts +6 -0
- package/test/load-meter.test.ts +29 -15
- package/test/local-declarations.test.ts +1 -1
- package/test/log-stamp.test.ts +59 -0
- package/test/loop-lag.test.ts +82 -0
- package/test/model-gap.test.ts +153 -0
- package/test/oee-accumulator.test.ts +4 -0
- package/test/operational-vocabulary.test.ts +4 -4
- package/test/read-failure-visible.test.ts +145 -0
- package/test/reference-grounding.test.ts +70 -0
- package/test/resolve-ts-siblings.mjs +52 -0
- package/test/restart-policy.test.ts +7 -7
- package/test/revision-axis.test.ts +93 -0
- package/test/runtime-key.test.ts +2 -2
- package/test/scale-twin-bench.test.ts +2 -2
- package/test/spec-coverage.test.ts +1 -1
- package/test/stage-path.test.ts +95 -0
- package/test/standard-coverage.test.ts +15 -3
- package/test/status-tally.test.ts +55 -0
- package/test/structure-revision-db.test.ts +4 -0
- package/test/tenant-registry-db.test.ts +1 -1
- package/test/time-range.test.ts +152 -0
- package/test/touched-items.test.ts +61 -0
- package/test/twin-event-keys.test.ts +10 -2
- package/test/twin-model-item-db.test.ts +2 -0
- package/test/warm-start-seam.test.ts +4 -0
- package/test/yield-loop.test.ts +57 -2
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export type StageId = 'structure' | 'live' | 'forecast' | 'closed-loop';
|
|
2
|
+
/** 판정에 쓰는 사실. `null` = **계측되지 않았다**(0 이 아니다). */
|
|
3
|
+
export interface StageEvidence {
|
|
4
|
+
/** 이 트윈이 든 자리 수. */
|
|
5
|
+
locationCount: number;
|
|
6
|
+
/** 자리에 놓인 물품 수(비직렬 수량 물품 포함 — 식품·화학은 개수가 아니라 총량으로 온다). */
|
|
7
|
+
itemCount: number;
|
|
8
|
+
/**
|
|
9
|
+
* 마스터를 옮기며 **못 옮긴 값의 수**. `null` = 세지 않았다.
|
|
10
|
+
*
|
|
11
|
+
* 0 이면 통과이고 n 이어도 통과다 — 이 판정이 요구하는 것은 「없다」가 아니라 **「말할 수 있다」**다.
|
|
12
|
+
* 못 옮긴 값을 세지 않은 구조 인제스트는 도크를 랙으로 바꿔 놓고도 조용히 성공한다.
|
|
13
|
+
*/
|
|
14
|
+
masterWarnings: number | null;
|
|
15
|
+
/**
|
|
16
|
+
* 이 축이 이 트윈에 **해당하나**. 레퍼런스 없이 템플릿으로 세운 트윈에는 옮겨 온 마스터가 없으므로
|
|
17
|
+
* 「못 옮긴 값」을 물을 대상이 아니다 — 그걸 미측정으로 접으면 영원히 배선을 기다린다.
|
|
18
|
+
*/
|
|
19
|
+
masterWarningsApplicable?: boolean;
|
|
20
|
+
/** 인제스트 누적 건수. */
|
|
21
|
+
liveIngestTotal: number;
|
|
22
|
+
/** **반출(remove)을 접은 누적 건수.** `null` = 계측되지 않음. 0 = 세었으나 없었다(→ 스냅샷 의심). */
|
|
23
|
+
removalsObserved: number | null;
|
|
24
|
+
/** 이 트윈에서 예측이 한 번이라도 돌았나. */
|
|
25
|
+
forecastRan: boolean;
|
|
26
|
+
/** 선언 적재량 — `declared`/`total`. `null` = 알 수 없음. */
|
|
27
|
+
specCoverage: {
|
|
28
|
+
declared: number;
|
|
29
|
+
total: number;
|
|
30
|
+
} | null;
|
|
31
|
+
/**
|
|
32
|
+
* 이 트윈이 **무엇인가** — 시뮬레이션은 원천에서 받는 것이 없다.
|
|
33
|
+
*
|
|
34
|
+
* 시뮬에 라이브를 요구하면 영원히 빨갛고, 그 빨강은 결함이 아니다. 도입 단계는 **연결된 트윈**의
|
|
35
|
+
* 축이므로 시뮬은 라이브 이후를 「해당 없음」으로 말한다(달성으로도, 미측정으로도 접지 않는다).
|
|
36
|
+
*/
|
|
37
|
+
subject?: 'live' | 'sim';
|
|
38
|
+
/** 피드 성격 — `snapshot-poll` 이면 반출 0 은 「아직」이 아니라 **구조적으로 불가**다. */
|
|
39
|
+
feed?: 'push' | 'delta-poll' | 'snapshot-poll' | 'none' | 'unknown';
|
|
40
|
+
/** 원본이 **선언∩구현**으로 액추에이션 능력을 갖나(`capabilitiesOf`). */
|
|
41
|
+
actuateCapability: boolean;
|
|
42
|
+
/** 내 명령의 효과가 인바운드로 **상관되어** 돌아온 누적 건수. `null` = 계측되지 않음. */
|
|
43
|
+
correlatedEffectObserved: number | null;
|
|
44
|
+
}
|
|
45
|
+
export interface StageVerdict {
|
|
46
|
+
id: StageId;
|
|
47
|
+
achieved: boolean;
|
|
48
|
+
/** 아직 아닌 이유 — 언어중립 코드(화면이 옮긴다). 달성이면 빈 배열. */
|
|
49
|
+
blockedBy: string[];
|
|
50
|
+
/** 판정할 수 없었던 신호 — **배선되지 않은 계측**. 있으면 달성이 아니다. */
|
|
51
|
+
unmeasured: string[];
|
|
52
|
+
}
|
|
53
|
+
export interface StagePath {
|
|
54
|
+
stages: StageVerdict[];
|
|
55
|
+
/** 지금 서 있는 칸 — 연속으로 달성된 마지막 단계. 하나도 없으면 `null`. */
|
|
56
|
+
reached: StageId | null;
|
|
57
|
+
/**
|
|
58
|
+
* 다음 한 걸음 — 사람이 오늘 할 수 있는 일 하나. 없으면(전부 달성) `null`.
|
|
59
|
+
* 「무엇이 부족한지」의 목록이 아니라 **다음 하나**다. 목록은 사람을 멈추게 한다.
|
|
60
|
+
*/
|
|
61
|
+
next: {
|
|
62
|
+
stage: StageId;
|
|
63
|
+
blockedBy: string[];
|
|
64
|
+
unmeasured: string[];
|
|
65
|
+
} | null;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 증거로 단계를 판정한다.
|
|
69
|
+
*
|
|
70
|
+
* **단계는 앞선 단계를 전제한다**(구조 없이 라이브를 달성했다고 말할 수 없다). 앞이 막히면 뒤는
|
|
71
|
+
* 자기 조건을 따지지 않고 `prerequisite` 으로 막는다 — 뒤 단계의 조건을 나열하면 사람이 순서를
|
|
72
|
+
* 건너뛴다.
|
|
73
|
+
*/
|
|
74
|
+
export declare function assessStagePath(e: StageEvidence): StagePath;
|
|
75
|
+
/**
|
|
76
|
+
* 이 단계에 이르지 못했다면 **쓰지 않기로 한 말**.
|
|
77
|
+
*
|
|
78
|
+
* 근거 수준 규율(`connector-developer-guide` §8)을 판정과 같은 자리에 둔다. 문장이 문서에만 있으면
|
|
79
|
+
* 제안서를 쓰는 사람이 그 문서를 읽지 않는다. 값이 되면 화면이 그 자리에서 말할 수 있다.
|
|
80
|
+
*/
|
|
81
|
+
export declare const CLAIM_GATE: Record<StageId, string>;
|
|
82
|
+
/** 아직 쓸 수 없는 주장들 — 도달하지 못한 단계의 게이트 전부. */
|
|
83
|
+
export declare function forbiddenClaims(path: StagePath): string[];
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* 단계 판정 — **가벼운 성공을 최종 성공으로 잇는 길을, 사람의 체크가 아니라 증거로 센다.**
|
|
4
|
+
*
|
|
5
|
+
* ── 왜 이 모듈이 있나 ───────────────────────────────────────────────────────
|
|
6
|
+
* 트윈 도입은 「구조 → 라이브 → 예측 → 폐루프」로 간다. 그런데 이 진행을 **체크리스트**로 관리하면
|
|
7
|
+
* 사람이 체크하는 순간 참이 된다. 실제로 가장 자주 건너뛰어지는 자리가 구조 다음이다 — 구조가 서면
|
|
8
|
+
* 화면이 그럴듯해지므로 「실시간」이라고 말해 버리고, 되풀어 읽는 스냅샷이 그 말을 뒷받침하지 못한다.
|
|
9
|
+
*
|
|
10
|
+
* 그래서 단계를 **판정식**으로 둔다. 각 단계는 시스템이 이미 내놓는 사실로만 판정되고, 선언으로
|
|
11
|
+
* 위조할 수 없다. 특히:
|
|
12
|
+
*
|
|
13
|
+
* · **반출 관측**이 라이브의 판별식이다. 스냅샷을 되풀어 읽는 방식은 **원리적으로** 반출을 낼 수 없다
|
|
14
|
+
* (사라진 것은 그냥 없어질 뿐이라 `OBSERVE` 만 남는다). 그러므로 「반출을 한 번이라도 접었는가」는
|
|
15
|
+
* 진짜 delta 의 증거이고, 폴링을 실시간이라 부르는 것을 막는다.
|
|
16
|
+
* · **선언 적재량**이 예측의 판별식이다. 커널 기본값으로 돌아간 예측도 숫자를 내므로, 화면만 보면
|
|
17
|
+
* 구별되지 않는다. 그 숫자가 무엇 위에 섰는지는 `specCoverage` 만 안다.
|
|
18
|
+
*
|
|
19
|
+
* ── 이 모듈의 정수: 「모른다」를 「아니다」로도 「그렇다」로도 바꾸지 않는다 ──────────────
|
|
20
|
+
* 계측되지 않은 신호는 `null` 로 온다. `null` 은 0 이 아니다. 계측되지 않았으면 그 단계는 **달성이
|
|
21
|
+
* 아니고**, 그 사실을 `unmeasured` 로 말한다 — 신호를 배선하지 않은 채 단계를 주장할 길을 남기지
|
|
22
|
+
* 않는다. (0 으로 대체하면 「반출 0건」과 「반출을 세지 않았다」가 같아진다.)
|
|
23
|
+
*
|
|
24
|
+
* 순수 함수다(프레임워크·DB 무의존) — 증거 수집은 호출자 몫이다.
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.CLAIM_GATE = void 0;
|
|
28
|
+
exports.assessStagePath = assessStagePath;
|
|
29
|
+
exports.forbiddenClaims = forbiddenClaims;
|
|
30
|
+
const ORDER = ['structure', 'live', 'forecast', 'closed-loop'];
|
|
31
|
+
/**
|
|
32
|
+
* 증거로 단계를 판정한다.
|
|
33
|
+
*
|
|
34
|
+
* **단계는 앞선 단계를 전제한다**(구조 없이 라이브를 달성했다고 말할 수 없다). 앞이 막히면 뒤는
|
|
35
|
+
* 자기 조건을 따지지 않고 `prerequisite` 으로 막는다 — 뒤 단계의 조건을 나열하면 사람이 순서를
|
|
36
|
+
* 건너뛴다.
|
|
37
|
+
*/
|
|
38
|
+
function assessStagePath(e) {
|
|
39
|
+
const stages = [];
|
|
40
|
+
let prereqOk = true;
|
|
41
|
+
for (const id of ORDER) {
|
|
42
|
+
const blockedBy = [];
|
|
43
|
+
const unmeasured = [];
|
|
44
|
+
if (!prereqOk) {
|
|
45
|
+
blockedBy.push('prerequisite');
|
|
46
|
+
}
|
|
47
|
+
else if (id === 'structure') {
|
|
48
|
+
if (!(e.locationCount > 0))
|
|
49
|
+
blockedBy.push('no-locations');
|
|
50
|
+
if (!(e.itemCount > 0))
|
|
51
|
+
blockedBy.push('no-items');
|
|
52
|
+
if (e.masterWarningsApplicable !== false && e.masterWarnings === null)
|
|
53
|
+
unmeasured.push('master-warnings');
|
|
54
|
+
}
|
|
55
|
+
else if (id === 'live') {
|
|
56
|
+
if (e.subject === 'sim')
|
|
57
|
+
blockedBy.push('subject-is-simulation');
|
|
58
|
+
else {
|
|
59
|
+
if (!(e.liveIngestTotal > 0))
|
|
60
|
+
blockedBy.push('no-ingest');
|
|
61
|
+
if (e.removalsObserved === null)
|
|
62
|
+
unmeasured.push('removals');
|
|
63
|
+
/*
|
|
64
|
+
* 세었는데 없다. 이유가 둘이고 **처방이 정반대**다: 스냅샷을 되풀어 읽는 방식이면 더 기다려도
|
|
65
|
+
* 오지 않으므로 방식을 바꿔야 하고, 그 밖이면 아직 반출이 일어나지 않은 것일 수 있다.
|
|
66
|
+
*/
|
|
67
|
+
else if (e.removalsObserved === 0)
|
|
68
|
+
blockedBy.push(e.feed === 'snapshot-poll' ? 'snapshot-only-feed' : 'no-removal-observed');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else if (id === 'forecast') {
|
|
72
|
+
if (!e.forecastRan)
|
|
73
|
+
blockedBy.push('no-forecast-run');
|
|
74
|
+
if (e.specCoverage === null)
|
|
75
|
+
unmeasured.push('spec-coverage');
|
|
76
|
+
/* 선언이 0 이면 예측은 커널 기본값 위에 섰다 — 숫자는 나오지만 이 현장의 숫자가 아니다. */
|
|
77
|
+
else if (!(e.specCoverage.declared > 0))
|
|
78
|
+
blockedBy.push('declarations-empty');
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (!e.actuateCapability)
|
|
82
|
+
blockedBy.push('no-actuate-capability');
|
|
83
|
+
if (e.correlatedEffectObserved === null)
|
|
84
|
+
unmeasured.push('correlated-effect');
|
|
85
|
+
/* 접수만으로는 폐루프가 아니다 — 효과가 관측으로 돌아와야 닫힌다. */
|
|
86
|
+
else if (!(e.correlatedEffectObserved > 0))
|
|
87
|
+
blockedBy.push('effect-not-observed');
|
|
88
|
+
}
|
|
89
|
+
const achieved = blockedBy.length === 0 && unmeasured.length === 0;
|
|
90
|
+
stages.push({ id, achieved, blockedBy, unmeasured });
|
|
91
|
+
if (!achieved)
|
|
92
|
+
prereqOk = false;
|
|
93
|
+
}
|
|
94
|
+
const achievedIds = stages.filter(s => s.achieved).map(s => s.id);
|
|
95
|
+
const firstUnmet = stages.find(s => !s.achieved);
|
|
96
|
+
return {
|
|
97
|
+
stages,
|
|
98
|
+
reached: achievedIds.length ? achievedIds[achievedIds.length - 1] : null,
|
|
99
|
+
next: firstUnmet ? { stage: firstUnmet.id, blockedBy: firstUnmet.blockedBy, unmeasured: firstUnmet.unmeasured } : null
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 이 단계에 이르지 못했다면 **쓰지 않기로 한 말**.
|
|
104
|
+
*
|
|
105
|
+
* 근거 수준 규율(`connector-developer-guide` §8)을 판정과 같은 자리에 둔다. 문장이 문서에만 있으면
|
|
106
|
+
* 제안서를 쓰는 사람이 그 문서를 읽지 않는다. 값이 되면 화면이 그 자리에서 말할 수 있다.
|
|
107
|
+
*/
|
|
108
|
+
exports.CLAIM_GATE = {
|
|
109
|
+
structure: 'claim.structure-synced',
|
|
110
|
+
live: 'claim.realtime',
|
|
111
|
+
forecast: 'claim.prediction',
|
|
112
|
+
'closed-loop': 'claim.automatic-control'
|
|
113
|
+
};
|
|
114
|
+
/** 아직 쓸 수 없는 주장들 — 도달하지 못한 단계의 게이트 전부. */
|
|
115
|
+
function forbiddenClaims(path) {
|
|
116
|
+
return path.stages.filter(s => !s.achieved).map(s => exports.CLAIM_GATE[s.id]);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=stage-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stage-path.js","sourceRoot":"","sources":["../../server/engine/stage-path.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 단계 판정 — **가벼운 성공을 최종 성공으로 잇는 길을, 사람의 체크가 아니라 증거로 센다.**\n *\n * ── 왜 이 모듈이 있나 ───────────────────────────────────────────────────────\n * 트윈 도입은 「구조 → 라이브 → 예측 → 폐루프」로 간다. 그런데 이 진행을 **체크리스트**로 관리하면\n * 사람이 체크하는 순간 참이 된다. 실제로 가장 자주 건너뛰어지는 자리가 구조 다음이다 — 구조가 서면\n * 화면이 그럴듯해지므로 「실시간」이라고 말해 버리고, 되풀어 읽는 스냅샷이 그 말을 뒷받침하지 못한다.\n *\n * 그래서 단계를 **판정식**으로 둔다. 각 단계는 시스템이 이미 내놓는 사실로만 판정되고, 선언으로\n * 위조할 수 없다. 특히:\n *\n * · **반출 관측**이 라이브의 판별식이다. 스냅샷을 되풀어 읽는 방식은 **원리적으로** 반출을 낼 수 없다\n * (사라진 것은 그냥 없어질 뿐이라 `OBSERVE` 만 남는다). 그러므로 「반출을 한 번이라도 접었는가」는\n * 진짜 delta 의 증거이고, 폴링을 실시간이라 부르는 것을 막는다.\n * · **선언 적재량**이 예측의 판별식이다. 커널 기본값으로 돌아간 예측도 숫자를 내므로, 화면만 보면\n * 구별되지 않는다. 그 숫자가 무엇 위에 섰는지는 `specCoverage` 만 안다.\n *\n * ── 이 모듈의 정수: 「모른다」를 「아니다」로도 「그렇다」로도 바꾸지 않는다 ──────────────\n * 계측되지 않은 신호는 `null` 로 온다. `null` 은 0 이 아니다. 계측되지 않았으면 그 단계는 **달성이\n * 아니고**, 그 사실을 `unmeasured` 로 말한다 — 신호를 배선하지 않은 채 단계를 주장할 길을 남기지\n * 않는다. (0 으로 대체하면 「반출 0건」과 「반출을 세지 않았다」가 같아진다.)\n *\n * 순수 함수다(프레임워크·DB 무의존) — 증거 수집은 호출자 몫이다.\n */\n\nexport type StageId = 'structure' | 'live' | 'forecast' | 'closed-loop'\n\n/** 판정에 쓰는 사실. `null` = **계측되지 않았다**(0 이 아니다). */\nexport interface StageEvidence {\n /* ── S1 구조 ── */\n /** 이 트윈이 든 자리 수. */\n locationCount: number\n /** 자리에 놓인 물품 수(비직렬 수량 물품 포함 — 식품·화학은 개수가 아니라 총량으로 온다). */\n itemCount: number\n /**\n * 마스터를 옮기며 **못 옮긴 값의 수**. `null` = 세지 않았다.\n *\n * 0 이면 통과이고 n 이어도 통과다 — 이 판정이 요구하는 것은 「없다」가 아니라 **「말할 수 있다」**다.\n * 못 옮긴 값을 세지 않은 구조 인제스트는 도크를 랙으로 바꿔 놓고도 조용히 성공한다.\n */\n masterWarnings: number | null\n /**\n * 이 축이 이 트윈에 **해당하나**. 레퍼런스 없이 템플릿으로 세운 트윈에는 옮겨 온 마스터가 없으므로\n * 「못 옮긴 값」을 물을 대상이 아니다 — 그걸 미측정으로 접으면 영원히 배선을 기다린다.\n */\n masterWarningsApplicable?: boolean\n\n /* ── S2 라이브 ── */\n /** 인제스트 누적 건수. */\n liveIngestTotal: number\n /** **반출(remove)을 접은 누적 건수.** `null` = 계측되지 않음. 0 = 세었으나 없었다(→ 스냅샷 의심). */\n removalsObserved: number | null\n\n /* ── S3 예측 ── */\n /** 이 트윈에서 예측이 한 번이라도 돌았나. */\n forecastRan: boolean\n /** 선언 적재량 — `declared`/`total`. `null` = 알 수 없음. */\n specCoverage: { declared: number; total: number } | null\n\n /* ── S4 폐루프 ── */\n /**\n * 이 트윈이 **무엇인가** — 시뮬레이션은 원천에서 받는 것이 없다.\n *\n * 시뮬에 라이브를 요구하면 영원히 빨갛고, 그 빨강은 결함이 아니다. 도입 단계는 **연결된 트윈**의\n * 축이므로 시뮬은 라이브 이후를 「해당 없음」으로 말한다(달성으로도, 미측정으로도 접지 않는다).\n */\n subject?: 'live' | 'sim'\n /** 피드 성격 — `snapshot-poll` 이면 반출 0 은 「아직」이 아니라 **구조적으로 불가**다. */\n feed?: 'push' | 'delta-poll' | 'snapshot-poll' | 'none' | 'unknown'\n /** 원본이 **선언∩구현**으로 액추에이션 능력을 갖나(`capabilitiesOf`). */\n actuateCapability: boolean\n /** 내 명령의 효과가 인바운드로 **상관되어** 돌아온 누적 건수. `null` = 계측되지 않음. */\n correlatedEffectObserved: number | null\n}\n\nexport interface StageVerdict {\n id: StageId\n achieved: boolean\n /** 아직 아닌 이유 — 언어중립 코드(화면이 옮긴다). 달성이면 빈 배열. */\n blockedBy: string[]\n /** 판정할 수 없었던 신호 — **배선되지 않은 계측**. 있으면 달성이 아니다. */\n unmeasured: string[]\n}\n\nexport interface StagePath {\n stages: StageVerdict[]\n /** 지금 서 있는 칸 — 연속으로 달성된 마지막 단계. 하나도 없으면 `null`. */\n reached: StageId | null\n /**\n * 다음 한 걸음 — 사람이 오늘 할 수 있는 일 하나. 없으면(전부 달성) `null`.\n * 「무엇이 부족한지」의 목록이 아니라 **다음 하나**다. 목록은 사람을 멈추게 한다.\n */\n next: { stage: StageId; blockedBy: string[]; unmeasured: string[] } | null\n}\n\nconst ORDER: StageId[] = ['structure', 'live', 'forecast', 'closed-loop']\n\n/**\n * 증거로 단계를 판정한다.\n *\n * **단계는 앞선 단계를 전제한다**(구조 없이 라이브를 달성했다고 말할 수 없다). 앞이 막히면 뒤는\n * 자기 조건을 따지지 않고 `prerequisite` 으로 막는다 — 뒤 단계의 조건을 나열하면 사람이 순서를\n * 건너뛴다.\n */\nexport function assessStagePath(e: StageEvidence): StagePath {\n const stages: StageVerdict[] = []\n let prereqOk = true\n\n for (const id of ORDER) {\n const blockedBy: string[] = []\n const unmeasured: string[] = []\n\n if (!prereqOk) {\n blockedBy.push('prerequisite')\n } else if (id === 'structure') {\n if (!(e.locationCount > 0)) blockedBy.push('no-locations')\n if (!(e.itemCount > 0)) blockedBy.push('no-items')\n if (e.masterWarningsApplicable !== false && e.masterWarnings === null) unmeasured.push('master-warnings')\n } else if (id === 'live') {\n if (e.subject === 'sim') blockedBy.push('subject-is-simulation')\n else {\n if (!(e.liveIngestTotal > 0)) blockedBy.push('no-ingest')\n if (e.removalsObserved === null) unmeasured.push('removals')\n /*\n * 세었는데 없다. 이유가 둘이고 **처방이 정반대**다: 스냅샷을 되풀어 읽는 방식이면 더 기다려도\n * 오지 않으므로 방식을 바꿔야 하고, 그 밖이면 아직 반출이 일어나지 않은 것일 수 있다.\n */\n else if (e.removalsObserved === 0) blockedBy.push(e.feed === 'snapshot-poll' ? 'snapshot-only-feed' : 'no-removal-observed')\n }\n } else if (id === 'forecast') {\n if (!e.forecastRan) blockedBy.push('no-forecast-run')\n if (e.specCoverage === null) unmeasured.push('spec-coverage')\n /* 선언이 0 이면 예측은 커널 기본값 위에 섰다 — 숫자는 나오지만 이 현장의 숫자가 아니다. */\n else if (!(e.specCoverage.declared > 0)) blockedBy.push('declarations-empty')\n } else {\n if (!e.actuateCapability) blockedBy.push('no-actuate-capability')\n if (e.correlatedEffectObserved === null) unmeasured.push('correlated-effect')\n /* 접수만으로는 폐루프가 아니다 — 효과가 관측으로 돌아와야 닫힌다. */\n else if (!(e.correlatedEffectObserved > 0)) blockedBy.push('effect-not-observed')\n }\n\n const achieved = blockedBy.length === 0 && unmeasured.length === 0\n stages.push({ id, achieved, blockedBy, unmeasured })\n if (!achieved) prereqOk = false\n }\n\n const achievedIds = stages.filter(s => s.achieved).map(s => s.id)\n const firstUnmet = stages.find(s => !s.achieved)\n return {\n stages,\n reached: achievedIds.length ? achievedIds[achievedIds.length - 1] : null,\n next: firstUnmet ? { stage: firstUnmet.id, blockedBy: firstUnmet.blockedBy, unmeasured: firstUnmet.unmeasured } : null\n }\n}\n\n/**\n * 이 단계에 이르지 못했다면 **쓰지 않기로 한 말**.\n *\n * 근거 수준 규율(`connector-developer-guide` §8)을 판정과 같은 자리에 둔다. 문장이 문서에만 있으면\n * 제안서를 쓰는 사람이 그 문서를 읽지 않는다. 값이 되면 화면이 그 자리에서 말할 수 있다.\n */\nexport const CLAIM_GATE: Record<StageId, string> = {\n structure: 'claim.structure-synced',\n live: 'claim.realtime',\n forecast: 'claim.prediction',\n 'closed-loop': 'claim.automatic-control'\n}\n\n/** 아직 쓸 수 없는 주장들 — 도달하지 못한 단계의 게이트 전부. */\nexport function forbiddenClaims(path: StagePath): string[] {\n return path.stages.filter(s => !s.achieved).map(s => CLAIM_GATE[s.id])\n}\n"]}
|