@things-factory/headless-twin 10.1.277 → 10.1.279
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/package.json +21 -7
- package/dist-server/engine/attention-digest.js.map +0 -1
- package/dist-server/engine/canonical-ingest.js.map +0 -1
- package/dist-server/engine/checkpoint-fold.js.map +0 -1
- package/dist-server/engine/command-routing.js.map +0 -1
- package/dist-server/engine/declared-stimulus.js.map +0 -1
- package/dist-server/engine/energy-topology.js.map +0 -1
- package/dist-server/engine/event-time.js.map +0 -1
- package/dist-server/engine/index.js.map +0 -1
- package/dist-server/engine/ingest-dedupe.js.map +0 -1
- package/dist-server/engine/ingest-health.js.map +0 -1
- package/dist-server/engine/integration-coverage.js.map +0 -1
- package/dist-server/engine/integration-probes.js.map +0 -1
- package/dist-server/engine/integration-runner.js.map +0 -1
- package/dist-server/engine/integration-target-profile.js.map +0 -1
- package/dist-server/engine/kpi-baseline.js.map +0 -1
- package/dist-server/engine/kpi-fold.js.map +0 -1
- package/dist-server/engine/kpi-query.js.map +0 -1
- package/dist-server/engine/kpi-target.js.map +0 -1
- package/dist-server/engine/live-attentions.js.map +0 -1
- package/dist-server/engine/live-feed-registry.js.map +0 -1
- package/dist-server/engine/load-meter.js.map +0 -1
- package/dist-server/engine/local-declarations.js.map +0 -1
- package/dist-server/engine/log.js.map +0 -1
- package/dist-server/engine/loop-lag.js.map +0 -1
- package/dist-server/engine/measured-estimator.js.map +0 -1
- package/dist-server/engine/measured-yield.js.map +0 -1
- package/dist-server/engine/model-basis.js.map +0 -1
- package/dist-server/engine/model-gap.js.map +0 -1
- package/dist-server/engine/model-refusal.js.map +0 -1
- package/dist-server/engine/model-vocabulary.js.map +0 -1
- package/dist-server/engine/oee-accumulator.js.map +0 -1
- package/dist-server/engine/operation-basis.js.map +0 -1
- package/dist-server/engine/producer-guard.js.map +0 -1
- package/dist-server/engine/property-effects.js.map +0 -1
- package/dist-server/engine/read-time.js.map +0 -1
- package/dist-server/engine/restart-policy.js.map +0 -1
- package/dist-server/engine/runtime-key.js.map +0 -1
- package/dist-server/engine/spec-coverage.js.map +0 -1
- package/dist-server/engine/stage-path.js.map +0 -1
- package/dist-server/engine/state-axes.js.map +0 -1
- package/dist-server/engine/structure-diff.js.map +0 -1
- package/dist-server/engine/travel-estimator.js.map +0 -1
- package/dist-server/engine/twin-engine.js.map +0 -1
- package/dist-server/engine/twin-state-channel.js.map +0 -1
- package/dist-server/engine/warm-start.js.map +0 -1
- package/dist-server/index.js.map +0 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +0 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +0 -1
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +0 -1
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +0 -1
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +0 -1
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +0 -1
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +0 -1
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +0 -1
- package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js.map +0 -1
- package/dist-server/migrations/index.js.map +0 -1
- package/dist-server/routes.js.map +0 -1
- package/dist-server/service/actuation/actuation-advisor.js.map +0 -1
- package/dist-server/service/actuation/actuation-outcome.js.map +0 -1
- package/dist-server/service/actuation/actuation-rule-resolver.js.map +0 -1
- package/dist-server/service/actuation/actuation-rule.js.map +0 -1
- package/dist-server/service/actuation/approval-gateway.js.map +0 -1
- package/dist-server/service/actuation/catch-up-effects.js.map +0 -1
- package/dist-server/service/actuation/command-dispatcher.js.map +0 -1
- package/dist-server/service/actuation/command-store.js.map +0 -1
- package/dist-server/service/actuation/dispatch-after-approval.js.map +0 -1
- package/dist-server/service/actuation/effect-match.js.map +0 -1
- package/dist-server/service/actuation/index.js.map +0 -1
- package/dist-server/service/actuation/retry-plan.js.map +0 -1
- package/dist-server/service/actuation/rule-evaluate.js.map +0 -1
- package/dist-server/service/actuation/rule-loop.js.map +0 -1
- package/dist-server/service/actuation/rule-runner.js.map +0 -1
- package/dist-server/service/actuation/rule-wiring.js.map +0 -1
- package/dist-server/service/actuation/settle-effects.js.map +0 -1
- package/dist-server/service/actuation/sweep-dispatch.js.map +0 -1
- package/dist-server/service/actuation/twin-command-resolver.js.map +0 -1
- package/dist-server/service/actuation/twin-command.js.map +0 -1
- package/dist-server/service/actuation/twin-now.js.map +0 -1
- package/dist-server/service/index.js.map +0 -1
- package/dist-server/service/reference/actuation-routing.js.map +0 -1
- package/dist-server/service/reference/actuation-target.js.map +0 -1
- package/dist-server/service/reference/actuation-verification.js.map +0 -1
- package/dist-server/service/reference/attention-lane.js.map +0 -1
- package/dist-server/service/reference/connection-portability-resolver.js.map +0 -1
- package/dist-server/service/reference/connection-portability.js.map +0 -1
- package/dist-server/service/reference/control-routing.js.map +0 -1
- package/dist-server/service/reference/discovery-result.js.map +0 -1
- package/dist-server/service/reference/fill-cadence.js.map +0 -1
- package/dist-server/service/reference/fill-dedupe.js.map +0 -1
- package/dist-server/service/reference/fill-loop.js.map +0 -1
- package/dist-server/service/reference/fill-span.js.map +0 -1
- package/dist-server/service/reference/hook-contract.js.map +0 -1
- package/dist-server/service/reference/hook-rejected.js.map +0 -1
- package/dist-server/service/reference/hook-store.js.map +0 -1
- package/dist-server/service/reference/index.js.map +0 -1
- package/dist-server/service/reference/ingest-space.js.map +0 -1
- package/dist-server/service/reference/knob-defaults.js.map +0 -1
- package/dist-server/service/reference/live-cadence.js.map +0 -1
- package/dist-server/service/reference/live-feed-lease.js.map +0 -1
- package/dist-server/service/reference/live-ingest.js.map +0 -1
- package/dist-server/service/reference/reference-adapter.js.map +0 -1
- package/dist-server/service/reference/reference-assessment.js.map +0 -1
- package/dist-server/service/reference/reference-fill-run.js.map +0 -1
- package/dist-server/service/reference/reference-fill.js.map +0 -1
- package/dist-server/service/reference/reference-hook.js.map +0 -1
- package/dist-server/service/reference/reference-link-groups.js.map +0 -1
- package/dist-server/service/reference/reference-links.js.map +0 -1
- package/dist-server/service/reference/reference-live.js.map +0 -1
- package/dist-server/service/reference/reference-master.js.map +0 -1
- package/dist-server/service/reference/reference-probe.js.map +0 -1
- package/dist-server/service/reference/reference-progress-channel.js.map +0 -1
- package/dist-server/service/reference/reference-progress-subscription.js.map +0 -1
- package/dist-server/service/reference/reference-progress.js.map +0 -1
- package/dist-server/service/reference/reference-reconciliation-query.js.map +0 -1
- package/dist-server/service/reference/reference-reconciliation-writer.js.map +0 -1
- package/dist-server/service/reference/reference-reconciliation.js.map +0 -1
- package/dist-server/service/reference/reference-registry.js.map +0 -1
- package/dist-server/service/reference/reference-resolver.js.map +0 -1
- package/dist-server/service/reference/template-registry.js.map +0 -1
- package/dist-server/service/reference/twin-reference-reconciliation.js.map +0 -1
- package/dist-server/service/reference/twin-reference.js.map +0 -1
- package/dist-server/service/twin-attention/index.js.map +0 -1
- package/dist-server/service/twin-attention/twin-attention-query.js.map +0 -1
- package/dist-server/service/twin-audit/command-audit.js.map +0 -1
- package/dist-server/service/twin-audit/index.js.map +0 -1
- package/dist-server/service/twin-audit/twin-audit-event.js.map +0 -1
- package/dist-server/service/twin-audit/twin-audit-query.js.map +0 -1
- package/dist-server/service/twin-backfill/backfill-period-facts.js.map +0 -1
- package/dist-server/service/twin-backfill/backfill-shape.js.map +0 -1
- package/dist-server/service/twin-backfill/index.js.map +0 -1
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +0 -1
- package/dist-server/service/twin-control/index.js.map +0 -1
- package/dist-server/service/twin-control/twin-control-mutation.js.map +0 -1
- package/dist-server/service/twin-event/index.js.map +0 -1
- package/dist-server/service/twin-event/journal-count.js.map +0 -1
- package/dist-server/service/twin-event/twin-event-keys.js.map +0 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +0 -1
- package/dist-server/service/twin-event/twin-event.js.map +0 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js.map +0 -1
- package/dist-server/service/twin-forecast/forecast-tuning.js.map +0 -1
- package/dist-server/service/twin-forecast/gap-analytics.js.map +0 -1
- package/dist-server/service/twin-forecast/index.js.map +0 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +0 -1
- package/dist-server/service/twin-ingest-window/index.js.map +0 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +0 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +0 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +0 -1
- package/dist-server/service/twin-instance/index.js.map +0 -1
- package/dist-server/service/twin-instance/twin-instance.js.map +0 -1
- package/dist-server/service/twin-journal/event-type-split-shape.js.map +0 -1
- package/dist-server/service/twin-journal/event-type-split.js.map +0 -1
- package/dist-server/service/twin-journal/index.js.map +0 -1
- package/dist-server/service/twin-journal/journal-order.js.map +0 -1
- package/dist-server/service/twin-journal/twin-journal-query.js.map +0 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.js.map +0 -1
- package/dist-server/service/twin-lifecycle/index.js.map +0 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +0 -1
- package/dist-server/service/twin-metrics/index.js.map +0 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +0 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +0 -1
- package/dist-server/service/twin-model/choose-recipe.js.map +0 -1
- package/dist-server/service/twin-model/epcis-coverage.js.map +0 -1
- package/dist-server/service/twin-model/external-resolver.js.map +0 -1
- package/dist-server/service/twin-model/iec61850-coverage.js.map +0 -1
- package/dist-server/service/twin-model/index.js.map +0 -1
- package/dist-server/service/twin-model/isa95-coverage.js.map +0 -1
- package/dist-server/service/twin-model/item-ref.js.map +0 -1
- package/dist-server/service/twin-model/name-index.js.map +0 -1
- package/dist-server/service/twin-model/project-structure.js.map +0 -1
- package/dist-server/service/twin-model/standard-coverage.js.map +0 -1
- package/dist-server/service/twin-model/status-tally.js.map +0 -1
- package/dist-server/service/twin-model/twin-equipment.js.map +0 -1
- package/dist-server/service/twin-model/twin-lineage-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-location.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-item-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-mutation.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-operation.js.map +0 -1
- package/dist-server/service/twin-readiness/index.js.map +0 -1
- package/dist-server/service/twin-readiness/twin-readiness-query.js.map +0 -1
- package/dist-server/service/twin-space/area-resolver.js.map +0 -1
- package/dist-server/service/twin-space/index.js.map +0 -1
- package/dist-server/service/twin-space/move-space.js.map +0 -1
- package/dist-server/service/twin-space/representation-role.js.map +0 -1
- package/dist-server/service/twin-space/space-integrity.js.map +0 -1
- package/dist-server/service/twin-space/twin-area.js.map +0 -1
- package/dist-server/service/twin-space/twin-space-area.js.map +0 -1
- package/dist-server/service/twin-space/twin-space-representation.js.map +0 -1
- package/dist-server/service/twin-space/twin-space-resolver.js.map +0 -1
- package/dist-server/service/twin-space/twin-space.js.map +0 -1
- package/dist-server/service/twin-state/index.js.map +0 -1
- package/dist-server/service/twin-state/twin-state-subscription.js.map +0 -1
- package/dist-server/service/twin-structure/index.js.map +0 -1
- package/dist-server/service/twin-structure/twin-structure.js.map +0 -1
- package/dist-server/service/twin-subject/event-subjects.js.map +0 -1
- package/dist-server/service/twin-subject/index.js.map +0 -1
- package/dist-server/service/twin-subject/item-resolver.js.map +0 -1
- package/dist-server/service/twin-subject/subject-query-shape.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-backfill.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-event.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-query.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-writer.js.map +0 -1
- package/dist-server/service/twin-target/index.js.map +0 -1
- package/dist-server/service/twin-target/twin-target-resolver.js.map +0 -1
- package/dist-server/service/twin-target/twin-target.js.map +0 -1
- package/dist-shared/axis-read.js.map +0 -1
- package/dist-shared/entity-delta.js.map +0 -1
- package/dist-shared/kpi-broadcast.js.map +0 -1
- package/dist-shared/touched-items.js.map +0 -1
- package/dist-shared/twin-level.js.map +0 -1
- package/server/engine/attention-digest.ts +0 -102
- package/server/engine/canonical-ingest.ts +0 -511
- package/server/engine/checkpoint-fold.ts +0 -12
- package/server/engine/command-routing.ts +0 -67
- package/server/engine/declared-stimulus.ts +0 -66
- package/server/engine/energy-topology.ts +0 -189
- package/server/engine/event-time.ts +0 -96
- package/server/engine/index.ts +0 -51
- package/server/engine/ingest-dedupe.ts +0 -24
- package/server/engine/ingest-health.ts +0 -24
- package/server/engine/integration-coverage.ts +0 -147
- package/server/engine/integration-probes.ts +0 -144
- package/server/engine/integration-runner.ts +0 -95
- package/server/engine/integration-target-profile.ts +0 -103
- package/server/engine/kpi-baseline.ts +0 -202
- package/server/engine/kpi-fold.ts +0 -1516
- package/server/engine/kpi-query.ts +0 -1210
- package/server/engine/kpi-target.ts +0 -248
- package/server/engine/live-attentions.ts +0 -30
- package/server/engine/live-feed-registry.ts +0 -59
- package/server/engine/load-meter.ts +0 -393
- package/server/engine/local-declarations.ts +0 -779
- package/server/engine/log.ts +0 -72
- package/server/engine/loop-lag.ts +0 -120
- package/server/engine/measured-estimator.ts +0 -91
- package/server/engine/measured-yield.ts +0 -89
- package/server/engine/model-basis.ts +0 -162
- package/server/engine/model-gap.ts +0 -168
- package/server/engine/model-refusal.ts +0 -31
- package/server/engine/model-vocabulary.ts +0 -82
- package/server/engine/oee-accumulator.ts +0 -195
- package/server/engine/operation-basis.ts +0 -131
- package/server/engine/producer-guard.ts +0 -64
- package/server/engine/property-effects.ts +0 -216
- package/server/engine/read-time.ts +0 -77
- package/server/engine/restart-policy.ts +0 -55
- package/server/engine/runtime-key.ts +0 -58
- package/server/engine/spec-coverage.ts +0 -105
- package/server/engine/stage-path.ts +0 -172
- package/server/engine/state-axes.ts +0 -60
- package/server/engine/structure-diff.ts +0 -88
- package/server/engine/travel-estimator.ts +0 -138
- package/server/engine/twin-engine.ts +0 -4979
- package/server/engine/twin-state-channel.ts +0 -39
- package/server/engine/warm-start.ts +0 -265
- package/server/index.ts +0 -90
- package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +0 -43
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +0 -54
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +0 -53
- package/server/migrations/1786300000000-IndexEventTypeByTime.ts +0 -48
- package/server/migrations/1786400000000-IndexEventCreatedAt.ts +0 -38
- package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +0 -93
- package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +0 -66
- package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +0 -82
- package/server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts +0 -39
- package/server/migrations/index.ts +0 -25
- package/server/routes.ts +0 -75
- package/server/service/actuation/actuation-advisor.ts +0 -131
- package/server/service/actuation/actuation-outcome.ts +0 -97
- package/server/service/actuation/actuation-rule-resolver.ts +0 -147
- package/server/service/actuation/actuation-rule.ts +0 -139
- package/server/service/actuation/approval-gateway.ts +0 -44
- package/server/service/actuation/catch-up-effects.ts +0 -152
- package/server/service/actuation/command-dispatcher.ts +0 -474
- package/server/service/actuation/command-store.ts +0 -236
- package/server/service/actuation/dispatch-after-approval.ts +0 -71
- package/server/service/actuation/effect-match.ts +0 -124
- package/server/service/actuation/index.ts +0 -27
- package/server/service/actuation/retry-plan.ts +0 -145
- package/server/service/actuation/rule-evaluate.ts +0 -352
- package/server/service/actuation/rule-loop.ts +0 -160
- package/server/service/actuation/rule-runner.ts +0 -216
- package/server/service/actuation/rule-wiring.ts +0 -84
- package/server/service/actuation/settle-effects.ts +0 -115
- package/server/service/actuation/sweep-dispatch.ts +0 -179
- package/server/service/actuation/twin-command-resolver.ts +0 -387
- package/server/service/actuation/twin-command.ts +0 -232
- package/server/service/actuation/twin-now.ts +0 -97
- package/server/service/index.ts +0 -100
- package/server/service/reference/actuation-routing.ts +0 -192
- package/server/service/reference/actuation-target.ts +0 -163
- package/server/service/reference/actuation-verification.ts +0 -85
- package/server/service/reference/attention-lane.ts +0 -21
- package/server/service/reference/connection-portability-resolver.ts +0 -171
- package/server/service/reference/connection-portability.ts +0 -329
- package/server/service/reference/control-routing.ts +0 -62
- package/server/service/reference/discovery-result.ts +0 -95
- package/server/service/reference/fill-cadence.ts +0 -29
- package/server/service/reference/fill-dedupe.ts +0 -66
- package/server/service/reference/fill-loop.ts +0 -153
- package/server/service/reference/fill-span.ts +0 -31
- package/server/service/reference/hook-contract.ts +0 -364
- package/server/service/reference/hook-rejected.ts +0 -53
- package/server/service/reference/hook-store.ts +0 -28
- package/server/service/reference/index.ts +0 -18
- package/server/service/reference/ingest-space.ts +0 -104
- package/server/service/reference/knob-defaults.ts +0 -59
- package/server/service/reference/live-cadence.ts +0 -129
- package/server/service/reference/live-feed-lease.ts +0 -56
- package/server/service/reference/live-ingest.ts +0 -94
- package/server/service/reference/reference-adapter.ts +0 -985
- package/server/service/reference/reference-assessment.ts +0 -250
- package/server/service/reference/reference-fill-run.ts +0 -179
- package/server/service/reference/reference-fill.ts +0 -223
- package/server/service/reference/reference-hook.ts +0 -479
- package/server/service/reference/reference-link-groups.ts +0 -21
- package/server/service/reference/reference-links.ts +0 -77
- package/server/service/reference/reference-live.ts +0 -420
- package/server/service/reference/reference-master.ts +0 -999
- package/server/service/reference/reference-probe.ts +0 -264
- package/server/service/reference/reference-progress-channel.ts +0 -26
- package/server/service/reference/reference-progress-subscription.ts +0 -78
- package/server/service/reference/reference-progress.ts +0 -95
- package/server/service/reference/reference-reconciliation-query.ts +0 -28
- package/server/service/reference/reference-reconciliation-writer.ts +0 -38
- package/server/service/reference/reference-reconciliation.ts +0 -55
- package/server/service/reference/reference-registry.ts +0 -17
- package/server/service/reference/reference-resolver.ts +0 -969
- package/server/service/reference/template-registry.ts +0 -85
- package/server/service/reference/twin-reference-reconciliation.ts +0 -27
- package/server/service/reference/twin-reference.ts +0 -190
- package/server/service/twin-attention/index.ts +0 -3
- package/server/service/twin-attention/twin-attention-query.ts +0 -70
- package/server/service/twin-audit/command-audit.ts +0 -114
- package/server/service/twin-audit/index.ts +0 -5
- package/server/service/twin-audit/twin-audit-event.ts +0 -158
- package/server/service/twin-audit/twin-audit-query.ts +0 -72
- package/server/service/twin-backfill/backfill-period-facts.ts +0 -168
- package/server/service/twin-backfill/backfill-shape.ts +0 -86
- package/server/service/twin-backfill/index.ts +0 -3
- package/server/service/twin-backfill/twin-backfill-resolver.ts +0 -35
- package/server/service/twin-control/index.ts +0 -3
- package/server/service/twin-control/twin-control-mutation.ts +0 -150
- package/server/service/twin-event/index.ts +0 -3
- package/server/service/twin-event/journal-count.ts +0 -53
- package/server/service/twin-event/twin-event-keys.ts +0 -215
- package/server/service/twin-event/twin-event-type.ts +0 -68
- package/server/service/twin-event/twin-event.ts +0 -250
- package/server/service/twin-forecast/forecast-metrics.ts +0 -106
- package/server/service/twin-forecast/forecast-tuning.ts +0 -40
- package/server/service/twin-forecast/gap-analytics.ts +0 -139
- package/server/service/twin-forecast/index.ts +0 -3
- package/server/service/twin-forecast/twin-forecast-query.ts +0 -486
- package/server/service/twin-ingest-window/index.ts +0 -7
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +0 -125
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +0 -57
- package/server/service/twin-ingest-window/twin-ingest-window.ts +0 -113
- package/server/service/twin-instance/index.ts +0 -3
- package/server/service/twin-instance/twin-instance.ts +0 -199
- package/server/service/twin-journal/event-type-split-shape.ts +0 -112
- package/server/service/twin-journal/event-type-split.ts +0 -144
- package/server/service/twin-journal/index.ts +0 -3
- package/server/service/twin-journal/journal-order.ts +0 -20
- package/server/service/twin-journal/twin-journal-query.ts +0 -615
- package/server/service/twin-lifecycle/domain-catalog.ts +0 -51
- package/server/service/twin-lifecycle/index.ts +0 -3
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +0 -285
- package/server/service/twin-metrics/index.ts +0 -3
- package/server/service/twin-metrics/twin-metrics-query.ts +0 -172
- package/server/service/twin-model/axis-journal-evidence.ts +0 -57
- package/server/service/twin-model/choose-recipe.ts +0 -31
- package/server/service/twin-model/epcis-coverage.ts +0 -251
- package/server/service/twin-model/external-resolver.ts +0 -173
- package/server/service/twin-model/iec61850-coverage.ts +0 -111
- package/server/service/twin-model/index.ts +0 -25
- package/server/service/twin-model/isa95-coverage.ts +0 -320
- package/server/service/twin-model/item-ref.ts +0 -80
- package/server/service/twin-model/name-index.ts +0 -94
- package/server/service/twin-model/project-structure.ts +0 -213
- package/server/service/twin-model/standard-coverage.ts +0 -35
- package/server/service/twin-model/status-tally.ts +0 -37
- package/server/service/twin-model/twin-equipment.ts +0 -191
- package/server/service/twin-model/twin-lineage-query.ts +0 -327
- package/server/service/twin-model/twin-location.ts +0 -147
- package/server/service/twin-model/twin-model-item-query.ts +0 -1029
- package/server/service/twin-model/twin-model-mutation.ts +0 -503
- package/server/service/twin-model/twin-model-query.ts +0 -792
- package/server/service/twin-model/twin-model-tree-query.ts +0 -211
- package/server/service/twin-model/twin-operation.ts +0 -157
- package/server/service/twin-readiness/index.ts +0 -3
- package/server/service/twin-readiness/twin-readiness-query.ts +0 -96
- package/server/service/twin-space/area-resolver.ts +0 -75
- package/server/service/twin-space/index.ts +0 -8
- package/server/service/twin-space/move-space.ts +0 -262
- package/server/service/twin-space/representation-role.ts +0 -15
- package/server/service/twin-space/space-integrity.ts +0 -293
- package/server/service/twin-space/twin-area.ts +0 -63
- package/server/service/twin-space/twin-space-area.ts +0 -66
- package/server/service/twin-space/twin-space-representation.ts +0 -99
- package/server/service/twin-space/twin-space-resolver.ts +0 -582
- package/server/service/twin-space/twin-space.ts +0 -148
- package/server/service/twin-state/index.ts +0 -3
- package/server/service/twin-state/twin-state-subscription.ts +0 -62
- package/server/service/twin-structure/index.ts +0 -3
- package/server/service/twin-structure/twin-structure.ts +0 -96
- package/server/service/twin-subject/event-subjects.ts +0 -162
- package/server/service/twin-subject/index.ts +0 -10
- package/server/service/twin-subject/item-resolver.ts +0 -92
- package/server/service/twin-subject/subject-query-shape.ts +0 -84
- package/server/service/twin-subject/twin-subject-backfill.ts +0 -196
- package/server/service/twin-subject/twin-subject-event.ts +0 -101
- package/server/service/twin-subject/twin-subject-fast-path.ts +0 -346
- package/server/service/twin-subject/twin-subject-query.ts +0 -210
- package/server/service/twin-subject/twin-subject-writer.ts +0 -189
- package/server/service/twin-target/index.ts +0 -5
- package/server/service/twin-target/twin-target-resolver.ts +0 -142
- package/server/service/twin-target/twin-target.ts +0 -97
- package/server/types/global.d.ts +0 -22
- package/shared/axis-read.ts +0 -28
- package/shared/entity-delta.ts +0 -335
- package/shared/kpi-broadcast.ts +0 -13
- package/shared/touched-items.ts +0 -73
- package/shared/twin-level.ts +0 -48
- package/test/ack-shape-alignment.test.ts +0 -97
- package/test/actuation-advisor.test.ts +0 -55
- package/test/actuation-approval-door.test.ts +0 -411
- package/test/actuation-dispatch.test.ts +0 -209
- package/test/actuation-effect.test.ts +0 -162
- package/test/actuation-outcome.test.ts +0 -152
- package/test/actuation-rule-runner.test.ts +0 -281
- package/test/actuation-rule.test.ts +0 -266
- package/test/actuation-seam.test.ts +0 -255
- package/test/actuation-settle.test.ts +0 -164
- package/test/actuation-target.test.ts +0 -145
- package/test/actuation-verification-state.test.ts +0 -139
- package/test/adopt-structure-live.test.ts +0 -133
- package/test/aggregate-not-findone.test.ts +0 -112
- package/test/area-resolver-db.test.ts +0 -110
- package/test/attention-digest.test.ts +0 -161
- package/test/axis-declaration-passthrough-db.test.ts +0 -152
- package/test/axis-journal-evidence.test.ts +0 -71
- package/test/axis-read.test.ts +0 -188
- package/test/backfill-shape.test.ts +0 -89
- package/test/boot-resume.test.ts +0 -287
- package/test/broadcast-cost-baseline.test.ts +0 -201
- package/test/broadcast-period.test.ts +0 -58
- package/test/broadcast-queue-overflow.test.ts +0 -244
- package/test/canonical-ingest-vocabularies.test.ts +0 -451
- package/test/canonical-quantity-door.test.ts +0 -61
- package/test/capability-mapping.test.ts +0 -89
- package/test/checkpoint-refuses-empty.test.ts +0 -103
- package/test/choose-recipe.test.ts +0 -117
- package/test/column-type-portability.test.ts +0 -122
- package/test/command-routing.test.ts +0 -61
- package/test/command-store-writes-columns.test.ts +0 -126
- package/test/connection-portability-doors.test.ts +0 -87
- package/test/connector-capability-declaration.test.ts +0 -195
- package/test/contract-layer-guard.test.ts +0 -48
- package/test/control-capability.test.ts +0 -98
- package/test/cursor-stall-not-read-failure.test.ts +0 -126
- package/test/declaration-reaches-model.test.ts +0 -226
- package/test/declared-location-types.test.ts +0 -89
- package/test/declared-stimulus.test.ts +0 -88
- package/test/dedupe-transformation.test.ts +0 -112
- package/test/discovery-result.test.ts +0 -75
- package/test/dispatch-after-approval-source.test.ts +0 -81
- package/test/duration-estimators.test.ts +0 -144
- package/test/energy-topology.test.ts +0 -113
- package/test/entity-delta.test.ts +0 -352
- package/test/equipment-identity-reaches.test.ts +0 -57
- package/test/event-subjects.test.ts +0 -148
- package/test/event-time-column.test.ts +0 -67
- package/test/event-type-split.test.ts +0 -178
- package/test/external-resolver.test.ts +0 -177
- package/test/fact-scope-wiring.test.ts +0 -96
- package/test/fill-dedupe.test.ts +0 -59
- package/test/fill-loop.test.ts +0 -35
- package/test/first-envelope.test.ts +0 -153
- package/test/fold-gap-speaks.test.ts +0 -53
- package/test/fold-shift-dst.test.ts +0 -110
- package/test/forecast-metrics.test.ts +0 -157
- package/test/forecast-tuning.test.ts +0 -42
- package/test/gap-analytics.test.ts +0 -113
- package/test/generated-not-counted.test.ts +0 -99
- package/test/guard-roots.ts +0 -76
- package/test/home-in-area.test.ts +0 -90
- package/test/hook-address.test.ts +0 -28
- package/test/hook-rejected-shape.test.ts +0 -86
- package/test/hook-response-sum.test.ts +0 -193
- package/test/hook-sequence.test.ts +0 -333
- package/test/ingest-bench.test.ts +0 -70
- package/test/ingest-dedupe.test.ts +0 -56
- package/test/ingest-expected-quiet.test.ts +0 -111
- package/test/ingest-health-engine.test.ts +0 -248
- package/test/ingest-health-wiring.test.ts +0 -120
- package/test/ingest-health.test.ts +0 -306
- package/test/ingest-history.test.ts +0 -247
- package/test/ingest-idempotent.test.ts +0 -211
- package/test/ingest-reconcile-four-ways.test.ts +0 -97
- package/test/ingest-rules-header.test.ts +0 -100
- package/test/ingest-running-guard.test.ts +0 -136
- package/test/ingest-space.test.ts +0 -78
- package/test/ingest-window-rolls-unobserved.test.ts +0 -72
- package/test/ingest-wiring-guard.test.ts +0 -197
- package/test/instance-cache-lifecycle.test.ts +0 -110
- package/test/intake-mapping-parity.test.ts +0 -175
- package/test/integration-probes.test.ts +0 -103
- package/test/integration-runner.test.ts +0 -95
- package/test/item-ref.test.ts +0 -78
- package/test/journal-order-axis.test.ts +0 -80
- package/test/journal-read-discipline.test.ts +0 -186
- package/test/journal-retention.test.ts +0 -275
- package/test/journal-sort-axis.test.ts +0 -142
- package/test/journal-write-door.test.ts +0 -110
- package/test/journal-write-trend.test.ts +0 -115
- package/test/kernel-kind-guard.test.ts +0 -98
- package/test/knob-defaults.test.ts +0 -72
- package/test/kpi-baseline-db.test.ts +0 -216
- package/test/kpi-baseline.test.ts +0 -196
- package/test/kpi-fold.test.ts +0 -1456
- package/test/kpi-query-bench.test.ts +0 -130
- package/test/lens-own-places.test.ts +0 -50
- package/test/lineage-survives-restart.test.ts +0 -234
- package/test/live-cadence.test.ts +0 -170
- package/test/live-cursor-wiring.test.ts +0 -87
- package/test/live-feed-lease.test.ts +0 -63
- package/test/live-feed-registry.test.ts +0 -75
- package/test/live-kernel-facts.test.ts +0 -161
- package/test/live-mirror-parity.test.ts +0 -286
- package/test/load-meter.test.ts +0 -328
- package/test/local-declarations.test.ts +0 -662
- package/test/log-stamp.test.ts +0 -59
- package/test/loop-lag.test.ts +0 -82
- package/test/master-to-twin.test.ts +0 -279
- package/test/measured-yield.test.ts +0 -90
- package/test/merge-streams.test.ts +0 -70
- package/test/mirror-resumes-from-checkpoint.test.ts +0 -192
- package/test/model-basis.test.ts +0 -141
- package/test/model-gap.test.ts +0 -153
- package/test/model-refusal.test.ts +0 -75
- package/test/model-vocabulary.test.ts +0 -114
- package/test/move-space.test.ts +0 -149
- package/test/mutation-gate.test.ts +0 -122
- package/test/oee-accumulator.test.ts +0 -204
- package/test/oee-site-calendar.test.ts +0 -146
- package/test/open-doors.test.ts +0 -197
- package/test/operation-basis.test.ts +0 -127
- package/test/operational-vocabulary.test.ts +0 -108
- package/test/operations-capability-db.test.ts +0 -165
- package/test/order-and-transaction-split.test.ts +0 -107
- package/test/order-fold-host.test.ts +0 -112
- package/test/producer-guard.test.ts +0 -66
- package/test/project-structure-db.test.ts +0 -226
- package/test/projection-reaches-screen.test.ts +0 -277
- package/test/projection-reads-declared.test.ts +0 -109
- package/test/property-effects.test.ts +0 -128
- package/test/provenance-promotion.test.ts +0 -99
- package/test/read-failure-ledger.test.ts +0 -106
- package/test/read-failure-visible.test.ts +0 -145
- package/test/read-time.test.ts +0 -118
- package/test/reference-fill.test.ts +0 -235
- package/test/reference-grounding.test.ts +0 -70
- package/test/reference-hook.test.ts +0 -177
- package/test/reference-links.test.ts +0 -56
- package/test/reference-reconciliation.test.ts +0 -39
- package/test/registry-key-guard.test.ts +0 -80
- package/test/rejection-carries-event-id.test.ts +0 -101
- package/test/rename-twin.test.ts +0 -89
- package/test/resolve-ts-siblings.mjs +0 -52
- package/test/restart-policy.test.ts +0 -111
- package/test/resync-origin-site.test.ts +0 -83
- package/test/retry-plan.test.ts +0 -139
- package/test/revision-axis.test.ts +0 -110
- package/test/runtime-key.test.ts +0 -66
- package/test/save-intake-collision.test.ts +0 -91
- package/test/scale-twin-bench.test.ts +0 -103
- package/test/secrets-at-rest.test.ts +0 -135
- package/test/snapshot-freshness.test.ts +0 -60
- package/test/source-outcome-audit.test.ts +0 -104
- package/test/space-integrity.test.ts +0 -223
- package/test/space-representation.test.ts +0 -66
- package/test/space-scope-includes-stopped.test.ts +0 -55
- package/test/spec-coverage.test.ts +0 -113
- package/test/stage-path.test.ts +0 -95
- package/test/standard-coverage.test.ts +0 -148
- package/test/state-axes.test.ts +0 -74
- package/test/status-tally.test.ts +0 -55
- package/test/streamline-e2e.test.ts +0 -76
- package/test/structure-revision-db.test.ts +0 -315
- package/test/subject-fast-path.test.ts +0 -148
- package/test/subject-read-model.test.ts +0 -175
- package/test/sweep-dispatch.test.ts +0 -191
- package/test/tenant-registry-db.test.ts +0 -149
- package/test/time-range.test.ts +0 -152
- package/test/touched-items.test.ts +0 -61
- package/test/twin-audit.test.ts +0 -83
- package/test/twin-event-keys.test.ts +0 -193
- package/test/twin-model-item-db.test.ts +0 -319
- package/test/twin-model-parent-relation-db.test.ts +0 -125
- package/test/twin-model-tree-db.test.ts +0 -209
- package/test/twin-origin-resync.test.ts +0 -133
- package/test/twin-state-channel.test.ts +0 -111
- package/test/vocabulary-guard.test.ts +0 -43
- package/test/warm-start-seam.test.ts +0 -162
- package/test/warm-start.test.ts +0 -297
- package/test/withheld-door.test.ts +0 -95
- package/test/yield-loop.test.ts +0 -199
- package/tsconfig.json +0 -15
- package/tsconfig.shared.json +0 -23
- package/tsconfig.shared.tsbuildinfo +0 -1
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 첫 봉투가 몇 번부터 시작했는지 남기는가.
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 있었나 (2026-09-06 실측, 인티그레이션 레인이 찾음) ────────────────
|
|
5
|
-
* plant 아웃박스의 1~20 이 400 으로 거절돼 그 연결에 커서가 없었다. 다음에 온 21번이 첫 봉투로
|
|
6
|
-
* 앉으며 커서가 21 로 뛰었다.
|
|
7
|
-
*
|
|
8
|
-
* 커서가 0 이었다면 같은 21이 구멍이었다. 둘의 차이가 그 결함의 전부다. 스무 건이 구멍으로도 안
|
|
9
|
-
* 세어지고, 경고도 없고, backfill 대상도 아니었다 — backfill 은 커서부터 묻는다.
|
|
10
|
-
*/
|
|
11
|
-
import { test } from 'node:test'
|
|
12
|
-
import assert from 'node:assert/strict'
|
|
13
|
-
|
|
14
|
-
import { firstSeqOf, seqOf, takeInSequence, withSeq } from '../server/service/reference/hook-contract.js'
|
|
15
|
-
|
|
16
|
-
const item = (seq: number) => ({ seq, record: { at: '2026-09-06T00:00:00.000Z' } })
|
|
17
|
-
|
|
18
|
-
test('★ 커서가 없는 연결에 21번이 오면 그 앞 20건을 못 봤다고 말한다', () => {
|
|
19
|
-
const got = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: undefined })
|
|
20
|
-
|
|
21
|
-
assert.equal(got.firstUnseenBefore, 20, '못 본 개수를 안 말하면 그 사실이 아무 데도 안 남는다')
|
|
22
|
-
assert.equal(got.advancedSeq, 21)
|
|
23
|
-
assert.equal(got.gap, undefined, '첫 봉투를 구멍으로 만들지 않는다 — 거절할 근거가 없다')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('★ 커서가 0 이면 같은 21번이 구멍이다 — 둘의 차이가 이 결함의 전부다', () => {
|
|
27
|
-
const got = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: 0 })
|
|
28
|
-
|
|
29
|
-
assert.equal(got.gap?.expectedSeq, 1)
|
|
30
|
-
assert.equal(got.firstUnseenBefore, undefined, '구멍은 첫 봉투가 아니다')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('1번부터 시작한 연결은 못 본 것이 없다 — 그 칸을 내지 않는다', () => {
|
|
34
|
-
const got = takeInSequence({ items: [item(1)], scope: 'EVENTS', lastSeq: undefined })
|
|
35
|
-
|
|
36
|
-
assert.equal(got.firstUnseenBefore, undefined, '0 을 내면 화면이 말할 것 없는 줄을 그린다')
|
|
37
|
-
assert.equal(got.advancedSeq, 1)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test('늦게 붙은 연결도 수만 말한다 — 손실이라고 하지 않는다', () => {
|
|
41
|
-
/*
|
|
42
|
-
* 원본이 오래 돌고 있는데 트윈을 나중에 붙이면 seq 4,712 부터 받는 것이 정상이고, 그 아래
|
|
43
|
-
* 4,711 개는 애초에 안 받기로 한 것이다. 계약은 둘을 구별할 수 없으므로 수만 낸다.
|
|
44
|
-
*/
|
|
45
|
-
const got = takeInSequence({ items: [item(4712)], scope: 'EVENTS', lastSeq: undefined })
|
|
46
|
-
assert.equal(got.firstUnseenBefore, 4711)
|
|
47
|
-
assert.equal(got.advancedSeq, 4712, '늦게 붙은 것을 거절하지 않는다')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('이어받는 봉투에는 그 칸이 없다', () => {
|
|
51
|
-
const got = takeInSequence({ items: [item(49)], scope: 'EVENTS', lastSeq: 48 })
|
|
52
|
-
assert.equal(got.firstUnseenBefore, undefined)
|
|
53
|
-
assert.equal(got.advancedSeq, 49)
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
test('★ 이 테스트가 실제로 무언가를 잰다 — 견준 것이 0이면 빨강', () => {
|
|
57
|
-
const first = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: undefined })
|
|
58
|
-
const cont = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: 20 })
|
|
59
|
-
assert.notEqual(first.firstUnseenBefore, cont.firstUnseenBefore, '첫 봉투와 이어받은 봉투가 같아졌다')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
/* ── 몇 번부터 받기 시작했나를 커서에 적는다 ────────────────────────────────── */
|
|
63
|
-
|
|
64
|
-
test('★ 첫 봉투의 번호를 커서에 적는다 — 「앞이 비었나」를 나중에 답하려면 이 값이 있어야 한다', () => {
|
|
65
|
-
const got = takeInSequence({ items: [item(21)], scope: 'EVENTS', lastSeq: undefined })
|
|
66
|
-
assert.equal(got.firstSeq, 21)
|
|
67
|
-
|
|
68
|
-
const cursor = withSeq({}, 'mes-line-a::EVENTS', got.advancedSeq!, got.firstSeq)
|
|
69
|
-
assert.equal(firstSeqOf(cursor, 'mes-line-a::EVENTS'), 21)
|
|
70
|
-
assert.equal(seqOf(cursor, 'mes-line-a::EVENTS'), 21)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test('★ 한 번 적힌 firstSeq 는 안 바뀐다 — 덮으면 「앞이 비었나」를 다시 못 묻는다', () => {
|
|
74
|
-
/*
|
|
75
|
-
* 커서가 나아간 뒤에 이 값을 만들면 그 번호는 첫 번호가 아니라 그때 마침 온 번호다.
|
|
76
|
-
*/
|
|
77
|
-
let cursor = withSeq({}, 'S', 21, 21)
|
|
78
|
-
cursor = withSeq(cursor, 'S', 22, 22)
|
|
79
|
-
cursor = withSeq(cursor, 'S', 48, 48)
|
|
80
|
-
|
|
81
|
-
assert.equal(firstSeqOf(cursor, 'S'), 21, 'firstSeq 가 덮였다')
|
|
82
|
-
assert.equal(seqOf(cursor, 'S'), 48)
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
test('★ 적힌 적이 없으면 모른다고 말한다 — 0 으로 답하면 1번부터 다 받은 것으로 읽힌다', () => {
|
|
86
|
-
const old = withSeq({}, 'S', 48)
|
|
87
|
-
assert.equal(firstSeqOf(old, 'S'), undefined)
|
|
88
|
-
assert.equal(seqOf(old, 'S'), 48, '옛 커서의 번호는 그대로 읽힌다')
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
test('이어받는 봉투는 firstSeq 를 안 낸다 — 그 값을 만들 근거가 없다', () => {
|
|
92
|
-
const got = takeInSequence({ items: [item(49)], scope: 'EVENTS', lastSeq: 48 })
|
|
93
|
-
assert.equal(got.firstSeq, undefined)
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
test('1번부터 시작한 연결도 firstSeq 를 적는다 — 못 본 것이 없다는 사실도 사실이다', () => {
|
|
97
|
-
const got = takeInSequence({ items: [item(1)], scope: 'EVENTS', lastSeq: undefined })
|
|
98
|
-
assert.equal(got.firstSeq, 1)
|
|
99
|
-
assert.equal(got.firstUnseenBefore, undefined, '못 본 것이 0이면 그 칸은 안 낸다')
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('★ 밑에서부터 다시 받으면 그 번호가 firstSeq 가 된다 — from: 0 은 first 판정이 아니다', () => {
|
|
103
|
-
/*
|
|
104
|
-
* 실제로 났다(2026-09-06). `fillTwinScope(from: 0)` 으로 잃은 구간을 되찾았는데 커서에
|
|
105
|
-
* `firstSeq` 가 안 적혔다. 커서가 0 이라 seq 1 이 `next` 판정이었고 `first` 가 아니었기 때문이다.
|
|
106
|
-
*
|
|
107
|
-
* 그런데 우리는 실제로 1번부터 갖게 됐다. `lowestSeq` 가 그 사실을 낸다.
|
|
108
|
-
*/
|
|
109
|
-
const got = takeInSequence({ items: [item(1), item(2), item(3)], scope: 'EVENTS', lastSeq: 0 })
|
|
110
|
-
|
|
111
|
-
assert.equal(got.firstSeq, undefined, '커서가 있었으므로 첫 봉투가 아니다')
|
|
112
|
-
assert.equal(got.lowestSeq, 1, '받아들인 것 중 가장 낮은 번호')
|
|
113
|
-
assert.equal(got.advancedSeq, 3)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
test('가장 낮은 번호는 하나도 못 받으면 없다', () => {
|
|
117
|
-
const got = takeInSequence({ items: [item(9)], scope: 'EVENTS', lastSeq: 4 })
|
|
118
|
-
assert.equal(got.lowestSeq, undefined, '구멍이라 아무것도 안 받았다')
|
|
119
|
-
assert.equal(got.gap?.expectedSeq, 5)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
/* ── 봉투의 시각 ────────────────────────────────────────────────────────────── */
|
|
123
|
-
|
|
124
|
-
test('★ 시각을 안 싣는 레코드에 봉투의 시각을 얹는다 — 안 얹으면 다시 받을 때마다 새 사실이 된다', () => {
|
|
125
|
-
/*
|
|
126
|
-
* [측정함 2026-09-06] `fillTwinScope(from: 0)` 을 두 번 돌렸더니 mes-line-a 저널이 40 → 69건이
|
|
127
|
-
* 되고 중복이 27가지 생겼다. 시각을 안 싣는 레코드는 ingest 시각으로 떨어지는데 그 값이 실행할
|
|
128
|
-
* 때마다 달라서 fact identity 가 매번 바뀐다. 시각을 말하는 12건만 되풀이로 걸러졌다.
|
|
129
|
-
*
|
|
130
|
-
* plant 아웃박스는 그 시각을 봉투에 실어 보내고 있었다. 갈 자리가 없어서 버려지던 값이다.
|
|
131
|
-
*/
|
|
132
|
-
const got = takeInSequence({
|
|
133
|
-
items: [{ seq: 1, record: { subjectId: 'L', decision: 'scrap' }, at: '2026-09-01T00:00:00.000Z' }],
|
|
134
|
-
scope: 'EVENTS',
|
|
135
|
-
lastSeq: 0
|
|
136
|
-
})
|
|
137
|
-
assert.equal((got.records[0] as any).at, '2026-09-01T00:00:00.000Z')
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
test('★ 레코드가 자기 시각을 말하면 그것이 이긴다 — 봉투는 「언제 보냈나」에 가깝다', () => {
|
|
141
|
-
const got = takeInSequence({
|
|
142
|
-
items: [{ seq: 1, record: { subjectId: 'L', decision: 'scrap', at: '2026-08-30T00:00:00.000Z' }, at: '2026-09-01T00:00:00.000Z' }],
|
|
143
|
-
scope: 'EVENTS',
|
|
144
|
-
lastSeq: 0
|
|
145
|
-
})
|
|
146
|
-
assert.equal((got.records[0] as any).at, '2026-08-30T00:00:00.000Z')
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
test('봉투가 시각을 안 말하면 레코드를 그대로 둔다 — 없는 값을 만들지 않는다', () => {
|
|
150
|
-
const rec = { subjectId: 'L', decision: 'scrap' }
|
|
151
|
-
const got = takeInSequence({ items: [{ seq: 1, record: rec }], scope: 'EVENTS', lastSeq: 0 })
|
|
152
|
-
assert.equal(got.records[0], rec, '새 객체를 만들 이유가 없다')
|
|
153
|
-
})
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ★ 저장본 뒤의 사건 — 계산했든 못 했든 **말한다**.
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 침묵했나 (2026-08-28 실측) ───────────────────────────────────────
|
|
5
|
-
* 미러는 저장본에서 상태를 되세우고 그 뒤의 사건만 따라 계산한다. 그 자리가 이랬다.
|
|
6
|
-
*
|
|
7
|
-
* 10:34:37 found cached state → revision 3372228
|
|
8
|
-
* 10:34:37 resumed from the stored fold seed at revision 3372228 — items 2665 · orders 4794
|
|
9
|
-
*
|
|
10
|
-
* 그 시각 저널의 머리는 3,374,894였다. 2,666건이 저장본보다 앞서 있었는데 **계산했다는 줄도, 못
|
|
11
|
-
* 했다는 줄도 없었다.** 갈래가 셋인데 말하는 갈래가 하나뿐이었기 때문이다.
|
|
12
|
-
*
|
|
13
|
-
* ① 계산해서 나아갔다 → 말한다
|
|
14
|
-
* ② 계산이 오류로 실패했다 → 말한다
|
|
15
|
-
* ③ 오류 없이 끝났는데 그대로 → **아무 말도 없었다** ← 여기
|
|
16
|
-
* ④ 저널 머리를 못 읽었다 → **아무 말도 없었다** ← 여기
|
|
17
|
-
*
|
|
18
|
-
* ③④에서 트윈은 그 사건들의 결과 없이 뜨고, 화면은 그것을 「없는 사실」로 그린다.
|
|
19
|
-
*/
|
|
20
|
-
import { test } from 'node:test'
|
|
21
|
-
import assert from 'node:assert/strict'
|
|
22
|
-
import { readFileSync } from 'node:fs'
|
|
23
|
-
import { join } from 'node:path'
|
|
24
|
-
|
|
25
|
-
const engine = readFileSync(join(import.meta.dirname, '..', 'server', 'engine', 'twin-engine.ts'), 'utf8')
|
|
26
|
-
|
|
27
|
-
test('★ 저널 머리를 못 읽었으면 그 사실을 말하고 그대로 뜬다', () => {
|
|
28
|
-
assert.match(engine, /if \(!tip\) \{/, '못 읽은 갈래가 있어야 한다')
|
|
29
|
-
assert.match(engine, /could not read the journal tip/, '무엇을 못 했는지 말해야 한다')
|
|
30
|
-
assert.match(engine, /without folding what came after it/, '무엇을 잃는지 말해야 한다')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
test('★ 계산이 아무 일도 하지 않았으면 그것도 말한다', () => {
|
|
34
|
-
assert.match(engine, /\} else if \(!failed\) \{/, '오류가 아닌데 나아가지 않은 갈래가 있어야 한다')
|
|
35
|
-
assert.match(engine, /left the seed at revision/)
|
|
36
|
-
/* 오류로 실패한 것을 두 번 말하지 않는다 — 이미 위에서 말했다. */
|
|
37
|
-
assert.match(engine, /let failed: string \| undefined/)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test('★ 부팅마다 부르는 질의를 관계로 묻지 않는다 — 이 파일이 스스로 금지한 모양이다', () => {
|
|
41
|
-
/*
|
|
42
|
-
* `findOne({ where: { domain: { id } } })` 는 안쪽에 상한이 없는 두 단계 질의가 된다(실측 12초,
|
|
43
|
-
* 저널 375만 행). `tipOf` 는 부팅마다 트윈마다 불리므로 그 한 번이 부팅을 붙잡는다.
|
|
44
|
-
*/
|
|
45
|
-
const from = engine.indexOf('private static async tipOf')
|
|
46
|
-
assert.ok(from > 0, 'tipOf 가 있어야 한다')
|
|
47
|
-
const tipOf = engine.slice(from, from + 2600)
|
|
48
|
-
/* 주석에 인용된 나쁜 모양과 헷갈리지 않게, 실제 코드 모양만 본다. */
|
|
49
|
-
assert.doesNotMatch(tipOf, /findOne\(\{ where: \{ domain: \{ id: domainId \}/, '관계로 묻는 모양이 남아 있다')
|
|
50
|
-
assert.match(tipOf, /createQueryBuilder\('e'\)/)
|
|
51
|
-
assert.match(tipOf, /orderBy\('e\.revision', 'DESC'\)/)
|
|
52
|
-
assert.match(tipOf, /\.limit\(1\)/)
|
|
53
|
-
})
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Folding a span that crosses a daylight-saving transition.
|
|
3
|
-
*
|
|
4
|
-
* ── Why this is worth a test ───────────────────────────────────────────────
|
|
5
|
-
* `utcOffsetMinutes` on the model is one number, resolved when the host starts the twin.
|
|
6
|
-
* Folding walks a past span, so a year of facts gets read with whatever offset happened to
|
|
7
|
-
* be current at start — and a year holds two transitions. Facts after one land an hour off
|
|
8
|
-
* and are counted into the neighbouring shift. Nothing errors; shift performance is simply
|
|
9
|
-
* wrong, and plausible.
|
|
10
|
-
*
|
|
11
|
-
* `America/Tijuana` is already in this deployment's data (PST -480 / PDT -420), so this is
|
|
12
|
-
* not a hypothetical.
|
|
13
|
-
*/
|
|
14
|
-
import test from 'node:test'
|
|
15
|
-
import assert from 'node:assert/strict'
|
|
16
|
-
|
|
17
|
-
import { foldKpi } from '../server/engine/kpi-fold.js'
|
|
18
|
-
|
|
19
|
-
/* Two shifts that between them cover the day, so a misread hour lands in the other one. */
|
|
20
|
-
const SHIFTS = [
|
|
21
|
-
{ id: 'day', fromTime: '06:00', toTime: '18:00' },
|
|
22
|
-
{ id: 'night', fromDayOffset: 0, fromTime: '18:00', toDayOffset: 1, toTime: '06:00' }
|
|
23
|
-
] as any[]
|
|
24
|
-
|
|
25
|
-
/* Tijuana: PDT (-420) in July, PST (-480) in December. */
|
|
26
|
-
const SUMMER = Date.UTC(2026, 6, 15, 13, 30) // 06:30 local under -420 → day shift
|
|
27
|
-
const WINTER = Date.UTC(2026, 11, 15, 13, 30) // 05:30 local under -480 → night shift
|
|
28
|
-
|
|
29
|
-
/*
|
|
30
|
-
* The same rule the host uses (`utcOffsetOf`), written out here because that function lives
|
|
31
|
-
* beside code the test runner cannot load. Kept small and checked by its own test below, so a
|
|
32
|
-
* drift in it does not quietly turn the rest of this file green.
|
|
33
|
-
*/
|
|
34
|
-
function offsetAt(atMs: number): number | undefined {
|
|
35
|
-
const name = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Tijuana', timeZoneName: 'longOffset' })
|
|
36
|
-
.formatToParts(new Date(atMs))
|
|
37
|
-
.find(p => p.type === 'timeZoneName')?.value
|
|
38
|
-
|
|
39
|
-
const m = /^GMT([+-])(\d{1,2}):(\d{2})$/.exec(name ?? '')
|
|
40
|
-
|
|
41
|
-
return m ? (m[1] === '-' ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3])) : undefined
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/*
|
|
45
|
-
* A completed task is a transition, not one row — the fold counts throughput at completion
|
|
46
|
-
* and needs the earlier states to have a lead time to measure.
|
|
47
|
-
*/
|
|
48
|
-
const doneAt = (ms: number, id: string): any[] =>
|
|
49
|
-
['created', 'in-progress', 'completed'].map((status, i) => ({
|
|
50
|
-
eventType: 'task.status',
|
|
51
|
-
eventTime: new Date(ms - (2 - i) * 30_000).toISOString(),
|
|
52
|
-
payload: { data: { taskId: id, status } }
|
|
53
|
-
}))
|
|
54
|
-
|
|
55
|
-
function shiftsOf(events: any[], options: any) {
|
|
56
|
-
const out = foldKpi(events, { fromMs: Date.UTC(2026, 0, 1), toMs: Date.UTC(2027, 0, 1) } as any, {
|
|
57
|
-
groupBy: 'shift',
|
|
58
|
-
shifts: SHIFTS,
|
|
59
|
-
...options
|
|
60
|
-
} as any)
|
|
61
|
-
|
|
62
|
-
return (out as any)?.groups?.items?.map((g: any) => g.key) ?? []
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
test('the probe itself reads Tijuana as -420 in July and -480 in December', () => {
|
|
66
|
-
/* If this drifts, the rest of the file is measuring nothing. */
|
|
67
|
-
assert.equal(offsetAt(SUMMER), -420)
|
|
68
|
-
assert.equal(offsetAt(WINTER), -480)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test('a fixed offset puts a December fact in the wrong shift — the defect', () => {
|
|
72
|
-
/*
|
|
73
|
-
* Both facts are 13:30 UTC. Read with the summer offset the December one looks like
|
|
74
|
-
* 06:30 local and lands on the day shift, when locally it was 05:30 and belongs to the
|
|
75
|
-
* night shift.
|
|
76
|
-
*/
|
|
77
|
-
const keys = shiftsOf([...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420 })
|
|
78
|
-
|
|
79
|
-
assert.deepEqual(keys, ['day'])
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('offsetAt resolves per fact, so the December fact lands on the night shift', () => {
|
|
83
|
-
const keys = shiftsOf([...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420, offsetAt })
|
|
84
|
-
|
|
85
|
-
assert.deepEqual(keys, ['night'])
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test('the same span read per instant splits across both shifts, not one', () => {
|
|
89
|
-
const keys = shiftsOf([...doneAt(SUMMER, 's'), ...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420, offsetAt }).sort()
|
|
90
|
-
|
|
91
|
-
assert.deepEqual(keys, ['day', 'night'])
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test('offsetAt returning undefined falls back to the number rather than to UTC', () => {
|
|
95
|
-
/*
|
|
96
|
-
* Not knowing the offset for an instant is not the same as the instant being UTC.
|
|
97
|
-
* Falling through to the declared number keeps the previous behaviour instead of
|
|
98
|
-
* silently asserting Greenwich.
|
|
99
|
-
*/
|
|
100
|
-
const keys = shiftsOf([...doneAt(WINTER, 'w')], { utcOffsetMinutes: -420, offsetAt: () => undefined })
|
|
101
|
-
|
|
102
|
-
assert.deepEqual(keys, ['day'])
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test('no offset at all still folds, reading times as UTC as the contract states', () => {
|
|
106
|
-
const keys = shiftsOf([...doneAt(WINTER, 'w')], {})
|
|
107
|
-
|
|
108
|
-
/* 13:30 UTC is inside 06:00-18:00. */
|
|
109
|
-
assert.deepEqual(keys, ['day'])
|
|
110
|
-
})
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 예측 지표 — **스냅샷에서 무엇을 세는가.**
|
|
3
|
-
*
|
|
4
|
-
* 여기서 틀리면 예측 그래프가 알리지 않고 다른 것을 그린다(오류 없이 곡선이 나온다). 특히 에너지 지표는
|
|
5
|
-
* 「없음」과 「0」이 같은 그림이 되기 쉬워서, 그 경계를 명시로 잡아 둔다.
|
|
6
|
-
*/
|
|
7
|
-
import { test } from 'node:test'
|
|
8
|
-
import assert from 'node:assert/strict'
|
|
9
|
-
|
|
10
|
-
import { FORECAST_METRICS, metricsForKind } from '../server/service/twin-forecast/forecast-metrics.ts'
|
|
11
|
-
|
|
12
|
-
test('물류 지표 — 기존 여섯은 그대로 센다', () => {
|
|
13
|
-
const s = {
|
|
14
|
-
items: [1, 2, 3],
|
|
15
|
-
locations: [{ occupancy: 2 }, { occupancy: 5 }],
|
|
16
|
-
tasks: [{}, {}],
|
|
17
|
-
orders: [{ status: 'shipped' }, { status: 'picking' }, { status: 'cancelled' }]
|
|
18
|
-
}
|
|
19
|
-
assert.equal(FORECAST_METRICS.items(s), 3)
|
|
20
|
-
assert.equal(FORECAST_METRICS.occupancy(s), 7)
|
|
21
|
-
assert.equal(FORECAST_METRICS.tasks(s), 2)
|
|
22
|
-
assert.equal(FORECAST_METRICS.orders(s), 3)
|
|
23
|
-
assert.equal(FORECAST_METRICS.shipped(s), 1)
|
|
24
|
-
/* 취소는 백로그가 아니다 — 대기와 취소를 같이 세면 「안 빠지는 일감」이 부풀려진다. */
|
|
25
|
-
assert.equal(FORECAST_METRICS.backlog(s), 1)
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
/*
|
|
29
|
-
* ── 받는 오더는 흐름 지표에서 빠진다 (2026-09-19, ADR-0076 보탬 ⑥) ──────────
|
|
30
|
-
*
|
|
31
|
-
* 입고 · 반품 입고는 도착이 세우고 도착이 이행한다. 세면 「나간 것」이 받을 때마다 늘고 백로그는
|
|
32
|
-
* 영영 안 줄어든다 — 그래프가 있지도 않은 일을 그린다.
|
|
33
|
-
*/
|
|
34
|
-
test('★ 입고 · 반품 입고는 세지 않는다 — 그 오더는 나가지 않는다', () => {
|
|
35
|
-
const s = {
|
|
36
|
-
orders: [
|
|
37
|
-
{ kind: 'outbound', status: 'shipped' },
|
|
38
|
-
{ kind: 'outbound', status: 'picking' },
|
|
39
|
-
{ kind: 'inbound', status: 'received', requested: 10, fulfilled: 10 },
|
|
40
|
-
{ kind: 'return', status: 'received', requested: 3, fulfilled: 3 }
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
assert.equal(FORECAST_METRICS.orders(s), 2, '흐름 오더 둘만 센다')
|
|
44
|
-
assert.equal(FORECAST_METRICS.shipped(s), 1)
|
|
45
|
-
assert.equal(FORECAST_METRICS.backlog(s), 1, '받은 오더가 백로그에 남으면 곡선이 안 내려간다')
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
/*
|
|
49
|
-
* ── 백로그는 「종결인가」로 묻는다 ──────────────────────────────────────────
|
|
50
|
-
*
|
|
51
|
-
* 낱말 둘로 물을 때 원본이 `completed` 를 쓰는 미러에서 끝난 오더가 영영 백로그에 남았다. `status` 는
|
|
52
|
-
* 열린 축이고 실 시스템은 자기 낱말을 쓴다 — 이 셈은 낱말이 아니라 양을 1차 근거로 본다.
|
|
53
|
-
*/
|
|
54
|
-
test('★ 원본의 낱말로 끝난 오더도 백로그에서 빠진다', () => {
|
|
55
|
-
const s = {
|
|
56
|
-
orders: [
|
|
57
|
-
{ kind: 'outbound', status: 'completed', requested: 5, fulfilled: 5 },
|
|
58
|
-
{ kind: 'outbound', status: 'FINISHED', requested: 2, fulfilled: 2 },
|
|
59
|
-
{ kind: 'outbound', status: 'picking', requested: 4, fulfilled: 1 }
|
|
60
|
-
]
|
|
61
|
-
}
|
|
62
|
-
assert.equal(FORECAST_METRICS.backlog(s), 1, '아직 안 끝난 하나만 백로그다')
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('낱말을 말하지 않은 오더는 그대로 센다 — 지금까지의 거동이다', () => {
|
|
66
|
-
const s = { orders: [{ status: 'picking' }, { status: 'shipped' }] }
|
|
67
|
-
assert.equal(FORECAST_METRICS.orders(s), 2)
|
|
68
|
-
assert.equal(FORECAST_METRICS.shipped(s), 1)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test('에너지 지표 — 커널이 낸 값을 그대로 읽는다(다시 계산하지 않는다)', () => {
|
|
72
|
-
const s = {
|
|
73
|
-
energy: {
|
|
74
|
-
peakSince: { kW: 812 },
|
|
75
|
-
open: { meanKW: 300, projectedKW: 455, projectionBasis: 'mean-so-far' },
|
|
76
|
-
closed: [{ overContract: true }, { overContract: false }, { overContract: true }]
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
assert.equal(FORECAST_METRICS.peakKW(s), 812)
|
|
80
|
-
assert.equal(FORECAST_METRICS.projectedKW(s), 455)
|
|
81
|
-
assert.equal(FORECAST_METRICS.overWindows(s), 2)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('투영이 아직 없으면 지금까지의 평균으로 — 0 으로 떨어뜨리지 않는다', () => {
|
|
85
|
-
assert.equal(FORECAST_METRICS.projectedKW({ energy: { open: { meanKW: 120 } } }), 120)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
test('에너지가 없는 스냅샷 — 세 지표 모두 0(그리지 않을 값이지, 관측한 0 이 아니다)', () => {
|
|
89
|
-
assert.equal(FORECAST_METRICS.peakKW({ items: [] }), 0)
|
|
90
|
-
assert.equal(FORECAST_METRICS.projectedKW({ items: [] }), 0)
|
|
91
|
-
assert.equal(FORECAST_METRICS.overWindows({ items: [] }), 0)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
test('계약을 모르면 초과 구간은 0 — overContract 가 없는 구간을 초과로 세지 않는다', () => {
|
|
95
|
-
const s = { energy: { closed: [{ maxKW: 900 }, { maxKW: 1200 }] } }
|
|
96
|
-
assert.equal(FORECAST_METRICS.overWindows(s), 0)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
test('종류별 지표 목록 — 에너지 트윈에는 물류 지표가 섞이지 않는다', () => {
|
|
100
|
-
const ems = metricsForKind('ems')
|
|
101
|
-
assert.deepEqual(ems, ['projectedKW', 'peakKW', 'overWindows'])
|
|
102
|
-
assert.equal(ems.includes('items' as any), false)
|
|
103
|
-
assert.equal(metricsForKind('wms').includes('items'), true)
|
|
104
|
-
/* 종류를 모르면 물류로 — 기존 트윈(kind 미선언)이 지표를 잃지 않는다. */
|
|
105
|
-
assert.equal(metricsForKind(undefined).includes('items'), true)
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
test('목록의 모든 키가 실제로 계산 가능하다 — 화면에 뜨는데 서버가 모르는 지표는 없다', () => {
|
|
109
|
-
for (const kind of ['ems', 'wms', 'mes', 'yms']) {
|
|
110
|
-
for (const key of metricsForKind(kind)) {
|
|
111
|
-
assert.equal(typeof (FORECAST_METRICS as any)[key], 'function', `${kind}:${key}`)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
/*
|
|
117
|
-
* ★ Folding does not move the count (ADR-0092 decision 3, ruling 2026-09-24).
|
|
118
|
-
* A twin folds finished orders out of its hot list and keeps kind × status counts. Counting the hot list
|
|
119
|
-
* alone dropped orders/shipped at every fold, and made the gap trend's actual increase negative.
|
|
120
|
-
*/
|
|
121
|
-
const hot = (orders: any[], foldedOrders?: any) => ({ orders, ...(foldedOrders ? { foldedOrders } : {}) })
|
|
122
|
-
|
|
123
|
-
test('folding the finished orders leaves orders and shipped where they were', () => {
|
|
124
|
-
const before = hot([
|
|
125
|
-
{ id: 'o1', kind: 'outbound', status: 'shipped' },
|
|
126
|
-
{ id: 'o2', kind: 'outbound', status: 'cancelled' },
|
|
127
|
-
{ id: 'o3', kind: 'outbound', status: 'picking' },
|
|
128
|
-
{ id: 'r1', kind: 'inbound', status: 'completed' }
|
|
129
|
-
])
|
|
130
|
-
const after = hot([{ id: 'o3', kind: 'outbound', status: 'picking' }], {
|
|
131
|
-
counts: { outbound: { shipped: 1, cancelled: 1 }, inbound: { completed: 1 } },
|
|
132
|
-
boundaryMs: 1
|
|
133
|
-
})
|
|
134
|
-
for (const m of ['orders', 'shipped', 'backlog'] as const) assert.equal(FORECAST_METRICS[m](after), FORECAST_METRICS[m](before), m)
|
|
135
|
-
assert.equal(FORECAST_METRICS.orders(after), 3, 'receiving orders are not counted, folded or not')
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
test('a fold between T and T+h keeps the actual increase from going negative', () => {
|
|
139
|
-
const atT = hot([{ kind: 'outbound', status: 'shipped' }, { kind: 'outbound', status: 'shipped' }, { kind: 'outbound', status: 'picking' }])
|
|
140
|
-
/* by T+h: one more shipped, and the three shipped have been folded */
|
|
141
|
-
const atTh = hot([{ kind: 'outbound', status: 'picking' }], { counts: { outbound: { shipped: 3 } }, boundaryMs: 2 })
|
|
142
|
-
assert.equal(FORECAST_METRICS.shipped(atTh) - FORECAST_METRICS.shipped(atT), 1)
|
|
143
|
-
assert.ok(FORECAST_METRICS.orders(atTh) - FORECAST_METRICS.orders(atT) >= 0)
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test('a twin with nothing folded counts as before', () => {
|
|
147
|
-
const s = hot([{ kind: 'outbound', status: 'shipped' }, { status: 'picking' }])
|
|
148
|
-
assert.equal(FORECAST_METRICS.orders(s), 2)
|
|
149
|
-
assert.equal(FORECAST_METRICS.shipped(s), 1)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
test('a folded order with no kind is a flow order, as it is in the hot list', () => {
|
|
153
|
-
const folded = hot([], { counts: { '': { shipped: 2 } }, boundaryMs: 1 })
|
|
154
|
-
const unfolded = hot([{ status: 'shipped' }, { status: 'shipped' }])
|
|
155
|
-
assert.equal(FORECAST_METRICS.orders(folded), FORECAST_METRICS.orders(unfolded))
|
|
156
|
-
assert.equal(FORECAST_METRICS.shipped(folded), FORECAST_METRICS.shipped(unfolded))
|
|
157
|
-
})
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 예측 틱 크기 — **지평선이 정하고, 양끝은 묶인다.**
|
|
3
|
-
*
|
|
4
|
-
* 이 규칙이 틀리면 예측이 알리지 않고 비싸지거나(1초 고정 시절 9,000틱·28.5초) 알리지 않고 성글어진다
|
|
5
|
-
* (짧은 작업이 한 틱에 삼켜진다). 둘 다 화면에는 그럴듯한 숫자로 나오므로 시험이 지킨다.
|
|
6
|
-
*/
|
|
7
|
-
import { test } from 'node:test'
|
|
8
|
-
import assert from 'node:assert/strict'
|
|
9
|
-
|
|
10
|
-
import { forecastStepMs, TARGET_TICKS_PER_RUN, MAX_STEP_MS, MIN_STEP_MS } from '../dist-server/service/twin-forecast/forecast-tuning.js'
|
|
11
|
-
|
|
12
|
-
test('기본 지평선(5분)은 5초 단위 — 실측으로 1초와 답이 같고 5배 싸다', () => {
|
|
13
|
-
assert.equal(forecastStepMs(300_000), 5_000)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
test('짧은 지평선은 더 잘게 — 1분이면 1초', () => {
|
|
17
|
-
assert.equal(forecastStepMs(60_000), 1_000)
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test('아주 짧아도 커널 시각 단위 아래로 내려가지 않는다', () => {
|
|
21
|
-
assert.equal(forecastStepMs(5_000), MIN_STEP_MS)
|
|
22
|
-
assert.equal(forecastStepMs(0), MIN_STEP_MS)
|
|
23
|
-
assert.equal(forecastStepMs(Number.NaN), MIN_STEP_MS)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('아무리 길어도 상한을 넘지 않는다 — 짧은 작업이 한 틱에 삼켜진다', () => {
|
|
27
|
-
assert.equal(forecastStepMs(2 * 60 * 60_000), MAX_STEP_MS)
|
|
28
|
-
assert.equal(forecastStepMs(24 * 60 * 60_000), MAX_STEP_MS)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('틱 크기는 초 단위로 떨어진다 — 3,333ms 는 사람도 화면도 설명할 수 없다', () => {
|
|
32
|
-
for (const h of [100_000, 200_000, 333_000, 777_000]) {
|
|
33
|
-
assert.equal(forecastStepMs(h) % 1000, 0, `${h}ms`)
|
|
34
|
-
}
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
test('상한에 걸리지 않는 구간에서는 목표 틱 수 근처로 돈다', () => {
|
|
38
|
-
for (const h of [60_000, 120_000, 300_000, 600_000]) {
|
|
39
|
-
const ticks = h / forecastStepMs(h)
|
|
40
|
-
assert.ok(ticks >= TARGET_TICKS_PER_RUN * 0.8 && ticks <= TARGET_TICKS_PER_RUN * 1.25, `${h}ms → ${ticks}틱`)
|
|
41
|
-
}
|
|
42
|
-
})
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 예측 정확도 진단(gap-analytics) — SPC 두 축(안정성·편향)·정확도·보정의 canonical 계산을 서버에서 단언.
|
|
3
|
-
* 클라에서 이전한 순수 함수라 DB 무의존, 결정적.
|
|
4
|
-
*/
|
|
5
|
-
import { test } from 'node:test'
|
|
6
|
-
import assert from 'node:assert/strict'
|
|
7
|
-
|
|
8
|
-
import { computeSpc, gapMetrics, learnedCalibration, aggregatePoints } from '../server/service/twin-forecast/gap-analytics.ts'
|
|
9
|
-
|
|
10
|
-
const pt = (error: number, actual = 100, inBand = true) => ({
|
|
11
|
-
t: 0, p50: actual - error, min: 0, max: 0, actual, error, inBand,
|
|
12
|
-
naiveAbsError: Math.abs(error) + 1, modelAbsError: Math.abs(error)
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
test('computeSpc — 표본 부족(n<4)은 insufficient', () => {
|
|
16
|
-
assert.equal(computeSpc([1, 2, 3]).insufficient, true)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
test('computeSpc — 안정적·무편향: 0 주변 교대 오차', () => {
|
|
20
|
-
const spc = computeSpc([1, -1, 1, -1, 1, -1, 1, -1])
|
|
21
|
-
assert.equal(spc.stable, true, '이탈·추세 없음 → 안정')
|
|
22
|
-
assert.equal(spc.biased, false, '평균 0 → 무편향')
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('computeSpc — 안정적이지만 편향: 계속 +6 (안정성과 편향은 별개 축)', () => {
|
|
26
|
-
const spc = computeSpc([6, 6, 6, 6, 6, 6])
|
|
27
|
-
assert.equal(spc.stable, true, '오차 폭 일정 → 안정')
|
|
28
|
-
assert.equal(spc.biased, true, '평균이 0 에서 크게 벗어남 → 편향')
|
|
29
|
-
assert.equal(spc.biasSign, 'under', 'CL>0 = 실제>예측 = 과소예측')
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('computeSpc — 급변(3σ 이탈)은 shift', () => {
|
|
33
|
-
const spc = computeSpc([0.1, -0.1, 0.1, -0.1, 0.1, -0.1, 10])
|
|
34
|
-
assert.equal(spc.instability, 'shift')
|
|
35
|
-
assert.equal(spc.stable, false)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
test('gapMetrics — 정확도=1−WMAPE, 커버리지·FVA', () => {
|
|
39
|
-
const m = gapMetrics([pt(1, 10, true), pt(-1, 10, false)])
|
|
40
|
-
assert.equal(m.accuracy, 90, '오차합2/실제합20=10% → 90%')
|
|
41
|
-
assert.equal(m.inBandCount, 1)
|
|
42
|
-
assert.equal(m.total, 2)
|
|
43
|
-
assert.equal(m.fva, 2, 'Σ(naiveAbs−modelAbs) = (2−1)+(2−1)')
|
|
44
|
-
assert.equal(m.fvaPct, 50, 'naive 오차 4 → 모델 2 → 50% 감소(예측 가치)')
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('learnedCalibration — biasShift=평균오차, 편향 제거로 정확도 개선', () => {
|
|
48
|
-
const pts = [pt(6, 100), pt(6, 100), pt(6, 100)]
|
|
49
|
-
const learn = learnedCalibration(pts, gapMetrics(pts))
|
|
50
|
-
assert.equal(learn.biasShift, 6, '학습된 편향 = 평균 오차')
|
|
51
|
-
assert.ok(learn.accAfter >= learn.accBefore, '편향 제거 → 정확도 유지·개선')
|
|
52
|
-
assert.equal(learn.accAfter, 100, '일정 편향을 완전 제거 → 잔차 0 → 100%')
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
/*
|
|
56
|
-
* ★ Nothing to divide by is not 100% (2026-09-24). With every actual at 0 the card read
|
|
57
|
-
* "100% accurate" beside "0 of 6 in band, mean error −20" — the installed build gave exactly that.
|
|
58
|
-
*/
|
|
59
|
-
test('gapMetrics — a window with no actual says the percentages are not measured, and keeps what is', () => {
|
|
60
|
-
const idle = Array.from({ length: 6 }, () => ({ t: 0, p50: 20, min: 15, max: 25, actual: 0, error: -20, inBand: false, naiveAbsError: 0, modelAbsError: 20 }))
|
|
61
|
-
const m = gapMetrics(idle)
|
|
62
|
-
assert.equal(m.accuracy, null, 'WMAPE has no denominator — not 100%')
|
|
63
|
-
assert.equal(m.fvaPct, null, 'the naive error is 0 — "adds nothing" would be invented')
|
|
64
|
-
/* Measured in that window, so they stay. */
|
|
65
|
-
assert.equal(m.mpe, -20)
|
|
66
|
-
assert.equal(m.inBandCount, 0)
|
|
67
|
-
assert.equal(m.total, 6)
|
|
68
|
-
assert.equal(m.fva, -120)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test('learnedCalibration — the bias is still learned with no actual; only the percentages are not', () => {
|
|
72
|
-
const idle = Array.from({ length: 4 }, () => pt(-20, 0, false))
|
|
73
|
-
const learn = learnedCalibration(idle, gapMetrics(idle))
|
|
74
|
-
assert.equal(learn.biasShift, -20, 'forecasting 20 over a standing line is a bias worth removing')
|
|
75
|
-
assert.equal(learn.accBefore, null)
|
|
76
|
-
assert.equal(learn.accAfter, null, 'no denominator was made up (it used to be `|| 1`)')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
/* 같은 격자 위의 점 — vantage 를 명시한다(예전 헬퍼는 t 를 늘 0 으로 두어 시간축을 전혀 시험하지 않았다). */
|
|
80
|
-
const at = (t: number, error: number, actual = 100) => ({ ...pt(error, actual), t })
|
|
81
|
-
|
|
82
|
-
test('aggregatePoints — 공동배치 합산(공장 총량)', () => {
|
|
83
|
-
const a = [at(10, 2, 30), at(20, 4, 30)]
|
|
84
|
-
const b = [at(10, 1, 20), at(20, 2, 20)]
|
|
85
|
-
const { points, droppedVantages } = aggregatePoints([a, b])
|
|
86
|
-
assert.equal(points.length, 2)
|
|
87
|
-
assert.equal(droppedVantages, 0)
|
|
88
|
-
assert.equal(points[0].actual, 50, '실제 30+20')
|
|
89
|
-
assert.equal(points[0].t, 10, '합산점의 시각은 그 vantage 다')
|
|
90
|
-
assert.equal(points[0].error, points[0].actual - points[0].p50, 'error = 실제−예측(합산 후 재계산)')
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
test('한 트윈이 못 잰 vantage 는 통째로 뺀다 — 빠진 트윈을 0 으로 세지 않는다', () => {
|
|
94
|
-
/*
|
|
95
|
-
* 예전에는 `max` 길이로 돌며 있는 것만 더했다. 그래서 이 경우 t=20 의 공장 총량이 20(b 뿐)으로
|
|
96
|
-
* 나왔다 — a 가 그 시각에 아무것도 안 했다는 뜻이 아니라, **못 쟀다**는 뜻인데도.
|
|
97
|
-
* 공장 합계 곡선이 꼬리에서 아래로 꺾이고, 그 곡선으로 예측 정확도를 판정했다.
|
|
98
|
-
*/
|
|
99
|
-
const a = [at(10, 2, 30), null]
|
|
100
|
-
const b = [at(10, 1, 20), at(20, 2, 20)]
|
|
101
|
-
const { points, droppedVantages } = aggregatePoints([a as any, b])
|
|
102
|
-
assert.equal(points.length, 1, '모두가 값을 가진 vantage 만 남는다')
|
|
103
|
-
assert.equal(points[0].t, 10)
|
|
104
|
-
assert.equal(droppedVantages, 1, '뺀 개수를 돌려줘야 화면이 "2점 중 1점" 이라고 말할 수 있다')
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('길이가 다르면 뒤쪽은 뺀다 — 남은 트윈만의 합을 공장 총량이라 부르지 않는다', () => {
|
|
108
|
-
const a = [at(10, 2, 30)]
|
|
109
|
-
const b = [at(10, 1, 20), at(20, 2, 20)]
|
|
110
|
-
const { points, droppedVantages } = aggregatePoints([a, b])
|
|
111
|
-
assert.equal(points.length, 1)
|
|
112
|
-
assert.equal(droppedVantages, 1)
|
|
113
|
-
})
|