@things-factory/headless-twin 10.0.15 → 10.0.18
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/attention-digest.d.ts +4 -4
- package/dist-server/engine/attention-digest.js +7 -7
- package/dist-server/engine/attention-digest.js.map +1 -1
- package/dist-server/engine/canonical-ingest.d.ts +18 -82
- package/dist-server/engine/canonical-ingest.js +183 -18
- 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/declared-stimulus.js +1 -1
- package/dist-server/engine/declared-stimulus.js.map +1 -1
- package/dist-server/engine/energy-topology.js +4 -4
- package/dist-server/engine/energy-topology.js.map +1 -1
- package/dist-server/engine/ingest-dedupe.d.ts +38 -0
- package/dist-server/engine/ingest-dedupe.js +145 -0
- package/dist-server/engine/ingest-dedupe.js.map +1 -0
- package/dist-server/engine/ingest-health.d.ts +90 -10
- package/dist-server/engine/ingest-health.js +125 -16
- package/dist-server/engine/ingest-health.js.map +1 -1
- package/dist-server/engine/integration-probes.d.ts +3 -3
- package/dist-server/engine/integration-probes.js +6 -6
- package/dist-server/engine/integration-probes.js.map +1 -1
- package/dist-server/engine/kpi-baseline.js +4 -4
- package/dist-server/engine/kpi-baseline.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +212 -14
- package/dist-server/engine/kpi-fold.js +374 -21
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +11 -11
- package/dist-server/engine/kpi-query.js +281 -36
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/live-feed-registry.d.ts +1 -1
- package/dist-server/engine/live-feed-registry.js +1 -1
- 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.map +1 -1
- package/dist-server/engine/local-declarations.d.ts +7 -7
- package/dist-server/engine/local-declarations.js +15 -14
- package/dist-server/engine/local-declarations.js.map +1 -1
- package/dist-server/engine/log.d.ts +1 -1
- package/dist-server/engine/log.js +1 -1
- package/dist-server/engine/log.js.map +1 -1
- package/dist-server/engine/measured-estimator.js +1 -1
- package/dist-server/engine/measured-estimator.js.map +1 -1
- package/dist-server/engine/measured-yield.js +1 -1
- package/dist-server/engine/measured-yield.js.map +1 -1
- package/dist-server/engine/model-basis.d.ts +1 -1
- package/dist-server/engine/model-basis.js +1 -1
- package/dist-server/engine/model-basis.js.map +1 -1
- package/dist-server/engine/model-gap.d.ts +3 -3
- package/dist-server/engine/model-gap.js +1 -1
- package/dist-server/engine/model-gap.js.map +1 -1
- package/dist-server/engine/model-vocabulary.js +3 -3
- package/dist-server/engine/model-vocabulary.js.map +1 -1
- package/dist-server/engine/oee-accumulator.d.ts +6 -5
- package/dist-server/engine/oee-accumulator.js +6 -5
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/property-effects.js +2 -2
- package/dist-server/engine/property-effects.js.map +1 -1
- package/dist-server/engine/read-time.d.ts +22 -0
- package/dist-server/engine/read-time.js +81 -0
- package/dist-server/engine/read-time.js.map +1 -0
- package/dist-server/engine/restart-policy.js +1 -1
- 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/spec-coverage.d.ts +2 -2
- package/dist-server/engine/spec-coverage.js +1 -1
- package/dist-server/engine/spec-coverage.js.map +1 -1
- package/dist-server/engine/stage-path.d.ts +4 -4
- package/dist-server/engine/stage-path.js +1 -1
- package/dist-server/engine/stage-path.js.map +1 -1
- package/dist-server/engine/state-axes.d.ts +1 -1
- package/dist-server/engine/state-axes.js +2 -2
- package/dist-server/engine/state-axes.js.map +1 -1
- package/dist-server/engine/travel-estimator.d.ts +2 -2
- package/dist-server/engine/travel-estimator.js +1 -1
- package/dist-server/engine/travel-estimator.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +307 -54
- package/dist-server/engine/twin-engine.js +822 -196
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/warm-start.d.ts +2 -2
- package/dist-server/engine/warm-start.js +5 -5
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-server/index.js +1 -1
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
- package/dist-server/migrations/index.js +7 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +69 -5
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/index.d.ts +2 -2
- package/dist-server/service/index.js +30 -24
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/control-routing.d.ts +1 -1
- package/dist-server/service/reference/control-routing.js +1 -1
- package/dist-server/service/reference/control-routing.js.map +1 -1
- package/dist-server/service/reference/discovery-result.d.ts +2 -2
- package/dist-server/service/reference/discovery-result.js +3 -3
- package/dist-server/service/reference/discovery-result.js.map +1 -1
- package/dist-server/service/reference/hook-contract.d.ts +43 -0
- package/dist-server/service/reference/hook-contract.js +59 -0
- package/dist-server/service/reference/hook-contract.js.map +1 -0
- package/dist-server/service/reference/ingest-space.d.ts +1 -1
- package/dist-server/service/reference/ingest-space.js +4 -4
- package/dist-server/service/reference/ingest-space.js.map +1 -1
- package/dist-server/service/reference/knob-defaults.d.ts +1 -1
- package/dist-server/service/reference/knob-defaults.js +4 -4
- package/dist-server/service/reference/knob-defaults.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +116 -9
- package/dist-server/service/reference/reference-adapter.js +27 -0
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-hook.d.ts +20 -0
- package/dist-server/service/reference/reference-hook.js +133 -0
- package/dist-server/service/reference/reference-hook.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +2 -2
- package/dist-server/service/reference/reference-live.js +76 -13
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +25 -16
- package/dist-server/service/reference/reference-master.js +35 -13
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +1 -1
- package/dist-server/service/reference/reference-resolver.js +12 -12
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/template-registry.d.ts +1 -1
- package/dist-server/service/reference/template-registry.js.map +1 -1
- package/dist-server/service/reference/twin-reference.d.ts +3 -3
- package/dist-server/service/reference/twin-reference.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
- package/dist-server/service/twin-audit/command-audit.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
- package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
- package/dist-server/service/twin-backfill/backfill-period-facts.d.ts +24 -0
- package/dist-server/service/twin-backfill/backfill-period-facts.js +96 -0
- package/dist-server/service/twin-backfill/backfill-period-facts.js.map +1 -0
- package/dist-server/service/twin-backfill/backfill-shape.d.ts +34 -0
- package/dist-server/service/twin-backfill/backfill-shape.js +75 -0
- package/dist-server/service/twin-backfill/backfill-shape.js.map +1 -0
- package/dist-server/service/twin-backfill/index.d.ts +2 -0
- package/dist-server/service/twin-backfill/index.js +6 -0
- package/dist-server/service/twin-backfill/index.js.map +1 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.d.ts +10 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js +44 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +1 -0
- package/dist-server/service/twin-control/twin-control-mutation.js +3 -3
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
- package/dist-server/service/twin-event/twin-event-keys.js +2 -2
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event-type.js +8 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +2 -2
- package/dist-server/service/twin-event/twin-event.js +32 -4
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
- package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
- package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
- package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
- package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
- package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
- package/dist-server/service/twin-journal/event-type-split.js +102 -0
- package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
- package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
- package/dist-server/service/twin-journal/journal-order.js +17 -0
- package/dist-server/service/twin-journal/journal-order.js.map +1 -0
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
- package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.js +2 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
- package/dist-server/service/twin-model/epcis-coverage.js +34 -3
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
- package/dist-server/service/twin-model/isa95-coverage.d.ts +12 -9
- package/dist-server/service/twin-model/isa95-coverage.js +33 -9
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
- package/dist-server/service/twin-model/item-ref.js +2 -2
- package/dist-server/service/twin-model/item-ref.js.map +1 -1
- package/dist-server/service/twin-model/name-index.d.ts +1 -1
- package/dist-server/service/twin-model/name-index.js +2 -2
- package/dist-server/service/twin-model/name-index.js.map +1 -1
- package/dist-server/service/twin-model/project-structure.d.ts +1 -1
- package/dist-server/service/twin-model/project-structure.js +7 -6
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.d.ts +2 -2
- package/dist-server/service/twin-model/twin-equipment.js +3 -3
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
- package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-location.d.ts +1 -1
- package/dist-server/service/twin-model/twin-location.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
- package/dist-server/service/twin-model/twin-model-item-query.js +15 -15
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
- package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
- package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-query.js +51 -30
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js +23 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
- package/dist-server/service/twin-model/twin-operation.js +1 -1
- package/dist-server/service/twin-model/twin-operation.js.map +1 -1
- package/dist-server/service/twin-space/move-space.js +2 -2
- package/dist-server/service/twin-space/move-space.js.map +1 -1
- package/dist-server/service/twin-space/representation-role.d.ts +2 -0
- package/dist-server/service/twin-space/representation-role.js +18 -0
- package/dist-server/service/twin-space/representation-role.js.map +1 -0
- package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
- package/dist-server/service/twin-space/space-integrity.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
- package/dist-server/service/twin-space/twin-space-representation.js +16 -0
- package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
- package/dist-server/service/twin-space/twin-space-resolver.js +37 -8
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-server/service/twin-structure/twin-structure.js +3 -3
- package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
- package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
- package/dist-server/service/twin-subject/event-subjects.js +137 -0
- package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
- package/dist-server/service/twin-subject/index.d.ts +9 -0
- package/dist-server/service/twin-subject/index.js +16 -0
- package/dist-server/service/twin-subject/index.js.map +1 -0
- package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
- package/dist-server/service/twin-subject/item-resolver.js +93 -0
- package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
- package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
- package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
- package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
- package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
- package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
- package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
- package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
- package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
- package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
- package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
- package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
- package/dist-shared/entity-delta.d.ts +1 -1
- package/dist-shared/entity-delta.js +4 -4
- package/dist-shared/entity-delta.js.map +1 -1
- package/dist-shared/touched-items.js +2 -2
- package/dist-shared/touched-items.js.map +1 -1
- package/dist-shared/twin-level.js +1 -1
- package/dist-shared/twin-level.js.map +1 -1
- package/package.json +7 -6
- package/server/engine/attention-digest.ts +9 -9
- package/server/engine/canonical-ingest.ts +202 -87
- package/server/engine/command-routing.ts +1 -1
- package/server/engine/declared-stimulus.ts +1 -1
- package/server/engine/energy-topology.ts +2 -2
- package/server/engine/ingest-dedupe.ts +149 -0
- package/server/engine/ingest-health.ts +171 -22
- package/server/engine/integration-probes.ts +7 -7
- package/server/engine/kpi-baseline.ts +4 -4
- package/server/engine/kpi-fold.ts +592 -41
- package/server/engine/kpi-query.ts +307 -48
- package/server/engine/live-feed-registry.ts +1 -1
- package/server/engine/load-meter.ts +1 -1
- package/server/engine/local-declarations.ts +15 -14
- package/server/engine/log.ts +1 -1
- package/server/engine/measured-estimator.ts +1 -1
- package/server/engine/measured-yield.ts +1 -1
- package/server/engine/model-basis.ts +2 -2
- package/server/engine/model-gap.ts +3 -3
- package/server/engine/model-vocabulary.ts +2 -2
- package/server/engine/oee-accumulator.ts +8 -6
- package/server/engine/property-effects.ts +2 -2
- package/server/engine/read-time.ts +77 -0
- package/server/engine/restart-policy.ts +1 -1
- package/server/engine/runtime-key.ts +1 -1
- package/server/engine/spec-coverage.ts +3 -3
- package/server/engine/stage-path.ts +5 -5
- package/server/engine/state-axes.ts +2 -2
- package/server/engine/travel-estimator.ts +2 -2
- package/server/engine/twin-engine.ts +906 -205
- package/server/engine/warm-start.ts +6 -6
- package/server/index.ts +1 -1
- package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
- package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
- package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
- package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
- package/server/migrations/index.ts +7 -1
- package/server/routes.ts +73 -5
- package/server/service/index.ts +7 -1
- package/server/service/reference/control-routing.ts +1 -1
- package/server/service/reference/discovery-result.ts +3 -3
- package/server/service/reference/hook-contract.ts +103 -0
- package/server/service/reference/ingest-space.ts +4 -4
- package/server/service/reference/knob-defaults.ts +4 -4
- package/server/service/reference/reference-adapter.ts +145 -10
- package/server/service/reference/reference-hook.ts +145 -0
- package/server/service/reference/reference-live.ts +87 -15
- package/server/service/reference/reference-master.ts +51 -20
- package/server/service/reference/reference-resolver.ts +11 -11
- package/server/service/reference/template-registry.ts +1 -1
- package/server/service/reference/twin-reference.ts +3 -3
- package/server/service/twin-attention/twin-attention-query.ts +1 -1
- package/server/service/twin-audit/command-audit.ts +1 -1
- package/server/service/twin-audit/twin-audit-event.ts +2 -2
- package/server/service/twin-audit/twin-audit-query.ts +1 -1
- package/server/service/twin-backfill/backfill-period-facts.ts +168 -0
- package/server/service/twin-backfill/backfill-shape.ts +86 -0
- package/server/service/twin-backfill/index.ts +3 -0
- package/server/service/twin-backfill/twin-backfill-resolver.ts +35 -0
- package/server/service/twin-control/twin-control-mutation.ts +2 -2
- package/server/service/twin-event/twin-event-keys.ts +2 -2
- package/server/service/twin-event/twin-event-type.ts +18 -1
- package/server/service/twin-event/twin-event.ts +32 -6
- package/server/service/twin-forecast/forecast-metrics.ts +1 -1
- package/server/service/twin-forecast/gap-analytics.ts +1 -1
- package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
- package/server/service/twin-instance/twin-instance.ts +4 -4
- package/server/service/twin-journal/event-type-split-shape.ts +112 -0
- package/server/service/twin-journal/event-type-split.ts +144 -0
- package/server/service/twin-journal/journal-order.ts +20 -0
- package/server/service/twin-journal/twin-journal-query.ts +224 -20
- package/server/service/twin-lifecycle/domain-catalog.ts +2 -1
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +58 -4
- package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
- package/server/service/twin-model/axis-journal-evidence.ts +1 -1
- package/server/service/twin-model/epcis-coverage.ts +34 -3
- package/server/service/twin-model/iec61850-coverage.ts +1 -1
- package/server/service/twin-model/isa95-coverage.ts +43 -16
- package/server/service/twin-model/item-ref.ts +2 -2
- package/server/service/twin-model/name-index.ts +2 -2
- package/server/service/twin-model/project-structure.ts +7 -6
- package/server/service/twin-model/twin-equipment.ts +16 -4
- package/server/service/twin-model/twin-lineage-query.ts +16 -5
- package/server/service/twin-model/twin-location.ts +1 -1
- package/server/service/twin-model/twin-model-item-query.ts +15 -15
- package/server/service/twin-model/twin-model-mutation.ts +4 -4
- package/server/service/twin-model/twin-model-query.ts +49 -28
- package/server/service/twin-model/twin-model-tree-query.ts +26 -2
- package/server/service/twin-model/twin-operation.ts +2 -2
- package/server/service/twin-space/move-space.ts +3 -3
- package/server/service/twin-space/representation-role.ts +15 -0
- package/server/service/twin-space/space-integrity.ts +1 -1
- package/server/service/twin-space/twin-space-representation.ts +37 -0
- package/server/service/twin-space/twin-space-resolver.ts +40 -8
- package/server/service/twin-structure/twin-structure.ts +3 -3
- package/server/service/twin-subject/event-subjects.ts +155 -0
- package/server/service/twin-subject/index.ts +10 -0
- package/server/service/twin-subject/item-resolver.ts +92 -0
- package/server/service/twin-subject/subject-query-shape.ts +84 -0
- package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
- package/server/service/twin-subject/twin-subject-event.ts +101 -0
- package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
- package/server/service/twin-subject/twin-subject-query.ts +210 -0
- package/server/service/twin-subject/twin-subject-writer.ts +189 -0
- package/server/service/twin-target/twin-target-resolver.ts +1 -1
- package/shared/entity-delta.ts +4 -4
- package/shared/touched-items.ts +2 -2
- package/shared/twin-level.ts +1 -1
- package/test/adopt-structure-live.test.ts +3 -3
- package/test/aggregate-not-findone.test.ts +112 -0
- package/test/attention-digest.test.ts +6 -6
- package/test/axis-read.test.ts +6 -5
- package/test/backfill-shape.test.ts +89 -0
- package/test/boot-resume.test.ts +20 -7
- package/test/broadcast-cost-baseline.test.ts +2 -1
- package/test/broadcast-period.test.ts +2 -2
- package/test/broadcast-queue-overflow.test.ts +244 -0
- package/test/canonical-ingest-vocabularies.test.ts +259 -8
- package/test/canonical-quantity-door.test.ts +2 -2
- package/test/capability-mapping.test.ts +1 -1
- package/test/checkpoint-refuses-empty.test.ts +2 -2
- package/test/connector-capability-declaration.test.ts +88 -0
- package/test/contract-layer-guard.test.ts +48 -0
- package/test/control-capability.test.ts +1 -1
- package/test/cursor-stall-not-read-failure.test.ts +2 -2
- package/test/declaration-reaches-model.test.ts +7 -5
- package/test/declared-stimulus.test.ts +4 -4
- package/test/discovery-result.test.ts +2 -2
- package/test/entity-delta.test.ts +4 -4
- package/test/equipment-identity-reaches.test.ts +57 -0
- package/test/event-subjects.test.ts +147 -0
- package/test/event-time-column.test.ts +3 -3
- package/test/event-type-split.test.ts +155 -0
- package/test/fact-scope-wiring.test.ts +96 -0
- package/test/fold-gap-speaks.test.ts +53 -0
- package/test/forecast-metrics.test.ts +1 -1
- package/test/forecast-tuning.test.ts +1 -1
- package/test/generated-not-counted.test.ts +99 -0
- package/test/ingest-bench.test.ts +2 -1
- package/test/ingest-health-engine.test.ts +2 -2
- package/test/ingest-health-wiring.test.ts +7 -5
- package/test/ingest-health.test.ts +6 -6
- package/test/ingest-history.test.ts +2 -2
- package/test/ingest-idempotent.test.ts +132 -0
- package/test/ingest-space.test.ts +3 -3
- package/test/ingest-wiring-guard.test.ts +197 -0
- package/test/integration-probes.test.ts +3 -3
- package/test/integration-runner.test.ts +1 -1
- package/test/item-ref.test.ts +1 -1
- package/test/journal-order-axis.test.ts +80 -0
- package/test/journal-read-discipline.test.ts +16 -6
- package/test/journal-retention.test.ts +171 -29
- package/test/journal-sort-axis.test.ts +1 -1
- package/test/journal-write-door.test.ts +3 -3
- package/test/journal-write-trend.test.ts +1 -1
- package/test/kernel-kind-guard.test.ts +3 -3
- package/test/knob-defaults.test.ts +1 -1
- package/test/kpi-baseline-db.test.ts +2 -2
- package/test/kpi-fold.test.ts +266 -14
- package/test/kpi-query-bench.test.ts +3 -3
- package/test/live-cursor-wiring.test.ts +6 -6
- package/test/live-kernel-facts.test.ts +11 -11
- package/test/live-mirror-parity.test.ts +9 -6
- package/test/load-meter.test.ts +1 -1
- package/test/local-declarations.test.ts +13 -12
- package/test/log-stamp.test.ts +1 -1
- package/test/master-to-twin.test.ts +2 -2
- package/test/measured-yield.test.ts +2 -2
- package/test/merge-streams.test.ts +70 -0
- package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
- package/test/model-basis.test.ts +2 -2
- package/test/model-gap.test.ts +4 -4
- package/test/model-vocabulary.test.ts +2 -2
- package/test/move-space.test.ts +1 -1
- package/test/oee-accumulator.test.ts +20 -5
- package/test/operational-vocabulary.test.ts +2 -2
- package/test/project-structure-db.test.ts +2 -2
- package/test/projection-reaches-screen.test.ts +7 -2
- package/test/projection-reads-declared.test.ts +109 -0
- package/test/property-effects.test.ts +10 -6
- package/test/read-failure-ledger.test.ts +106 -0
- package/test/read-failure-visible.test.ts +7 -7
- package/test/read-time.test.ts +118 -0
- package/test/reference-grounding.test.ts +1 -1
- package/test/reference-hook.test.ts +86 -0
- package/test/registry-key-guard.test.ts +1 -1
- package/test/rename-twin.test.ts +81 -0
- package/test/restart-policy.test.ts +3 -3
- package/test/resync-origin-site.test.ts +3 -3
- package/test/revision-axis.test.ts +14 -8
- package/test/runtime-key.test.ts +3 -3
- package/test/scale-twin-bench.test.ts +2 -1
- package/test/snapshot-freshness.test.ts +4 -4
- package/test/source-outcome-audit.test.ts +1 -1
- package/test/space-integrity.test.ts +1 -1
- package/test/space-representation.test.ts +66 -0
- package/test/space-scope-includes-stopped.test.ts +55 -0
- package/test/spec-coverage.test.ts +1 -1
- package/test/standard-coverage.test.ts +19 -21
- package/test/status-tally.test.ts +1 -1
- package/test/structure-revision-db.test.ts +7 -7
- package/test/subject-fast-path.test.ts +148 -0
- package/test/subject-read-model.test.ts +175 -0
- package/test/touched-items.test.ts +2 -2
- package/test/twin-audit.test.ts +1 -1
- package/test/twin-event-keys.test.ts +1 -1
- package/test/twin-model-item-db.test.ts +5 -5
- package/test/twin-model-tree-db.test.ts +35 -2
- package/test/twin-origin-resync.test.ts +1 -1
- package/test/vocabulary-guard.test.ts +1 -1
- package/test/warm-start-seam.test.ts +1 -1
- package/test/warm-start.test.ts +6 -6
- package/test/withheld-door.test.ts +2 -2
- package/test/yield-loop.test.ts +4 -4
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ★ 조회용 파생 표의 규율 — 앞으로만 채우고, 담지 않은 구간을 말한다.
|
|
3
|
+
*
|
|
4
|
+
* ── 왜 이 표를 만들었나 (2026-08-27 실측) ───────────────────────────────────
|
|
5
|
+
* 360 화면의 「이 대상의 이력」이 64초다. 「무엇으로 걸러 시각순으로 정렬해 앞에서 30개」라는 모양에
|
|
6
|
+
* 맞는 목차가 저널에 없고, 얹어도 풀리지 않는다.
|
|
7
|
+
*
|
|
8
|
+
* 여러 값으로 거르면(IN) 정렬 때문에 목차를 못 쓴다 — 실측한 질의 계획
|
|
9
|
+
* 축이 늘 때마다 저널의 목차가 늘어난다 — 360 의 축 넷이면 13개
|
|
10
|
+
* 품목과 로트가 한 문자열에 붙어 있어 품목만으로는 찾을 수 없다 — 정확일치가 0건
|
|
11
|
+
*
|
|
12
|
+
* ── 이 테스트가 무엇을 지키나 ───────────────────────────────────────────────
|
|
13
|
+
* DB 를 띄우지 않는다. 지켜야 할 것은 **규율**이고 그것은 순수하게 확인할 수 있다 — 목차가 조회 모양과
|
|
14
|
+
* 같은 순서인가, 소급 채우기를 하지 않는가, 담지 않은 구간을 말하는가, 저널이 먼저인가.
|
|
15
|
+
*
|
|
16
|
+
* 설계: `operato-twin/design/plans/read-models.md`
|
|
17
|
+
*/
|
|
18
|
+
import { test } from 'node:test'
|
|
19
|
+
import assert from 'node:assert/strict'
|
|
20
|
+
import { readFileSync } from 'node:fs'
|
|
21
|
+
import { join } from 'node:path'
|
|
22
|
+
|
|
23
|
+
const dir = (...p: string[]) => join(import.meta.dirname, '..', 'server', ...p)
|
|
24
|
+
const entity = readFileSync(dir('service', 'twin-subject', 'twin-subject-event.ts'), 'utf8')
|
|
25
|
+
const writer = readFileSync(dir('service', 'twin-subject', 'twin-subject-writer.ts'), 'utf8')
|
|
26
|
+
const query = readFileSync(dir('service', 'twin-subject', 'twin-subject-query.ts'), 'utf8')
|
|
27
|
+
const resolver = readFileSync(dir('service', 'twin-subject', 'item-resolver.ts'), 'utf8')
|
|
28
|
+
const engine = readFileSync(dir('engine', 'twin-engine.ts'), 'utf8')
|
|
29
|
+
|
|
30
|
+
test('★ 목차 순서가 조회 모양과 같다 — 이 순서가 이 표의 존재 이유다', () => {
|
|
31
|
+
/*
|
|
32
|
+
* 「이 트윈의 이 대상의 것을 최신순으로 30개」가 빠르려면 목차가 거르는 값 다음에 시각이어야 한다.
|
|
33
|
+
* 순서가 바뀌면 다시 정렬하게 되고, 그것이 저널에서 64초가 된 바로 그 원인이다.
|
|
34
|
+
*/
|
|
35
|
+
assert.match(
|
|
36
|
+
entity,
|
|
37
|
+
/ix_twin_subject_event_0'[\s\S]{0,160}?\[e\.domain, e\.instanceId, e\.subjectKind, e\.subjectId, e\.eventTime\]/,
|
|
38
|
+
'(도메인, 트윈, 종류, 식별자, 시각) 순서여야 한다'
|
|
39
|
+
)
|
|
40
|
+
/* 보관 상한을 지우는 축도 시각이 마지막이어야 한다(경계를 시각으로 찾으므로). */
|
|
41
|
+
assert.match(
|
|
42
|
+
entity,
|
|
43
|
+
/ix_twin_subject_event_1'[\s\S]{0,160}?\[e\.domain, e\.instanceId, e\.eventTime\]/,
|
|
44
|
+
'(도메인, 트윈, 시각) 목차가 있어야 한다'
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('★ 시각 컬럼에 드라이버 방언을 적지 않는다 — datetime 은 Postgres 가 거절한다', () => {
|
|
49
|
+
const at = entity.indexOf('eventTime: Date')
|
|
50
|
+
assert.ok(at > 0, 'eventTime 이 있어야 한다')
|
|
51
|
+
const decl = entity.slice(Math.max(0, at - 400), at)
|
|
52
|
+
assert.doesNotMatch(decl, /type: 'datetime'/, 'sqlite·MySQL 의 이름을 적지 않는다')
|
|
53
|
+
assert.doesNotMatch(decl, /type: 'timestamp'/, 'Postgres 의 이름도 적지 않는다')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('★ 저널이 먼저다 — 이 표에 적다가 실패해도 저널 쓰기를 되돌리지 않는다', () => {
|
|
57
|
+
/* 사실의 원본은 저널이고 이 표는 다시 만들 수 있다. 반대로 두면 조회를 빠르게 하려고 만든 표가
|
|
58
|
+
사실을 잃는 원인이 된다. */
|
|
59
|
+
assert.match(writer, /catch \(err: any\)[\s\S]{0,200}?noteFailure/, '실패를 잡아 세어야 한다')
|
|
60
|
+
assert.match(writer, /return 0\s*\n\s*\}/, '실패해도 던지지 않는다')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('★ 알리지 않고 비지 않는다 — 적지 못한 줄 수를 조회가 함께 낸다', () => {
|
|
64
|
+
/* 알리지 않고 비면 화면이 「그 대상에는 사건이 없다」고 말하게 된다. 그것이 이 부류에서 가장 나쁜 결함이다. */
|
|
65
|
+
assert.match(writer, /export function subjectWriteFailures/, '적지 못한 수를 물을 수 있어야 한다')
|
|
66
|
+
assert.match(query, /writeFailures: subjectWriteFailures\(/, '조회가 그 수를 실어 보낸다')
|
|
67
|
+
assert.match(query, /writeFailures: number/, '응답에 그 칸이 있어야 한다')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('★ 담지 않은 구간을 말한다 — 앞으로만 채우므로 그 앞은 「없다」가 아니다', () => {
|
|
71
|
+
assert.match(query, /coveredFrom\?: Date/, '경계를 낼 칸이 있어야 한다')
|
|
72
|
+
assert.match(query, /orderBy\('s\.eventTime', 'ASC'\)[\s\S]{0,120}?limit\(1\)/, '가장 오래된 줄 하나로 구한다')
|
|
73
|
+
/* 저장하지 않고 조회할 때 구한다 — 저장한 값은 실제 자료와 어긋날 수 있다. */
|
|
74
|
+
assert.doesNotMatch(entity, /coveredFrom/, '경계를 표에 저장하지 않는다')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('★ 종류가 닫힌 목록이고, 모르는 값은 빈 목록이 아니라 오류다', () => {
|
|
78
|
+
/* 빈 목록으로 답하면 오타가 「그 대상에는 사건이 없다」로 보인다. */
|
|
79
|
+
assert.match(query, /SUBJECT_KINDS as readonly string\[\]\)\.includes\(kind\)/, '닫힌 목록으로 검사한다')
|
|
80
|
+
assert.match(query, /throw new Error\(`unknown subjectKind/, '모르는 종류는 오류를 낸다')
|
|
81
|
+
assert.match(query, /subjectId is required/, '빈 식별자로 트윈 전체를 읽지 않는다')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('★ 페이지를 시각으로 넘긴다 — 머리에 쌓이는 목록에서 건너뛰기는 본 줄을 다시 낸다', () => {
|
|
85
|
+
assert.match(query, /eventTime < :before/, '시각 커서로 넘긴다')
|
|
86
|
+
assert.doesNotMatch(query, /\.skip\(/, '건너뛰기(offset)를 쓰지 않는다')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('★ 품목은 선언과 견주어 판정한다 — 문자열을 자르지 않는다', () => {
|
|
90
|
+
/*
|
|
91
|
+
* 어느 부호가 로트를 가리키는지는 연결된 시스템마다 다르고 커널 계약이 정한 적이 없다. 한 시스템의
|
|
92
|
+
* 관습(`-lot-`)을 규칙으로 굳히면 이 코드가 그 시스템 위에 서게 된다.
|
|
93
|
+
*/
|
|
94
|
+
assert.match(resolver, /resolveByDeclaration\(itemRefOf\(epc\)/, '선언과 견주어 찾는다')
|
|
95
|
+
/* 주석은 그 부호를 설명으로 쓴다 — 코드에 있는지만 본다(주석 줄을 걷어 내고 검사한다). */
|
|
96
|
+
const code = resolver
|
|
97
|
+
.split('\n')
|
|
98
|
+
.filter(l => !/^\s*(\*|\/\*|\/\/)/.test(l))
|
|
99
|
+
.join('\n')
|
|
100
|
+
assert.doesNotMatch(code, /'-lot-'|"-lot-"|split\('-'\)|indexOf\('-lot/, '문자열을 자르는 규칙을 넣지 않는다')
|
|
101
|
+
/* 사건마다 부르므로 기억해 둔다. 선언이 바뀌면 세대가 달라져 다시 판정한다. */
|
|
102
|
+
assert.match(resolver, /generation !== generation|entry\.generation !== generation/, '선언 세대를 본다')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('★ 저널을 쓰는 두 문이 모두 이 표에 적는다 — 한쪽만 적으면 그만큼이 화면에서 빈다', () => {
|
|
106
|
+
const carried = engine.indexOf('static async persistCarried')
|
|
107
|
+
const batch = engine.indexOf('static async persistBatch')
|
|
108
|
+
assert.ok(carried > 0 && batch > 0, '두 문이 있어야 한다')
|
|
109
|
+
assert.match(engine.slice(carried, carried + 900), /this\.writeSubjects\(/, 'persistCarried 가 적는다')
|
|
110
|
+
assert.match(engine.slice(batch, batch + 900), /this\.writeSubjects\(/, 'persistBatch 가 적는다')
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('★ 소급해서 채우지 않는다 — 마이그레이션이 표만 만든다', () => {
|
|
114
|
+
const migration = readFileSync(dir('migrations', '1786500000000-CreateTwinSubjectEvents.ts'), 'utf8')
|
|
115
|
+
assert.match(migration, /createTable/, '표를 만든다')
|
|
116
|
+
assert.match(migration, /createIndex/, '목차를 만든다')
|
|
117
|
+
/* 채우는 코드가 있으면 새 조회 축마다 3,128만 줄을 다시 만드는 구조가 된다. */
|
|
118
|
+
assert.doesNotMatch(migration, /INSERT|insert\(|from twin_events|TwinEvent/i, '기존 사건을 옮기지 않는다')
|
|
119
|
+
const list = readFileSync(dir('migrations', 'index.ts'), 'utf8')
|
|
120
|
+
assert.match(list, /CreateTwinSubjectEvents1786500000000/, '마이그레이션 목록에 배선돼 있어야 한다')
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('★ 이 표의 보관 정리는 체크포인트 상한을 보지 않는다 — 잃을 사실이 없다', () => {
|
|
124
|
+
/*
|
|
125
|
+
* 저널의 정리는 체크포인트가 반영하지 못한 사건을 지우지 않는다(그것을 지우면 사실이 사라진다).
|
|
126
|
+
* 이 표는 사건에서 파생된 값이라 지워도 사실이 사라지지 않는다 — 그 구간의 조회가 느려질 뿐이다.
|
|
127
|
+
*/
|
|
128
|
+
assert.match(writer, /export async function pruneSubjectRows/, '정리 함수가 있어야 한다')
|
|
129
|
+
assert.doesNotMatch(writer, /revision <= :upTo/, '체크포인트 상한을 보지 않는다')
|
|
130
|
+
assert.match(writer, /orderBy\('s\.eventTime', 'ASC'\)/, '경계를 시각으로 찾는다 — 목차를 쓰려면 그래야 한다')
|
|
131
|
+
/* 저널 정리와 같은 창에서 돈다 — 두 표가 같은 보관 기간을 본다. */
|
|
132
|
+
assert.match(engine, /pruneSubjectRows\(/, '저널 정리 창에서 함께 부른다')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('★ 어디까지 옮겼는지 이 프로세스가 든다 — 파생 테이블의 내용으로 알아내지 않는다', () => {
|
|
136
|
+
/*
|
|
137
|
+
* 저장하지 않고 파생 테이블의 내용으로 진행 지점을 알아내려 했고 세 번 틀렸다.
|
|
138
|
+
*
|
|
139
|
+
* ① 최대 리비전 뒤부터 실시간으로 채워진 줄의 최댓값이 곧 저널의 끝이라 옮길 구간을 다 건너뛴다
|
|
140
|
+
* ② 최소 리비전 앞까지 1부터 적으면 최솟값이 1이 되어 구간이 0으로 접힌다
|
|
141
|
+
* ③ 최소 시각 아래로 사건이 시각순으로 오지 않는 트윈에서 곧바로 끝난다
|
|
142
|
+
*
|
|
143
|
+
* 채워진 것은 구간이 아니라 집합이다. 어느 축으로도 하나의 경계로 말할 수 없다.
|
|
144
|
+
*
|
|
145
|
+
* 그래서 알아내려 하지 않고 든다. **테이블에 적지 않는다**(사용자 판단) — 사건이 들어올 때 파생
|
|
146
|
+
* 테이블도 같이 적히므로, 옛 사건을 옮기는 일은 새 파생 테이블을 만들 때와 계산 규칙을 고쳤을 때뿐이다.
|
|
147
|
+
* 대가는 서버가 내려가면 그 몫을 다시 읽는 것이다.
|
|
148
|
+
*/
|
|
149
|
+
const backfill = readFileSync(dir('service', 'twin-subject', 'twin-subject-backfill.ts'), 'utf8')
|
|
150
|
+
assert.match(backfill, /const inFlight = new Map</, '이 프로세스가 든다')
|
|
151
|
+
assert.doesNotMatch(backfill, /TwinProjectionProgress|progressRepo/, '테이블에 적지 않는다')
|
|
152
|
+
assert.doesNotMatch(backfill, /MIN\(s\.revision\)|MAX\(s\.revision\)|MIN\(s\.eventTime\)/, '파생 테이블의 내용으로 알아내지 않는다')
|
|
153
|
+
/* 목표는 처음 부를 때 한 번 정한다 — 매번 다시 재면 새 사건이 들어와 끝나지 않는다. */
|
|
154
|
+
assert.match(backfill, /targetRevision: Number\(tip\?\.max \?\? 0\)/, '목표를 한 번 정한다')
|
|
155
|
+
/* 다 옮기면 들고 있지 않는다 — 남겨 두면 다음 호출이 옛 목표를 본다. */
|
|
156
|
+
assert.match(backfill, /if \(complete\) inFlight\.delete\(key\)/, '끝나면 버린다')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('★ 목록의 순서가 같은 시각에서도 정해진다 — 페이지가 본 줄을 다시 내지 않게', () => {
|
|
160
|
+
assert.match(query, /addOrderBy\('s\.revision', 'DESC'\)/, '같은 시각은 리비전으로 가른다')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
test('★ 선언한 엔티티를 등록한다 — 선언만 하면 테이블이 만들어지지 않는다', () => {
|
|
164
|
+
/*
|
|
165
|
+
* 파생 테이블 엔티티를 만들고 `entities` 에 넣지 않은 적이 있다. 그러면 테이블이 만들어지지 않고,
|
|
166
|
+
* 그것을 쓰는 코드가 「그런 테이블 없다」로 실패한다. 파일이 있는 것과 등록된 것은 다르다.
|
|
167
|
+
*/
|
|
168
|
+
const index = readFileSync(dir('service', 'twin-subject', 'index.ts'), 'utf8')
|
|
169
|
+
assert.match(index, /entities = \[TwinSubjectEvent\]/, '이 폴더의 엔티티가 등록돼 있어야 한다')
|
|
170
|
+
const services = readFileSync(dir('service', 'index.ts'), 'utf8')
|
|
171
|
+
assert.match(services, /\.\.\.TwinSubjectEntities/, '서비스 목록에도 실려야 한다')
|
|
172
|
+
/* 마이그레이션의 테이블 이름은 TypeORM 이 만든 것과 글자 그대로 같아야 한다. */
|
|
173
|
+
const migration = readFileSync(dir('migrations', '1786500000000-CreateTwinSubjectEvents.ts'), 'utf8')
|
|
174
|
+
assert.match(migration, /const TABLE = 'twin_subject_events'/, '실제 테이블 이름을 쓴다')
|
|
175
|
+
})
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ① 개체를 말한 자리를 전부 읽는다 — 관측·집약(부모/자식)·변환(입력/출력).
|
|
6
6
|
* ② 부류 수량만 말한 사건은 「모른다」다 — 그 부류의 어느 개체인지 말하지 않았다.
|
|
7
7
|
* ③ 물품을 만지지 않는다고 **선언된** 사건은 빈 범위다(작업·설비·인원·자산·오더·품질·확인·에너지).
|
|
8
|
-
* ④ 낯선 낱말은 「모른다」다 — 커널이 사건을 늘렸을 때
|
|
8
|
+
* ④ 낯선 낱말은 「모른다」다 — 커널이 사건을 늘렸을 때 알리지 않고 빠뜨리지 않는다.
|
|
9
9
|
*
|
|
10
10
|
* 「모른다」(`null`)와 「하나도 없다」(빈 집합)는 **다른 사실**이다. 앞은 전부 다시 만들게 하고 뒤는
|
|
11
11
|
* 물품을 건너뛰게 한다 — 둘을 같게 두면 둘 중 하나가 반드시 틀린다.
|
|
@@ -53,7 +53,7 @@ test('물품을 만지지 않는다고 선언된 사건은 빈 범위다 — 전
|
|
|
53
53
|
assert.equal(energy!.size, 0)
|
|
54
54
|
})
|
|
55
55
|
|
|
56
|
-
test('낯선 낱말은 「모른다」다 — 커널이 사건을 늘렸을 때
|
|
56
|
+
test('낯선 낱말은 「모른다」다 — 커널이 사건을 늘렸을 때 알리지 않고 빠뜨리지 않는다', () => {
|
|
57
57
|
assert.equal(touchedItemKeys({ eventType: 'something.new', data: { epcList: ['x'] } }), null)
|
|
58
58
|
assert.equal(touchedItemKeys({ data: { epcList: ['x'] } }), null, '낱말이 없으면 판정할 수 없다')
|
|
59
59
|
assert.equal(touchedItemKeys(undefined), null)
|
package/test/twin-audit.test.ts
CHANGED
|
@@ -63,7 +63,7 @@ test('근거는 문자열일 때만 싣는다 — 객체를 그대로 넣어 화
|
|
|
63
63
|
assert.equal(bad.reason, undefined)
|
|
64
64
|
})
|
|
65
65
|
|
|
66
|
-
test('args 가 지나치게 크면 자르되 잘랐다고 알린다 —
|
|
66
|
+
test('args 가 지나치게 크면 자르되 잘랐다고 알린다 — 알리지 않고 버리지 않는다', () => {
|
|
67
67
|
const huge = { blob: 'x'.repeat(9000) }
|
|
68
68
|
const row = buildCommandAudit({ command: { type: 't', args: huge }, ack: {}, at: AT })
|
|
69
69
|
assert.equal(row.argsTruncated, true)
|
|
@@ -140,7 +140,7 @@ test('payload 가 data 로 감싸이지 않은 모양도 받는다', () => {
|
|
|
140
140
|
assert.equal(epcOf({ epcList: ['urn:epc:id:sgtin:1.2.3'] }), 'urn:epc:id:sgtin:1.2.3')
|
|
141
141
|
})
|
|
142
142
|
|
|
143
|
-
test('상한을 넘는 값은
|
|
143
|
+
test('상한을 넘는 값은 오류 없이 사라지지 않는다 — 잘리되 경고가 남는다', () => {
|
|
144
144
|
const long = 'urn:epc:id:sgtin:' + 'x'.repeat(400)
|
|
145
145
|
const warned: string[] = []
|
|
146
146
|
const orig = console.warn
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 여기서 지키는 것:
|
|
8
8
|
* · 어떻게 셌는지 밝힌다 — 행이면 DB 가 페이징, 문서면 서버가 통째로 읽어 잘랐다(`basis`)
|
|
9
|
-
* ·
|
|
9
|
+
* · 알리지 않고 자르지 않는다 — 총 건수가 항상 함께 오고, 훑기가 상한에 닿으면 그렇다고 말한다
|
|
10
10
|
* · 모르는 축은 **거절한다** — 빈 목록으로 답하면 오타 하나가 "우리 트윈엔 그게 없다" 로 굳는다
|
|
11
11
|
* · 투영이 낡아도 항목은 열린다 — 행에 없으면 문서에서 찾는다
|
|
12
12
|
* · 이어진 것은 **양방향**이고, 선언된 관계만 따른다(선언에 없는 길은 그리지 않는다)
|
|
@@ -141,7 +141,7 @@ test('비교하지 못했으면 관측은 null 이고, 답했으면 **근거를
|
|
|
141
141
|
* 이 픽스처에는 저널이 없다(행만 있다) — 되짚을 것이 없으므로 관측은 없다. **0 이나 빈 객체로
|
|
142
142
|
* 메우면 "관측했는데 아무것도 없었다" 로 읽힌다.**
|
|
143
143
|
*
|
|
144
|
-
* 예전 이 검사는 "트윈이 안 돌면 관측은 null" 이었다. 지금은 멈춘 트윈도 저널을
|
|
144
|
+
* 예전 이 검사는 "트윈이 안 돌면 관측은 null" 이었다. 지금은 멈춘 트윈도 저널을 계산해 답할 수 있고,
|
|
145
145
|
* 그 답이 **자기 근거를 달고 온다**(`observedBasis`). 그래서 규율을 그 자리에 맞춰 다시 적는다:
|
|
146
146
|
* 답이 없으면 null, 답이 있으면 그것이 라이브인지 되짚은 것인지 말한다. 근거 없는 답은 없다.
|
|
147
147
|
*/
|
|
@@ -230,10 +230,10 @@ test('지도의 선이 진단이 된다 — 이어진 것뿐 아니라 **끊긴
|
|
|
230
230
|
* ★ 관측 열이 **자기 근거를 말한다** — 라이브인지 되짚은 것인지.
|
|
231
231
|
*
|
|
232
232
|
* 둘은 같은 모양의 값이라, 근거가 없으면 되짚은 값을 "지금 이렇다" 로 읽는다. 그래서 근거를 실을
|
|
233
|
-
* 자리가 없던 동안에는 멈춘 트윈의 저널을 아예
|
|
233
|
+
* 자리가 없던 동안에는 멈춘 트윈의 저널을 아예 계산하지 않았다(관측 열이 통째로 `—`). 자리를 만든 뒤의
|
|
234
234
|
* 규율은 하나다: **답하면 근거를 말한다.**
|
|
235
235
|
*/
|
|
236
|
-
test('멈춘 트윈도 저널을
|
|
236
|
+
test('멈춘 트윈도 저널을 계산해 답하고, 그 답은 다시 계산한 값이라고 알린다', async () => {
|
|
237
237
|
const { WmsKernel, EventJournal } = req('@operato/twin-kernel')
|
|
238
238
|
const model = {
|
|
239
239
|
spaceId: 'journal-space',
|
|
@@ -273,5 +273,5 @@ test('멈춘 트윈도 저널을 접어 답하고, 그 답은 다시 계산한
|
|
|
273
273
|
const it = await q.twinModelItem(INST, 'equipment', 'fk-1', ctx)
|
|
274
274
|
assert.ok(it?.observed, '저널이 있으면 멈춘 트윈도 마지막으로 알던 사실을 답할 수 있다')
|
|
275
275
|
assert.equal(it.observedBasis, 'journal', '라이브가 아니라 되짚은 것이라고 말해야 "지금 이렇다" 로 읽히지 않는다')
|
|
276
|
-
assert.ok(it.observedRevision > 0, '몇 번째 리비전까지
|
|
276
|
+
assert.ok(it.observedRevision > 0, '몇 번째 리비전까지 계산했는지 함께 말한다 — 규모의 한계도 그 수가 말한다')
|
|
277
277
|
})
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* 여기서 지키는 것:
|
|
9
9
|
* · 패턴을 코드가 알지 않는다 — 선언 그래프에서 자기 축으로 돌아오는 순환을 찾는다
|
|
10
|
-
* ·
|
|
10
|
+
* · **알리지 않고 고르지 않는다** — 순환이 여럿이면 후보를 내고 멈춘다(방향이 뒤집힌 전개는
|
|
11
11
|
* 틀린 답이 아니라 **반대의 답**이라 화면만 보고는 구별되지 않는다)
|
|
12
12
|
* · 수량은 곱해 내려가되, **모르면 곱하지 않는다**(1 로 메우면 윗단 합계가 통째로 거짓이 된다)
|
|
13
13
|
* · 사이클을 만나면 다시 파지 않고 말한다
|
|
@@ -153,7 +153,7 @@ test('사이클은 다시 탐색하지 않고 알린다', async () => {
|
|
|
153
153
|
await TwinEngine.ingestMaster(domainId, master(), 'reset')
|
|
154
154
|
})
|
|
155
155
|
|
|
156
|
-
test('상한에서 끊기면 끊겼다고 알린다 —
|
|
156
|
+
test('상한에서 끊기면 끊겼다고 알린다 — 알리지 않고 자르지 않는다', async () => {
|
|
157
157
|
const shallow = await explode('trailer', { maxDepth: 1 })
|
|
158
158
|
assert.equal(shallow.truncated.depth, true)
|
|
159
159
|
assert.deepEqual(shallow.items.filter((n: any) => n.depth === 2), [], '깊이 상한 아래는 안 판다')
|
|
@@ -174,3 +174,36 @@ test('선언에 없는 자재를 참조하면 그 사실이 드러난다', async
|
|
|
174
174
|
assert.equal(child.known, false, '선언에 없다는 사실을 함께 낸다(있는 척하지 않는다)')
|
|
175
175
|
await TwinEngine.ingestMaster(domainId, master(), 'reset')
|
|
176
176
|
})
|
|
177
|
+
|
|
178
|
+
test('★ 선언에 없는 축을 거치면 터지지 않고 **왜 못 하는지 말한다**', async () => {
|
|
179
|
+
/*
|
|
180
|
+
* ── 실측으로 터진 것 (2026-08-24) ────────────────────────────────────────
|
|
181
|
+
* 이 전개는 **트윈 모델의 선언**을 읽는다(`axisInfo(...).path` 로 모델 안을 찾아 들어간다). 그런데
|
|
182
|
+
* 커널 카탈로그의 축 중 셋은 **선언되는 것이 아니라 일어나는 것**이다 — `orders`·`tasks`·`items`
|
|
183
|
+
* 는 사건이 만들고 끝나면 사라진다. 모델이 그 목록을 가질 수 없으므로 `path` 가 비어 있다.
|
|
184
|
+
*
|
|
185
|
+
* Cannot read properties of undefined (reading 'split')
|
|
186
|
+
*
|
|
187
|
+
* ★ **터진 것은 요청한 축이 아니라 「거쳐 가는 축」이다.** `locations` 를 물으면 그 순환이 `tasks` 를
|
|
188
|
+
* 거치는데(작업이 `from`·`at` 으로 자리를 두 번 가리킨다) 그 `tasks` 에 경로가 없다. 그래서 요청한
|
|
189
|
+
* 축만 지켜서는 막히지 않는다 — 처음에 그렇게 고쳤다가 이 시험이 잡았다.
|
|
190
|
+
*
|
|
191
|
+
* 그리고 빈 트리로 답하면 화면이 「이어진 것이 없다」로 읽는데 그것은 거짓이다 — 작업은 자리를
|
|
192
|
+
* 가리키고 있고, 다만 이 질의가 그것을 아는 곳이 아니다.
|
|
193
|
+
*/
|
|
194
|
+
const q = new TwinModelTreeQuery()
|
|
195
|
+
const ctx = { state: { domain: { id: domainId } } } as any
|
|
196
|
+
|
|
197
|
+
/* 두 홉을 지정해야 순환이 하나로 정해진다 — 안 지정하면 앞 갈래(모호함)에서 답한다. */
|
|
198
|
+
const loc = await q.twinModelTree(INSTANCE, 'locations', 'st-a', ctx, 'twin.rel.from', 'twin.rel.at')
|
|
199
|
+
assert.ok(loc, 'locations: 터지지 않는다')
|
|
200
|
+
assert.deepEqual(loc.items, [], 'locations: 펼친 것이 없다')
|
|
201
|
+
assert.equal(loc.reason, 'not-declared-in-model', 'locations: 왜 못 하는지 말한다(거쳐 갈 축이 선언에 없다)')
|
|
202
|
+
|
|
203
|
+
/* 자기참조 순환이 아예 없는 축은 그 사실을 말한다 — 이유가 다르면 낱말도 달라야 한다. */
|
|
204
|
+
for (const axis of ['orders', 'tasks', 'items']) {
|
|
205
|
+
const r = await q.twinModelTree(INSTANCE, axis, 'anything', ctx)
|
|
206
|
+
assert.ok(r, `${axis}: 터지지 않는다`)
|
|
207
|
+
assert.equal(r.reason, 'no-recursive-relation', `${axis}: 순환이 없다는 사실을 말한다`)
|
|
208
|
+
}
|
|
209
|
+
})
|
|
@@ -75,7 +75,7 @@ test('인제스트가 원천을 각인한다 — 원천이 무엇이든 한 자
|
|
|
75
75
|
test('원천을 모르는 재프로비전이 이미 아는 것을 지우지 않는다 — 한 번 지우면 다시 못 읽는다', async () => {
|
|
76
76
|
/*
|
|
77
77
|
* 이 경로는 실제로 있다 — 원천을 모르는 호출자(스크립트·복구·옛 코드)가 같은 트윈을 다시
|
|
78
|
-
* 인제스트한다. 그때 `origin` 을 덮어 비우면 그 트윈은
|
|
78
|
+
* 인제스트한다. 그때 `origin` 을 덮어 비우면 그 트윈은 **알리지 않고 다시 읽을 수 없게 된다.**
|
|
79
79
|
* 그런 종류의 손상은 다음번에 다시 읽으려 할 때까지 아무도 모른다.
|
|
80
80
|
*/
|
|
81
81
|
await TwinEngine.ingestMaster(domainId, master(BORN, 5), 'reset')
|
|
@@ -14,7 +14,7 @@ import assert from 'node:assert/strict'
|
|
|
14
14
|
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
15
15
|
import { join } from 'node:path'
|
|
16
16
|
|
|
17
|
-
import { retiredVocabularyIn } from '@operato/
|
|
17
|
+
import { retiredVocabularyIn } from '@operato/ops-contract'
|
|
18
18
|
|
|
19
19
|
const ROOTS = ['server', 'client'].map(d => new URL(`../${d}`, import.meta.url).pathname)
|
|
20
20
|
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* ── 무엇을 단언하나 ────────────────────────────────────────────────────────
|
|
14
14
|
* 스냅샷의 오더가 남은 데맨드(requested − fulfilled)로 커널에 서고, 이행이 끝난 오더는 서지 않고,
|
|
15
|
-
* 원값이 없는 오더는
|
|
15
|
+
* 원값이 없는 오더는 알리지 않고 서지 않는다는 것. 숫자를 지어내지 않는다는 규칙이 이음매 끝까지 지켜지는가.
|
|
16
16
|
*/
|
|
17
17
|
import { test } from 'node:test'
|
|
18
18
|
import assert from 'node:assert/strict'
|
package/test/warm-start.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* ★ 웜스타트 판정 검증 — 재기동하는 커널에 직전 관측 상태를 심을지.
|
|
3
3
|
*
|
|
4
|
-
* 이 판정은 **양쪽으로
|
|
4
|
+
* 이 판정은 **양쪽으로 오류 없이 틀린 값을 낸다**: 심어야 할 때 안 심으면 있는 재고가 0 으로 보이고
|
|
5
5
|
* (2026-07-31 hatiolab-wms — 저널에 입고 540·출고 94 인데 재고 화면이 비어 있었다),
|
|
6
6
|
* 벤치에 심으면 용량 측정이 오염된다. 둘 다 화면에는 아무 이상이 없어 보인다.
|
|
7
7
|
*/
|
|
@@ -41,7 +41,7 @@ test('원값 없는 오더는 심지 않는다 — 남은 수량을 알 수 없
|
|
|
41
41
|
|
|
42
42
|
test('뺀 오더의 수를 센다 — "주문이 없다" 와 "주문을 못 심었다" 가 같아지면 안 된다', () => {
|
|
43
43
|
/*
|
|
44
|
-
* 이 숫자가 로그의 근거다. 세지 않고
|
|
44
|
+
* 이 숫자가 로그의 근거다. 세지 않고 알리지 않고 빼면, 재기동 후 빈 주문 목록을 보고
|
|
45
45
|
* 사용자는 "원래 없었다" 로 읽는다. 실제로 그 화면이 오래 남아 있었다.
|
|
46
46
|
*/
|
|
47
47
|
const mixed = {
|
|
@@ -117,7 +117,7 @@ test('벤치 판정은 상태 유무보다 먼저다 — 상태가 없어도 이
|
|
|
117
117
|
assert.deepEqual(planWarmStart(null, 'bench', true), { action: 'skip', reason: 'bench' })
|
|
118
118
|
})
|
|
119
119
|
|
|
120
|
-
test('심을 상태가 없으면
|
|
120
|
+
test('심을 상태가 없으면 알리지 않고 넘어간다 — 처음 만든 트윈의 정상 경로다', () => {
|
|
121
121
|
assert.deepEqual(planWarmStart(null, undefined, true), { action: 'skip', reason: 'no-state' })
|
|
122
122
|
assert.deepEqual(planWarmStart(undefined, undefined, true), { action: 'skip', reason: 'no-state' })
|
|
123
123
|
assert.deepEqual(planWarmStart({ locations: [], items: [], equipment: [] }, undefined, true), { action: 'skip', reason: 'no-state' })
|
|
@@ -130,7 +130,7 @@ test('노드만 있어도 심는다 — 구조 배치가 복원되면 화면이
|
|
|
130
130
|
assert.equal(plan.itemCount, 0)
|
|
131
131
|
})
|
|
132
132
|
|
|
133
|
-
test('커널이 주입을 지원하지 않으면 알린다 —
|
|
133
|
+
test('커널이 주입을 지원하지 않으면 알린다 — 알리지 않고 빈 채로 뜨면 안 된다', () => {
|
|
134
134
|
assert.deepEqual(planWarmStart(STATE, undefined, false), { action: 'skip', reason: 'unsupported' })
|
|
135
135
|
})
|
|
136
136
|
|
|
@@ -178,7 +178,7 @@ test('확인 처리가 문자열 목록이 아니면 없는 것으로 본다 —
|
|
|
178
178
|
/*
|
|
179
179
|
* ── 겹포장 회귀 방지 (2026-08-05) ──────────────────────────────────────────
|
|
180
180
|
* 체크포인트가 `{revision, state:{revision, state:{실제}}}` 로 저장돼, 꺼낸 값에 축이 하나도 없었다.
|
|
181
|
-
* 판정은 "심을 것이 없다"(no-state)로 답하고
|
|
181
|
+
* 판정은 "심을 것이 없다"(no-state)로 답하고 **알리지 않고** 넘어갔다 — 저널에 수천 건이 있고 캐시도
|
|
182
182
|
* 최신인데 트윈이 빈 채로 떴다. 오류도 로그도 없어서 이 경로가 처음부터 죽어 있었다.
|
|
183
183
|
*/
|
|
184
184
|
test('봉투로 감싸여 와도 벗겨서 심는다 — 체크포인트가 겹포장돼 복원이 죽었던 자리', () => {
|
|
@@ -283,7 +283,7 @@ test('겹포장된 체크포인트도 벗겨 읽는다 — 한 번은 반드시
|
|
|
283
283
|
assert.equal(plan.energy, true)
|
|
284
284
|
})
|
|
285
285
|
|
|
286
|
-
/* 배선이 사라지면 미러는
|
|
286
|
+
/* 배선이 사라지면 미러는 알리지 않고 예전으로 돌아간다 — 소스 모양으로 못 박는다(엔진은 DB 를 물고 있다). */
|
|
287
287
|
test('미러 기동이 연속성 씨앗을 심는다 — 배선이 사라지지 않게', () => {
|
|
288
288
|
const src = readFileSync(fileURLToPath(new URL('../server/engine/twin-engine.ts', import.meta.url)), 'utf-8')
|
|
289
289
|
const at = src.indexOf('static startLive(')
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* ★ **원본에 있는데 세우지 않은 것** — 그 사실이 스쳐 지나가지 않게.
|
|
3
3
|
*
|
|
4
|
-
* ── 무엇이
|
|
4
|
+
* ── 무엇이 아무 신호도 내지 않았나 (2026-08-24) ────────────────────────────────────────────
|
|
5
5
|
* 커넥터가 원본의 사실 일부를 **일부러 받지 않는 일이 정상이다**(입자가 안 맞는다 · 받으면 상태가
|
|
6
6
|
* 거짓이 된다 · 커널 어휘로 옮길 수 없다). 그때 경고를 내는데, 그 경고의 유일한 소비처가 **프로비저닝
|
|
7
7
|
* 마법사**였다 — 트윈을 만드는 동안만 뜨는 화면이다.
|
|
@@ -32,7 +32,7 @@ const REASON = '아직 지시되지 않은 생산계획을 세우지 않았다
|
|
|
32
32
|
test('★ 세우지 않은 것이 조회에 나온다 — 유입이 한 건도 없어도', () => {
|
|
33
33
|
/*
|
|
34
34
|
* 이 갈래가 중요하다: 원본이 말하는데 **전부** 안 받는 경우가 있고, 그때 유입은 0 이다.
|
|
35
|
-
* 유입 있는 갈래에서만 내면 바로 그 상황에서
|
|
35
|
+
* 유입 있는 갈래에서만 내면 바로 그 상황에서 들어온 것이 없어진다.
|
|
36
36
|
*/
|
|
37
37
|
const l = newIngestLedger()
|
|
38
38
|
recordWithheld(l, REASON, 8, Date.parse('2026-08-24T11:19:16.000Z'))
|
package/test/yield-loop.test.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* **한 바퀴** — 커널이 라인을 돌리고, 그 이벤트를
|
|
2
|
+
* **한 바퀴** — 커널이 라인을 돌리고, 그 이벤트를 계산해, 배운 수율이 다시 커널로 들어간다.
|
|
3
3
|
*
|
|
4
4
|
* ── 왜 이 시험이 필요했나 (2026-08-19) ─────────────────────────────────────
|
|
5
5
|
* 층마다 시험이 있었다(커널 7 · 호스트 9). 그런데 **사슬이 이어졌는지**는 층 시험으로 알 수 없다 —
|
|
6
|
-
* 이벤트 모양이 한 칸만 어긋나도 각 층은 통과하면서 전체는
|
|
6
|
+
* 이벤트 모양이 한 칸만 어긋나도 각 층은 통과하면서 전체는 알리지 않고 상수로 돈다.
|
|
7
7
|
*
|
|
8
8
|
* 실 앱에서 보려고 데모 MES 트윈으로 한 바퀴를 돌리려 했는데 두 번 막혔다: ① 데모의 자극(시나리오)은
|
|
9
9
|
* **시드 때만 실려서** 트윈을 재기동하면 사라진다 ② 모델에서 읽은 값으로 자극을 재구성했지만 부품 도착이
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* 그래서 여기서 **DB·서버 없이** 진짜 사슬을 돌린다: 커널(MES) → `task.status` 이벤트 → 호스트의 실제
|
|
14
14
|
* 폴드(`foldKpi`) → 실제 추정기(`buildYieldEstimator`) → 커널 시임. 가짜 이벤트를 손으로 만들지 않는다 —
|
|
15
|
-
* 커널이 낸 것을 그대로
|
|
15
|
+
* 커널이 낸 것을 그대로 처리한다.
|
|
16
16
|
*/
|
|
17
17
|
import { test } from 'node:test'
|
|
18
18
|
import assert from 'node:assert/strict'
|
|
@@ -156,7 +156,7 @@ test('한 바퀴: 커널이 판정을 내고 → 폴드가 종류별로 세고
|
|
|
156
156
|
const rows = asJournal(events)
|
|
157
157
|
assert.ok(rows.length > 50, `작업 전이가 충분히 나야 한다(관측 ${rows.length})`)
|
|
158
158
|
|
|
159
|
-
/* 호스트의 **실제** 폴드로
|
|
159
|
+
/* 호스트의 **실제** 폴드로 처리한다 — 시험용 계산을 따로 만들지 않는다. */
|
|
160
160
|
const kpi = foldKpi(rows as any, window(rows as any), { groupBy: 'taskKind' })
|
|
161
161
|
const judged = (kpi.groups?.items ?? []).filter(g => g.quality)
|
|
162
162
|
assert.ok(judged.length > 0, '품질이 세어진 종류가 있어야 한다')
|