@things-factory/headless-twin 10.1.278 → 10.1.280
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,103 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ★ **아무것도 듣지 못한 것을 「비었다」로 적지 않는다** — 체크포인트가 좋은 스냅샷을 덮지 않게.
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 손실을 영구화하고 있었나 (2026-08-24 실측) ────────────────────────
|
|
5
|
-
* 미러가 재기동하면 관측 축(재고·오더·작업)을 들고 오지 않는다(`startLive` 가 되찾은 상태를 버린다 —
|
|
6
|
-
* 「다음 계측이 정정한다」는 전제). 그런데 원본이 **커서 증분**으로 말하는 현장에서는 그 정정이 오지
|
|
7
|
-
* 않는다: 커서가 따라잡힌 뒤 원본이 변하지 않으면 미러는 영구히 빈 채다.
|
|
8
|
-
*
|
|
9
|
-
* 실측(포천, `operato-chef-v1-pocheon`):
|
|
10
|
-
*
|
|
11
|
-
* 저널을 계산하면 items 741 · orders 2,856 · tasks 9,250 · nowTime 2026-04-15
|
|
12
|
-
* 라이브 미러 items 0 · orders 0 · tasks 0 · nowTime 2026-01-01
|
|
13
|
-
* 저장된 스냅샷 rev 221,884 · items 741 · orders 2,780 · tasks 6,353 (어제 것)
|
|
14
|
-
* 그런데 이 버전이 같은 키에 **빈 상태**를 쓰고 있었다
|
|
15
|
-
*
|
|
16
|
-
* 그러면 되살릴 캐시가 사라진다. 재기동이 상태를 버리는 것은 저널에서 되살릴 수 있지만, **캐시를
|
|
17
|
-
* 지우는 것은 그 복구 수단을 함께 지운다.**
|
|
18
|
-
*
|
|
19
|
-
* ── 왜 리비전으로는 막을 수 없나 ────────────────────────────────────────────
|
|
20
|
-
* 저널 줄 번호는 **관측이 없어도 자란다**(다른 축·다른 트윈의 사건, 체크포인트 자체). 그래서
|
|
21
|
-
* 「리비전이 더 크면 진전」이라는 규칙은 빈 상태를 통과시킨다.
|
|
22
|
-
*
|
|
23
|
-
* 막는 기준은 **「들은 것이 있나」**다. 유입이 한 건도 없었다면 그 빈 상태는 원본이 「비었다」고
|
|
24
|
-
* 말한 것이 아니라 **우리가 아무것도 못 들은 것**이다 — 그 둘을 같은 값으로 적으면 「모름」이
|
|
25
|
-
* 「없음」이 된다. 이 저장소가 여러 번 거절한 모양이다.
|
|
26
|
-
*
|
|
27
|
-
* 원본이 실제로 「다 비었다」고 말한 경우는 막지 않는다 — 그때는 유입이 있었으므로 이 문을 지난다.
|
|
28
|
-
*
|
|
29
|
-
* ── 엔진 본체는 부를 수 없다 ────────────────────────────────────────────────
|
|
30
|
-
* DB 를 물고 있어 시험에서 부르지 못한다(§`snapshot-freshness` 와 같은 사정). 그래서 판정식만 떼어
|
|
31
|
-
* 시험하고, **그 식이 엔진에 실제로 있는지**를 소스로 함께 확인한다 — 식만 시험하면 엔진이 그것을
|
|
32
|
-
* 쓰지 않아도 초록이다.
|
|
33
|
-
*/
|
|
34
|
-
import { test } from 'node:test'
|
|
35
|
-
import assert from 'node:assert/strict'
|
|
36
|
-
import { readFileSync } from 'node:fs'
|
|
37
|
-
import { join } from 'node:path'
|
|
38
|
-
|
|
39
|
-
/** 엔진의 조건과 같은 식(`twin-engine.persistSnapshot`). */
|
|
40
|
-
const observed = (s: any): number => (s?.items?.length ?? 0) + (s?.orders?.length ?? 0) + (s?.tasks?.length ?? 0)
|
|
41
|
-
|
|
42
|
-
const refuses = (live: any, stored: any, ingestedTotal: number): boolean =>
|
|
43
|
-
!(ingestedTotal > 0) && observed(live) === 0 && observed(stored) > 0
|
|
44
|
-
|
|
45
|
-
const RICH = { items: [{ epc: 'a' }], orders: [{ id: 'o' }], tasks: [{ id: 't' }] }
|
|
46
|
-
const EMPTY = { items: [], orders: [], tasks: [] }
|
|
47
|
-
|
|
48
|
-
test('못 들었고 라이브가 비었는데 저장된 것에 사실이 있으면 — **거절한다**', () => {
|
|
49
|
-
/* ★ 이 시험이 막는 것. 통과시키면 되살릴 유일한 사본이 사라진다. */
|
|
50
|
-
assert.equal(refuses(EMPTY, RICH, 0), true)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test('들은 것이 있으면 통과한다 — 원본이 「다 비었다」고 말한 것은 사실이다', () => {
|
|
54
|
-
/*
|
|
55
|
-
* 창고가 실제로 비는 것은 정상이다. 그때는 유입이 있었으므로 그 빈 상태가 **관측**이고,
|
|
56
|
-
* 여기서 막으면 스냅샷이 영구히 낡는다(고치려던 것의 반대 방향으로 틀린다).
|
|
57
|
-
*/
|
|
58
|
-
assert.equal(refuses(EMPTY, RICH, 1), false)
|
|
59
|
-
assert.equal(refuses(EMPTY, RICH, 24839), false)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
test('라이브에 사실이 있으면 통과한다 — 못 들었어도 심어진 상태는 지킬 값이다', () => {
|
|
63
|
-
assert.equal(refuses(RICH, RICH, 0), false)
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
test('저장된 것이 비어 있으면 막을 것이 없다 — 첫 체크포인트를 가로막지 않는다', () => {
|
|
67
|
-
/* 처음 뜬 트윈은 저장된 것이 없다. 여기서 막으면 그 트윈은 영원히 스냅샷을 갖지 못한다. */
|
|
68
|
-
assert.equal(refuses(EMPTY, EMPTY, 0), false)
|
|
69
|
-
assert.equal(refuses(EMPTY, null, 0), false)
|
|
70
|
-
assert.equal(refuses(EMPTY, undefined, 0), false)
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
test('축 하나만 남아도 사실이다 — 셋을 합쳐 센다', () => {
|
|
74
|
-
/*
|
|
75
|
-
* 오더만 남고 재고가 빈 상태는 흔하다(다 내보낸 창고). 축마다 따로 판정하면 그 상태가
|
|
76
|
-
* 「비었다」로 읽힌다.
|
|
77
|
-
*/
|
|
78
|
-
assert.equal(refuses(EMPTY, { items: [], orders: [{ id: 'o' }], tasks: [] }, 0), true)
|
|
79
|
-
assert.equal(refuses(EMPTY, { items: [], orders: [], tasks: [{ id: 't' }] }, 0), true)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('리비전은 판정에 쓰지 않는다 — 저널 번호는 관측이 없어도 자란다', () => {
|
|
83
|
-
/*
|
|
84
|
-
* ★ 이 단정이 **틀린 기준으로 돌아가는 것**을 막는다. 실측에서 저장된 스냅샷의 리비전은 221,884
|
|
85
|
-
* 였고 라이브는 그보다 큰 번호를 들고 있었다 — 리비전을 기준으로 삼으면 빈 상태가 통과한다.
|
|
86
|
-
*/
|
|
87
|
-
const live = { ...EMPTY, revision: 999999 }
|
|
88
|
-
const stored = { ...RICH, revision: 1 }
|
|
89
|
-
assert.equal(refuses(live, stored, 0), true, '번호가 커도 사실이 없으면 덮지 않는다')
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
test('엔진이 실제로 이 판정을 한다 — 식만 시험하면 엔진이 안 써도 초록이다', () => {
|
|
93
|
-
const src = readFileSync(join(import.meta.dirname, '..', 'server', 'engine', 'twin-engine.ts'), 'utf8')
|
|
94
|
-
/* 주석을 걷어낸 코드만 — 「덮지 않는다」를 적어 둔 근거가 가드를 뒤집지 않게. */
|
|
95
|
-
const code = src.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/(^|[^:])\/\/.*$/gm, '$1')
|
|
96
|
-
const at = code.indexOf('persistSnapshot')
|
|
97
|
-
assert.ok(at > 0, 'persistSnapshot 이 있어야 한다')
|
|
98
|
-
const body = code.slice(at, at + 2000)
|
|
99
|
-
assert.match(body, /ingestedTotal/, '「들은 것이 있나」를 본다')
|
|
100
|
-
assert.match(body, /items\?\.length[\s\S]{0,80}orders\?\.length[\s\S]{0,80}tasks\?\.length/, '관측 축 셋을 합쳐 센다')
|
|
101
|
-
assert.match(body, /checkpoint refused/, '거절을 말한다 — 알리지 않고 거절하지 않는다')
|
|
102
|
-
assert.doesNotMatch(body.slice(0, body.indexOf('checkpoint refused')), /revision >|revision </, '리비전 비교로 판정하지 않는다')
|
|
103
|
-
})
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ★ **어느 레시피로 만들었나 — 짐작하지 않는다.**
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 있었나 (2026-09-05) ─────────────────────────────────────────────
|
|
5
|
-
* 계보 진단이 자재로 되짚어 `recipes.find(...)` 로 **먼저 걸리는 하나**를 골랐다. 배열 순서가 답을
|
|
6
|
-
* 정한다는 뜻이다. 그리고 한 자재에 레시피가 둘인 경우가 이미 있다 — plant 마스터 실측:
|
|
7
|
-
*
|
|
8
|
-
* BOLT-M8 → RCP-BOLT-STD 유효시작 2026-09-01 09:25:29.447
|
|
9
|
-
* RCP-BOLT-FAST 유효시작 2026-09-01 09:25:29.510
|
|
10
|
-
*
|
|
11
|
-
* 빠른 방식으로 만든 볼트인데 표준 방식과 견주고 「설계와 다릅니다」가 나온다. 사람은 라인에
|
|
12
|
-
* 문제가 있다고 믿고 확인하러 간다. **틀린 답이 확신에 찬 얼굴로 나오는 것**이 그 상태였다.
|
|
13
|
-
*
|
|
14
|
-
* ── 답은 적혀 있었다 ───────────────────────────────────────────────────────
|
|
15
|
-
* 만들 때 정해지는 사실이고 커널이 오더에 싣는다(`FlowOrder.recipeKey`). 변환 사건은 그 오더를
|
|
16
|
-
* 거래 식별자로 가리키고, 계보 함수는 그 값을 이미 꺼내 놓고도 쓰지 않았다.
|
|
17
|
-
*
|
|
18
|
-
* 세 자리가 다 있었고 **마지막 연결만 없었다.**
|
|
19
|
-
*
|
|
20
|
-
* ── 왜 순수 함수인가 ───────────────────────────────────────────────────────
|
|
21
|
-
* 규칙만 따로 재려면 DB 도 트윈도 필요 없어야 한다. 여기서 값으로 못 박아 두면 다음에 「하나쯤
|
|
22
|
-
* 골라 주는 게 친절하지 않나」가 올라올 때 그 줄이 빨개진다.
|
|
23
|
-
*/
|
|
24
|
-
import { test } from 'node:test'
|
|
25
|
-
import assert from 'node:assert/strict'
|
|
26
|
-
|
|
27
|
-
import { chooseRecipe } from '../server/service/twin-model/choose-recipe.js'
|
|
28
|
-
|
|
29
|
-
const STD = { key: 'RCP-BOLT-STD', outputs: [{ material: 'BOLT-M8' }] }
|
|
30
|
-
const FAST = { key: 'RCP-BOLT-FAST', outputs: [{ material: 'BOLT-M8' }] }
|
|
31
|
-
const OTHER = { key: 'RCP-CT-BLANK', outputs: [{ material: 'CT-BLANK' }] }
|
|
32
|
-
|
|
33
|
-
const ALL = [STD, FAST, OTHER]
|
|
34
|
-
|
|
35
|
-
/* ── 오더가 말하면 그것을 쓴다 ───────────────────────────────────────────── */
|
|
36
|
-
|
|
37
|
-
test('★ 오더가 말한 레시피를 쓴다 — 버전이 둘이어도 고민하지 않는다', () => {
|
|
38
|
-
const r = chooseRecipe(ALL, [STD, FAST], 'RCP-BOLT-FAST')
|
|
39
|
-
|
|
40
|
-
assert.equal(r.recipe, FAST, '오더가 말했는데 다른 것을 골랐다')
|
|
41
|
-
assert.equal(r.basis, 'order')
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('★ 배열 순서가 답을 바꾸지 않는다 — 옛 결함의 핵심이 이것이었다', () => {
|
|
45
|
-
const forward = chooseRecipe([STD, FAST], [STD, FAST], 'RCP-BOLT-FAST')
|
|
46
|
-
const reversed = chooseRecipe([FAST, STD], [FAST, STD], 'RCP-BOLT-FAST')
|
|
47
|
-
|
|
48
|
-
assert.equal(forward.recipe, reversed.recipe, '순서를 뒤집으니 답이 달라졌다')
|
|
49
|
-
assert.equal(forward.recipe?.key, 'RCP-BOLT-FAST')
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
/* ── 오더가 말 안 할 때 ─────────────────────────────────────────────────── */
|
|
53
|
-
|
|
54
|
-
test('★ 후보가 하나면 쓴다 — 고를 것이 없으면 짐작이 아니다', () => {
|
|
55
|
-
const r = chooseRecipe(ALL, [OTHER], undefined)
|
|
56
|
-
|
|
57
|
-
assert.equal(r.recipe, OTHER)
|
|
58
|
-
assert.equal(r.basis, 'only-candidate')
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('★ 후보가 둘 이상이면 답하지 않는다 — 여기가 옛 결함의 자리다', () => {
|
|
62
|
-
/*
|
|
63
|
-
* 옛 코드: `recipes.find(...)` — 이 자리에서 STD 를 돌려줬다. 그 답이 틀릴 확률이 절반이고,
|
|
64
|
-
* 틀렸다는 사실은 아무 데도 안 남았다.
|
|
65
|
-
*/
|
|
66
|
-
const r = chooseRecipe(ALL, [STD, FAST], undefined)
|
|
67
|
-
|
|
68
|
-
assert.equal(r.recipe, undefined, '어느 것으로 만들었는지 모르는 채로 하나를 골랐다')
|
|
69
|
-
assert.equal(r.basis, 'ambiguous')
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
/* ── 어긋남과 모름을 가른다 ─────────────────────────────────────────────── */
|
|
73
|
-
|
|
74
|
-
test('★ 오더가 말한 레시피가 선언에 없으면 「모른다」가 아니라 「어긋났다」', () => {
|
|
75
|
-
/*
|
|
76
|
-
* 사람이 할 일이 다르다. 어긋남은 선언이나 원본을 고칠 일이고, 모호함은 그 개체의 기록이 없는
|
|
77
|
-
* 일이다. 한 낱말로 합치면 화면이 둘 중 어느 쪽인지 말할 수 없다.
|
|
78
|
-
*/
|
|
79
|
-
const r = chooseRecipe(ALL, [STD, FAST], 'RCP-사라진것')
|
|
80
|
-
|
|
81
|
-
assert.equal(r.recipe, undefined)
|
|
82
|
-
assert.equal(r.basis, 'said-but-undeclared')
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
test('★ 후보가 아예 없으면 none — 「이 자재를 내는 레시피가 없다」', () => {
|
|
86
|
-
const r = chooseRecipe(ALL, [], undefined)
|
|
87
|
-
|
|
88
|
-
assert.equal(r.recipe, undefined)
|
|
89
|
-
assert.equal(r.basis, 'none')
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
/* ── 무너지지 않는다 ────────────────────────────────────────────────────── */
|
|
93
|
-
|
|
94
|
-
test('빈 목록·빈 값에도 터지지 않는다', () => {
|
|
95
|
-
assert.equal(chooseRecipe([], [], undefined).basis, 'none')
|
|
96
|
-
assert.equal(chooseRecipe([], [], 'X').basis, 'said-but-undeclared')
|
|
97
|
-
assert.equal(chooseRecipe(ALL, [STD], '').basis, 'only-candidate', '빈 문자열은 「말했다」가 아니다')
|
|
98
|
-
})
|
|
99
|
-
|
|
100
|
-
/* ── 검사가 실제로 무언가를 재나 ─────────────────────────────────────────── */
|
|
101
|
-
|
|
102
|
-
test('★ 이 검사가 실제로 무언가를 잰다 — 견준 것이 0이면 빨강', () => {
|
|
103
|
-
/* 네 판정이 다 나오는지. 하나라도 안 나오면 위 검사들이 같은 가지만 밟고 있는 것이다. */
|
|
104
|
-
const bases = new Set([
|
|
105
|
-
chooseRecipe(ALL, [STD, FAST], 'RCP-BOLT-FAST').basis,
|
|
106
|
-
chooseRecipe(ALL, [OTHER], undefined).basis,
|
|
107
|
-
chooseRecipe(ALL, [STD, FAST], undefined).basis,
|
|
108
|
-
chooseRecipe(ALL, [STD], 'RCP-없음').basis,
|
|
109
|
-
chooseRecipe(ALL, [], undefined).basis
|
|
110
|
-
])
|
|
111
|
-
|
|
112
|
-
assert.deepEqual(
|
|
113
|
-
[...bases].sort(),
|
|
114
|
-
['ambiguous', 'none', 'only-candidate', 'order', 'said-but-undeclared'],
|
|
115
|
-
`판정이 다 나오지 않는다: ${[...bases].join(' · ')}`
|
|
116
|
-
)
|
|
117
|
-
})
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 컬럼 타입은 **드라이버 방언을 적지 않는다.**
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 났나 (2026-08-19) ────────────────────────────────────────────────
|
|
5
|
-
* 릴리즈 부팅이 Postgres 에서 막혔다:
|
|
6
|
-
*
|
|
7
|
-
* DataTypeNotSupportedError: Data type "datetime" in "TwinEvent.eventTime" is not supported by "postgres"
|
|
8
|
-
*
|
|
9
|
-
* `datetime` 은 **MySQL·MSSQL 의 이름**이다(Postgres·Oracle 은 거절한다). 거꾸로 `timestamp` 는 MySQL
|
|
10
|
-
* 이 받지만 예전 주석은 「sqlite 가 지원하지 않는다」고 적어 두었다 — sqlite 드라이버는 타입 목록이
|
|
11
|
-
* 비어 있어 **아무 이름도 검증하지 않는다**(그래서 개발 중에는 무엇을 적어도 통과했다). 결국 다섯
|
|
12
|
-
* 드라이버에 다 통하는 날짜 이름은 **없다.**
|
|
13
|
-
*
|
|
14
|
-
* 그래서 이름을 고르는 일을 TypeORM 에 맡긴다: `emitDecoratorMetadata` 가 켜져 있으므로 `Date` 속성에
|
|
15
|
-
* `@Column()` 만 달면 드라이버별로 알맞게 바뀐다(Postgres `timestamp without time zone` · sqlite·MySQL
|
|
16
|
-
* `datetime` · Oracle `timestamp`) — sqlite 거동은 그대로다.
|
|
17
|
-
*
|
|
18
|
-
* 이 시험은 그 규율을 **소스에서** 지킨다: 이 패키지의 엔티티가 명시한 타입 이름은 네 드라이버(검증을
|
|
19
|
-
* 하는 쪽) 모두가 아는 것이어야 한다. 개발 DB 가 sqlite 라 아무것도 못 잡아 주기 때문에 필요하다.
|
|
20
|
-
*/
|
|
21
|
-
import { test } from 'node:test'
|
|
22
|
-
import assert from 'node:assert/strict'
|
|
23
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
24
|
-
import { fileURLToPath } from 'node:url'
|
|
25
|
-
import { createRequire } from 'node:module'
|
|
26
|
-
import { join } from 'node:path'
|
|
27
|
-
|
|
28
|
-
const req = createRequire(import.meta.url)
|
|
29
|
-
|
|
30
|
-
/** 드라이버가 아는 타입 이름 — **TypeORM 소스에서 읽는다**(우리가 목록을 옮겨 적지 않는다). */
|
|
31
|
-
function supportedTypes(driverPath: string): Set<string> {
|
|
32
|
-
const src = readFileSync(req.resolve(driverPath), 'utf-8')
|
|
33
|
-
const m = src.match(/supportedDataTypes = \[([\s\S]*?)\];/)
|
|
34
|
-
assert.ok(m, `${driverPath}: supportedDataTypes 를 찾지 못했다(TypeORM 구조가 바뀌었다)`)
|
|
35
|
-
return new Set((m![1].match(/"[^"]+"/g) ?? []).map(s => s.slice(1, -1)))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/*
|
|
39
|
-
* sqlite 는 목록이 비어 있다 = **검증하지 않는다.** 그래서 여기 넣지 않는다 — 넣으면 모든 타입이
|
|
40
|
-
* 「sqlite 가 모른다」로 걸려 이 시험이 소음이 된다. 개발 DB 가 아무것도 못 잡는다는 사실이 이 시험의 이유다.
|
|
41
|
-
*/
|
|
42
|
-
const DRIVERS: Record<string, string> = {
|
|
43
|
-
postgres: 'typeorm/driver/postgres/PostgresDriver.js',
|
|
44
|
-
mysql: 'typeorm/driver/mysql/MysqlDriver.js',
|
|
45
|
-
oracle: 'typeorm/driver/oracle/OracleDriver.js',
|
|
46
|
-
mssql: 'typeorm/driver/sqlserver/SqlServerDriver.js'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function entitySources(): { file: string; src: string }[] {
|
|
50
|
-
const root = fileURLToPath(new URL('../server', import.meta.url))
|
|
51
|
-
const out: { file: string; src: string }[] = []
|
|
52
|
-
const walk = (dir: string) => {
|
|
53
|
-
for (const name of readdirSync(dir)) {
|
|
54
|
-
const path = join(dir, name)
|
|
55
|
-
if (statSync(path).isDirectory()) { walk(path); continue }
|
|
56
|
-
if (!name.endsWith('.ts')) continue
|
|
57
|
-
const src = readFileSync(path, 'utf-8')
|
|
58
|
-
if (/@Entity\(/.test(src)) out.push({ file: path.slice(root.length + 1), src })
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
walk(root)
|
|
62
|
-
return out
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
test('엔티티가 있다 — 훑을 대상이 없으면 이 시험은 아무것도 지키지 않는다', () => {
|
|
66
|
-
assert.ok(entitySources().length >= 5, '이 패키지의 엔티티를 찾아야 한다')
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
/*
|
|
70
|
-
* **드라이버마다 이름이 다른 컬럼은 코드가 고른다.**
|
|
71
|
-
*
|
|
72
|
-
* 긴 글이 그렇다: `text` 를 Oracle 이 모르고(그쪽은 `clob`), MySQL 계열은 `longtext`, MSSQL 은
|
|
73
|
-
* `nvarchar(MAX)` 를 쓴다. 이 레포의 정본 관행은 `DATABASE_TYPE` 으로 분기하는 것이다
|
|
74
|
-
* (`board-service` 의 `Board.model`) — 그 분기는 이 시험의 리터럴 검사에 걸리지 않는다(정상이다).
|
|
75
|
-
*
|
|
76
|
-
* 그래서 면제 목록을 두지 않는다: 고정 리터럴은 전부 잡고, 갈라야 하는 컬럼은 관행대로 가른다.
|
|
77
|
-
*/
|
|
78
|
-
const KNOWN_GAPS = new Set<string>()
|
|
79
|
-
|
|
80
|
-
test('명시한 컬럼 타입은 네 드라이버가 모두 아는 이름이다 — 개발 DB(sqlite)는 아무것도 잡아 주지 않는다', () => {
|
|
81
|
-
const supported = Object.fromEntries(Object.entries(DRIVERS).map(([n, p]) => [n, supportedTypes(p)]))
|
|
82
|
-
const violations: string[] = []
|
|
83
|
-
for (const { file, src } of entitySources()) {
|
|
84
|
-
for (const m of src.matchAll(/type:\s*'([a-z][a-z0-9 _]*)'/g)) {
|
|
85
|
-
const type = m[1]
|
|
86
|
-
const missing = Object.entries(supported).filter(([, s]) => !s.has(type)).map(([n]) => n)
|
|
87
|
-
if (!missing.length) continue
|
|
88
|
-
const line = `${file}: type '${type}' — ${missing.join(', ')} 가 모른다`
|
|
89
|
-
if (!KNOWN_GAPS.has(line)) violations.push(line)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
assert.deepEqual(violations, [], `드라이버 방언을 적은 컬럼:\n${violations.join('\n')}`)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
test('면제를 남겨 두면 그것이 아직 사실인지 확인한다 — 유령 면제를 남기지 않는다', () => {
|
|
96
|
-
const supported = Object.fromEntries(Object.entries(DRIVERS).map(([n, p]) => [n, supportedTypes(p)]))
|
|
97
|
-
const present = new Set<string>()
|
|
98
|
-
for (const { file, src } of entitySources()) {
|
|
99
|
-
for (const m of src.matchAll(/type:\s*'([a-z][a-z0-9 _]*)'/g)) {
|
|
100
|
-
const missing = Object.entries(supported).filter(([, s]) => !s.has(m[1])).map(([n]) => n)
|
|
101
|
-
if (missing.length) present.add(`${file}: type '${m[1]}' — ${missing.join(', ')} 가 모른다`)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
for (const gap of KNOWN_GAPS) assert.ok(present.has(gap), `이 예외는 이제 사실이 아니다(목록에서 지워라): ${gap}`)
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
test('날짜 컬럼은 이름을 적지 않는다 — TypeORM 이 드라이버별로 고르게 둔다', () => {
|
|
108
|
-
for (const { file, src } of entitySources()) {
|
|
109
|
-
assert.equal(/type:\s*'(datetime|timestamp|timestamptz|datetime2|smalldatetime)'/.test(src), false, `${file}: 날짜 타입 이름을 적었다`)
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
/*
|
|
114
|
-
* 드라이버별로 갈라야 하는 컬럼은 **갈린 상태로** 있어야 한다 — 다시 고정 리터럴로 돌아가면 그 드라이버가
|
|
115
|
-
* 부팅에서 거절한다(감사 사유는 사람이 쓰는 글이라 길이를 자르지 않는다).
|
|
116
|
-
*/
|
|
117
|
-
test('긴 글 컬럼은 DATABASE_TYPE 으로 갈린다 — 관행을 되돌리지 않는다', () => {
|
|
118
|
-
const src = readFileSync(fileURLToPath(new URL('../server/service/twin-audit/twin-audit-event.ts', import.meta.url)), 'utf-8')
|
|
119
|
-
assert.match(src, /const DATABASE_TYPE = config\.get\('ormconfig', \{\}\)\.type/, '관행과 같은 자리에서 읽는다')
|
|
120
|
-
for (const name of ['longtext', 'clob', 'nvarchar', 'text']) assert.ok(src.includes(`'${name}'`), `${name} 갈래가 있어야 한다`)
|
|
121
|
-
assert.match(src, /length: DATABASE_TYPE == 'mssql' \? 'MAX' : undefined/, 'MSSQL 은 길이까지 말해야 잘리지 않는다')
|
|
122
|
-
})
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ★ 커맨드 라우팅 판정 — 권위(모드) × 성격(주석/액추에이션).
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 문제였나 ─────────────────────────────────────────────────────────
|
|
5
|
-
* 리졸버가 `inst.runtime.dispatch(...)` 를 곧바로 불렀다. 그런데 `runtime` 은 **시뮬 경로에서만**
|
|
6
|
-
* 만들어진다. 실물을 비추는 트윈(mirror)에서는 그 한 줄이 `TypeError` 로 터져 조치 채널이 통째로
|
|
7
|
-
* 닿지 않았다 — 사용자에게는 이유 있는 거절이 아니라 그냥 실패였다.
|
|
8
|
-
*
|
|
9
|
-
* ── 그냥 열면 안 되는 이유 ──────────────────────────────────────────────────
|
|
10
|
-
* 미러에서 액추에이션을 허용하면 **새 거짓말이 생긴다**: 트윈은 "보류됨" 이라 말하고 실 시스템은
|
|
11
|
-
* 모른다. 그래서 실 프로세스를 바꾸는 커맨드는 아웃바운드 어댑터가 붙기 전까지 이유를 밝혀 거절한다
|
|
12
|
-
* (`design/plans/command-routing.md` P0). 확인(ack)은 트윈 쪽 주석이라 미러에서도 통과한다.
|
|
13
|
-
*/
|
|
14
|
-
import { test } from 'node:test'
|
|
15
|
-
import assert from 'node:assert/strict'
|
|
16
|
-
|
|
17
|
-
import { routeCommand, isActuation } from '../server/engine/command-routing.ts'
|
|
18
|
-
|
|
19
|
-
const sim = { hasRuntime: true, hasKernelDispatch: true }
|
|
20
|
-
const mirror = { mode: 'live', hasRuntime: false, hasKernelDispatch: true }
|
|
21
|
-
|
|
22
|
-
test('시뮬 — 커맨드는 커널 런타임으로 간다(현행 경로 그대로)', () => {
|
|
23
|
-
assert.deepEqual(routeCommand(sim, true, 'order.hold'), { target: 'runtime' })
|
|
24
|
-
assert.deepEqual(routeCommand(sim, true, 'attention.ack'), { target: 'runtime' })
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
test('미러 — 확인(ack)은 관측 커널로 간다. 현실을 건드리지 않는 주석이다', () => {
|
|
28
|
-
assert.deepEqual(routeCommand(mirror, true, 'attention.ack'), { target: 'kernel' })
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
test('미러 — 실 프로세스를 바꾸는 커맨드는 이유를 밝히고 거절한다', () => {
|
|
32
|
-
for (const type of ['order.hold', 'order.resume', 'order.release', 'resource.down', 'resource.repair', 'resource.add']) {
|
|
33
|
-
const r = routeCommand(mirror, true, type)
|
|
34
|
-
assert.equal(r.target, 'reject', `${type} 은 미러에서 통과하면 안 된다`)
|
|
35
|
-
if (r.target !== 'reject') return
|
|
36
|
-
assert.equal(r.errorCode, 'actuation-not-wired')
|
|
37
|
-
assert.equal(r.errorParams?.type, type, '어느 커맨드였는지 함께 알려야 한다')
|
|
38
|
-
}
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
test('없는 인스턴스와 다른 도메인은 같은 사유로 거절한다 — 있음/없음이 구별되면 그것이 정보다', () => {
|
|
42
|
-
assert.deepEqual(routeCommand(undefined, false, 'attention.ack'), { target: 'reject', errorCode: 'no-instance' })
|
|
43
|
-
assert.deepEqual(routeCommand(sim, false, 'attention.ack'), { target: 'reject', errorCode: 'no-instance' })
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
test('런타임이 없는 시뮬 — 오류를 내지 않고 이유를 알린다', () => {
|
|
47
|
-
assert.deepEqual(routeCommand({ hasRuntime: false, hasKernelDispatch: true }, true, 'order.hold'), { target: 'reject', errorCode: 'no-runtime' })
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('커널이 커맨드를 못 받는 미러 — 확인도 이유를 말하고 멈춘다', () => {
|
|
51
|
-
assert.deepEqual(routeCommand({ mode: 'live', hasRuntime: false, hasKernelDispatch: false }, true, 'attention.ack'), { target: 'reject', errorCode: 'no-runtime' })
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
test('빈 커맨드 종류는 거절한다 — 무엇을 하라는지 모르는 것을 보내지 않는다', () => {
|
|
55
|
-
assert.deepEqual(routeCommand(sim, true, ''), { target: 'reject', errorCode: 'unknown-command' })
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test('성격 판정 — 확인만 주석이고 나머지는 액추에이션이다', () => {
|
|
59
|
-
assert.equal(isActuation('attention.ack'), false)
|
|
60
|
-
for (const t of ['order.hold', 'resource.add', 'order.release']) assert.equal(isActuation(t), true, t)
|
|
61
|
-
})
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ★ **칸이 있으면 쓰는 길도 있어야 한다** — `save` 가 손으로 든 목록이라 새 칸을 빠뜨린다.
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 있었나 (2026-09-04) ──────────────────────────────────────────────
|
|
5
|
-
* 일꾼에 네 칸을 더했다 — `dispatch_note_next` · `retry` · `attempts` · `next_attempt_at`.
|
|
6
|
-
* 엔티티에 선언하고 마이그레이션까지 걸었는데 **`save` 가 그 칸을 안 썼다.** 그 함수는 쓸 칸을
|
|
7
|
-
* 손으로 들고 있고, 새 칸이 그 목록에 없었다.
|
|
8
|
-
*
|
|
9
|
-
* 그래서 일꾼이 물러섬을 계산해 넘겼는데 표에는 아무것도 안 남았다. 재기동하면 물러서던 것이
|
|
10
|
-
* 전부 즉시 재시도가 되고, 목록의 「몇 번 해 봤나」가 영원히 비어 있다. **오류는 나지 않는다.**
|
|
11
|
-
*
|
|
12
|
-
* 30분을 뒤진 끝에 찾았고, 이 저장소에서 하루 종일 「자리는 있고 길이 없다」로 부른 것과 같다 —
|
|
13
|
-
* 이번에는 **칸과 쓰는 길** 사이였다.
|
|
14
|
-
*
|
|
15
|
-
* ── 무엇을 검사하나 ────────────────────────────────────────────────────────
|
|
16
|
-
* 엔티티가 칸을 가진 필드는 `save` 가 쓰거나, 안 쓰는 이유가 아래 표에 있어야 한다. 목록을
|
|
17
|
-
* 소스에서 읽는다 — 손으로 적으면 새 칸이 조용히 빠지고 그것이 바로 이 검사가 막으려는 실패다.
|
|
18
|
-
*/
|
|
19
|
-
import { test } from 'node:test'
|
|
20
|
-
import assert from 'node:assert/strict'
|
|
21
|
-
import { readFileSync } from 'node:fs'
|
|
22
|
-
|
|
23
|
-
const bare = (p: string): string =>
|
|
24
|
-
readFileSync(new URL(p, import.meta.url), 'utf8')
|
|
25
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
26
|
-
.replace(/\/\/[^\n]*/g, '')
|
|
27
|
-
|
|
28
|
-
const ENTITY = bare('../server/service/actuation/twin-command.ts')
|
|
29
|
-
const STORE = bare('../server/service/actuation/command-store.ts')
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* `save` 가 일부러 안 쓰는 칸 — 이유와 함께.
|
|
33
|
-
*
|
|
34
|
-
* 비어 있는 것이 정상이 아니다: 이 표는 「고치는 경로가 다른 칸」을 든다. 채울 때 그 이유가
|
|
35
|
-
* 「다른 함수가 쓴다」인지 「읽기 전용」인지 적어야 한다.
|
|
36
|
-
*/
|
|
37
|
-
const NOT_BY_SAVE: { column: string; why: string }[] = [
|
|
38
|
-
{ column: 'domain', why: '열쇠 — 테넌트로 좁히는 조건이다' },
|
|
39
|
-
{ column: 'instanceId', why: '낼 때 정해지고 바뀌지 않는다(create)' },
|
|
40
|
-
{ column: 'type', why: '낼 때 정해지고 바뀌지 않는다(create)' },
|
|
41
|
-
{ column: 'payload', why: '낼 때 정해지고 바뀌지 않는다 — 지시 내용은 제안될 때 굳는다' },
|
|
42
|
-
{ column: 'origin', why: '낼 때 정해지고 바뀌지 않는다(create)' },
|
|
43
|
-
{ column: 'proposedBy', why: '낼 때 정해지고 바뀌지 않는다(create)' },
|
|
44
|
-
{ column: 'reversible', why: '명세에서 온다 — 낼 때 굳는다(create)' },
|
|
45
|
-
{ column: 'causeRef', why: '낼 때 정해진다 — 규칙이 낸 근거(create)' },
|
|
46
|
-
{ column: 'ruleRef', why: '낼 때 정해진다(create)' },
|
|
47
|
-
{ column: 'effectStatus', why: '유입 경로가 `recordEffects` 로만 적는다 — 관측이 조치의 수명에 손대지 않게' },
|
|
48
|
-
{ column: 'effectAt', why: '같음 — `recordEffects`' },
|
|
49
|
-
{ column: 'effectSeen', why: '같음 — `recordEffects`' },
|
|
50
|
-
{ column: 'createdAt', why: 'TypeORM 이 넣는다' },
|
|
51
|
-
{ column: 'updatedAt', why: 'TypeORM 이 넣는다' }
|
|
52
|
-
]
|
|
53
|
-
|
|
54
|
-
/** 엔티티가 컬럼을 선언한 속성 이름들 — 데코레이터 다음 줄의 필드를 읽는다. */
|
|
55
|
-
function entityColumns(): string[] {
|
|
56
|
-
const out: string[] = []
|
|
57
|
-
const lines = ENTITY.split('\n')
|
|
58
|
-
for (let i = 0; i < lines.length; i++) {
|
|
59
|
-
if (!/@(Column|CreateDateColumn|UpdateDateColumn|ManyToOne)\(/.test(lines[i])) continue
|
|
60
|
-
/* 데코레이터가 여럿 붙는다(@Column + @Field) — 다음 필드 줄까지 내려간다. */
|
|
61
|
-
for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) {
|
|
62
|
-
const m = lines[j].match(/^\s{2}([a-zA-Z][\w]*)\??:/)
|
|
63
|
-
if (m) {
|
|
64
|
-
out.push(m[1])
|
|
65
|
-
break
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return [...new Set(out)]
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** `save` 가 실제로 쓰는 속성 — 그 함수 본문에서만 읽는다. */
|
|
73
|
-
function savedColumns(): string[] {
|
|
74
|
-
const start = STORE.indexOf('save: async')
|
|
75
|
-
assert.ok(start > 0, '`save` 를 못 찾았다 — 이 검사가 따라가야 한다')
|
|
76
|
-
const body = STORE.slice(start, STORE.indexOf('create: async', start))
|
|
77
|
-
return [...new Set([...body.matchAll(/^\s{10}([a-zA-Z][\w]*):/gm)].map(m => m[1]))]
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
test('★ 엔티티가 든 칸은 save 가 쓰거나 안 쓰는 이유가 적혀 있다', () => {
|
|
81
|
-
const excused = new Set(NOT_BY_SAVE.map(e => e.column))
|
|
82
|
-
const written = new Set(savedColumns())
|
|
83
|
-
|
|
84
|
-
const orphans = entityColumns().filter(c => !written.has(c) && !excused.has(c))
|
|
85
|
-
|
|
86
|
-
assert.deepEqual(
|
|
87
|
-
orphans,
|
|
88
|
-
[],
|
|
89
|
-
'엔티티에 칸이 있는데 `save` 가 안 쓴다. 값이 계산되고 표에는 안 남는다 — 오류는 나지 않는다:\n ' +
|
|
90
|
-
orphans.join('\n ') +
|
|
91
|
-
'\n쓰게 하거나, 다른 경로로 쓴다면 NOT_BY_SAVE 에 이유를 적는다.'
|
|
92
|
-
)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
test('★ 이 검사가 실제로 무언가를 잰다 — 견준 것이 0이면 빨강', () => {
|
|
96
|
-
/*
|
|
97
|
-
* 정규식이 엔티티 모양을 못 따라가면 목록이 비고, 그러면 위 검사가 **초록인데 아무것도 안 재는
|
|
98
|
-
* 상태**가 된다. 오늘 이 저장소의 다른 검사가 그렇게 났다.
|
|
99
|
-
*/
|
|
100
|
-
const columns = entityColumns()
|
|
101
|
-
const written = savedColumns()
|
|
102
|
-
|
|
103
|
-
assert.ok(columns.length > 15, `엔티티 칸을 ${columns.length}개만 읽었다 — 정규식이 모양을 놓쳤다`)
|
|
104
|
-
assert.ok(written.length > 5, `save 가 쓰는 칸을 ${written.length}개만 읽었다`)
|
|
105
|
-
|
|
106
|
-
/* 예외 표가 낡지 않았나 — 지워진 칸이 남아 있으면 검사를 넓혀 주는 척만 한다. */
|
|
107
|
-
const known = new Set(columns)
|
|
108
|
-
const stale = NOT_BY_SAVE.map(e => e.column).filter(c => !known.has(c))
|
|
109
|
-
assert.deepEqual(stale, [], `예외 표에 있는데 엔티티에 없다:\n ${stale.join('\n ')}`)
|
|
110
|
-
|
|
111
|
-
/* 이유 없는 예외는 예외가 아니다. */
|
|
112
|
-
const mute = NOT_BY_SAVE.filter(e => !e.why?.trim()).map(e => e.column)
|
|
113
|
-
assert.deepEqual(mute, [], `이유 없이 예외로 올렸다:\n ${mute.join('\n ')}`)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
test('★ 일꾼이 쓰는 네 칸이 실제로 save 를 지난다', () => {
|
|
117
|
-
/*
|
|
118
|
-
* 위 검사는 목록 대조라 예외 표에 올리면 통과한다. 이 넷은 **물러섬이 표에 남는 근거**여서
|
|
119
|
-
* 예외가 될 수 없다 — 이름으로 못 박는다.
|
|
120
|
-
*/
|
|
121
|
-
const written = new Set(savedColumns())
|
|
122
|
-
|
|
123
|
-
for (const c of ['dispatchNoteNext', 'retry', 'attempts', 'nextAttemptAt']) {
|
|
124
|
-
assert.ok(written.has(c), `save 가 ${c} 를 안 쓴다 — 물러섬이 재기동에 사라진다`)
|
|
125
|
-
}
|
|
126
|
-
})
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* ★ **연결 이관의 문 셋** — 몸통은 순수 함수이고, 이 검사는 **문이 그 규율을 지키는지** 본다.
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇을 옮기나 (사용자와 맞춘 개념, 2026-09-05) ────────────────────────
|
|
5
|
-
* 옮기는 것은 「붙는 법」이지 「붙은 결과」가 아니다. 마스터·사실·잰 값·비밀값은 안 옮긴다 —
|
|
6
|
-
* 앞의 셋은 다시 얻을 수 있고, 비밀값은 그 환경의 것이지 셋업이 아니다.
|
|
7
|
-
*
|
|
8
|
-
* ── 이 검사가 지키는 것 ────────────────────────────────────────────────────
|
|
9
|
-
* 문이 몸통의 판단을 **뒤집지 않는지**다. 몸통 시험 19개가 이미 「비밀값이 안 나간다」를 지키는데,
|
|
10
|
-
* 문이 그것을 되살려 쓰면 그 19개가 아무것도 못 막는다.
|
|
11
|
-
*/
|
|
12
|
-
import { test } from 'node:test'
|
|
13
|
-
import assert from 'node:assert/strict'
|
|
14
|
-
|
|
15
|
-
import { exportConnections, planImport, SECRET_KEYS } from '../server/service/reference/connection-portability.js'
|
|
16
|
-
|
|
17
|
-
const row = (over: any = {}) => ({
|
|
18
|
-
source: 'mes-miratek',
|
|
19
|
-
system: 'operato-plant',
|
|
20
|
-
siteName: '미라텍',
|
|
21
|
-
adapterType: 'operato-plant',
|
|
22
|
-
connectionConfig: { baseUrl: 'https://plant.example', siteId: 'mes-miratek', token: 'SECRET-VALUE', hookSecret: 'ALSO-SECRET' },
|
|
23
|
-
...over
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('★ 비밀값이 패키지에 안 나간다 — 이름만 나간다', () => {
|
|
27
|
-
const b = exportConnections([row()], { at: '2026-09-05T00:00:00.000Z' })
|
|
28
|
-
const text = JSON.stringify(b)
|
|
29
|
-
|
|
30
|
-
assert.ok(!text.includes('SECRET-VALUE'), '토큰이 패키지에 실렸다')
|
|
31
|
-
assert.ok(!text.includes('ALSO-SECRET'), '훅 비밀값이 패키지에 실렸다')
|
|
32
|
-
assert.deepEqual(b.connections[0].secretsRequired.sort(), ['hookSecret', 'token'], '무엇을 채워야 하는지 말하지 않는다')
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
test('★ 선언은 나간다 — 그것이 옮기려는 것이다', () => {
|
|
36
|
-
const b = exportConnections([row()], { at: '2026-09-05T00:00:00.000Z' })
|
|
37
|
-
const c = b.connections[0]
|
|
38
|
-
|
|
39
|
-
assert.equal(c.adapterType, 'operato-plant')
|
|
40
|
-
assert.equal(c.config.baseUrl, 'https://plant.example')
|
|
41
|
-
assert.equal(c.config.siteId, 'mes-miratek')
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
test('★ 새 비밀 칸도 기본으로 안 나간다 — 이름 규칙으로 거른다', () => {
|
|
45
|
-
/*
|
|
46
|
-
* 비밀 칸이 하나 늘 때 이 목록을 안 고치면 그 값이 파일로 나간다. 그래서 규칙이 이름이고,
|
|
47
|
-
* 그 규칙이 지금 무엇을 아는지 값으로 못 박는다.
|
|
48
|
-
*/
|
|
49
|
-
assert.ok(SECRET_KEYS.includes('token' as any))
|
|
50
|
-
assert.ok(SECRET_KEYS.includes('hookSecret' as any))
|
|
51
|
-
assert.ok(SECRET_KEYS.includes('intakeSecret' as any))
|
|
52
|
-
assert.ok(SECRET_KEYS.length >= 4, `비밀 칸 이름을 ${SECRET_KEYS.length}개만 안다`)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('★ 없는 비밀값을 지어내지 않는다 — 이름을 대고 사람에게 넘긴다', () => {
|
|
56
|
-
const b = exportConnections([row()], { at: '2026-09-05T00:00:00.000Z' })
|
|
57
|
-
/* 이 환경에는 그 비밀값이 없다. */
|
|
58
|
-
const plan = planImport(b, [], () => false)
|
|
59
|
-
|
|
60
|
-
const needs = plan.needsHuman.find(n => n.source === 'mes-miratek')
|
|
61
|
-
assert.ok(needs, '사람이 채울 것을 말하지 않는다')
|
|
62
|
-
assert.deepEqual(needs!.keys.sort(), ['hookSecret', 'token'])
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
test('★ 이미 있는 연결의 비밀값을 패키지가 안 지운다', () => {
|
|
66
|
-
const b = exportConnections([row()], { at: '2026-09-05T00:00:00.000Z' })
|
|
67
|
-
const existing = [{ source: 'mes-miratek', connectionConfig: { token: 'LIVE-TOKEN' } }]
|
|
68
|
-
const plan = planImport(b, existing, () => false)
|
|
69
|
-
|
|
70
|
-
const a = plan.actions.find(x => (x as any).connection?.source === 'mes-miratek') as any
|
|
71
|
-
assert.equal(a?.action, 'update', `갱신이 아니라 ${a?.action} 이다`)
|
|
72
|
-
assert.ok(a.keptSecrets?.includes('token'), '이미 있는 토큰을 지킨다고 말하지 않는다')
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
test('★ 패키지가 아닌 것을 받으면 아무것도 하지 않는다', () => {
|
|
76
|
-
for (const bad of [null, {}, { kind: 'something-else' }, 'text']) {
|
|
77
|
-
const plan = planImport(bad, [], () => false)
|
|
78
|
-
assert.equal(plan.actions.length, 0, `${JSON.stringify(bad)} 로 무언가를 하려 했다`)
|
|
79
|
-
assert.ok(plan.errors.length, '왜 안 되는지 말하지 않는다')
|
|
80
|
-
}
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
test('★ 이 검사가 실제로 무언가를 잰다 — 견준 것이 0이면 빨강', () => {
|
|
84
|
-
const b = exportConnections([row()], { at: '2026-09-05T00:00:00.000Z' })
|
|
85
|
-
assert.equal(b.connections.length, 1, '패키지가 비었다 — 이 검사가 아무것도 안 잰다')
|
|
86
|
-
assert.ok(planImport(b, [], () => true).actions.length > 0, '계획이 비었다')
|
|
87
|
-
})
|