@operato/ops-contract 0.2.0 → 0.3.0

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.
@@ -555,7 +555,7 @@ export interface TestSpecificationCriterion {
555
555
  /** 표준 `EvaluatedPropertyID` — 이 기준이 **무엇을 재어** 판정하나. 측정값과 짝을 맞추는 키다. */
556
556
  evaluatedPropertyId?: string;
557
557
  }
558
- export interface TestSpecification {
558
+ export interface TestSpecification extends EffectivePeriod {
559
559
  /** 표준 `ID` — 자원의 `testSpecificationIds` 가 이 값을 가리킨다. */
560
560
  id: string;
561
561
  /** 표준 `Description`. i18n 키일 수 있다(사람 언어는 표현 계층이 렌더한다). */
@@ -3219,7 +3219,7 @@ export interface TwinModelDef {
3219
3219
  */
3220
3220
  identity?: IdentityDeclaration;
3221
3221
  /** parallelism = 동시 처리 수(LocationState.parallelism 참조). capacity 는 저장 용량. */
3222
- locations: {
3222
+ locations: (EffectivePeriod & {
3223
3223
  id: string;
3224
3224
  type: string;
3225
3225
  capacity: number;
@@ -3227,7 +3227,7 @@ export interface TwinModelDef {
3227
3227
  parentId?: string;
3228
3228
  properties?: ResourceProperty[];
3229
3229
  testSpecificationIds?: TestSpecificationRefs;
3230
- }[];
3230
+ })[];
3231
3231
  /**
3232
3232
  * 설비(설비). mtbfMs/mttrMs 지정 시 확률적 고장 모델 참여(OEE Availability 손실). 미지정=고장 없음.
3233
3233
  * `window` 지정 시 그 시간대에만 일한다(교대·가동시간) — 미지정이면 24시간 가용(기존 거동).
@@ -1,3 +1,4 @@
1
+ import type { EffectivePeriod } from './contract.ts';
1
2
  /** 표준 온톨로지 투영(열린 문자열 — 하드코딩 enum 금지). */
2
3
  /**
3
4
  * 표준 온톨로지 투영 — 이 개념을 각 표준이 무엇이라 부르나.
@@ -129,7 +130,7 @@ export interface DurationVariability {
129
130
  * 걸리나·얼마나 성공하나" 는 커널 소스의 상수(30·20·40초, 수율 0.8)였다. 그래서 현장마다 다른 값을
130
131
  * 데이터로 줄 방법이 없었고, 그 상수가 주목 신호(불량률 높음)까지 만들어 냈다.
131
132
  */
132
- export interface OperationDef {
133
+ export interface OperationDef extends EffectivePeriod {
133
134
  key: string;
134
135
  label: string;
135
136
  intent: OperationIntent;
@@ -3,7 +3,8 @@
3
3
  * 커널이 "무엇이 있고 어떻게 흐르나"(타입 + 공정 route/BOM)를 **데이터로** 받는 형식. zero-dep(자기 타입).
4
4
  *
5
5
  * 특정 공정이 커널 코드에 하드코딩되던 것을 이 데이터 계약으로 대체한다(design/plans/domain-catalog-layering.md).
6
- * 스토어 패키지 `@operato/twin-catalog` 가 이 타입을 import 스토어 메타(version/supplier)를 얹어 배포한다(catalog → kernel 의존).
6
+ * 스토어 패키지 `@operato/twin-catalog` 가 이 타입을 import type { EffectivePeriod } from './contract.ts'
7
+ import 해 스토어 메타(version/supplier)를 얹어 배포한다(catalog → kernel 의존).
7
8
  * 표준 앵커: GS1 EPCIS 2.0(bizStep) · ISA-95(WorkCenter·OperationsDefinition·BOM) · ISO 55000(Asset).
8
9
  */
9
10
  /**
package/dist/index.d.ts CHANGED
@@ -17,3 +17,4 @@ export * from './vocabulary.ts';
17
17
  export * from './wms-profile.ts';
18
18
  export * from './yms-profile.ts';
19
19
  export * from './canonical-record.ts';
20
+ export * from './version.ts';
package/dist/index.js CHANGED
@@ -38,3 +38,4 @@ export * from "./vocabulary.js";
38
38
  export * from "./wms-profile.js";
39
39
  export * from "./yms-profile.js";
40
40
  export * from "./canonical-record.js";
41
+ export * from "./version.js";
@@ -1,6 +1,6 @@
1
1
  import type { IngestResult } from './face2-adapter.ts';
2
2
  /** 이 문이 받는 여섯 가지 — 리듀서가 다루는 것과 같은 목록(주목 확인은 우리 안의 행위라 제외). */
3
- export type OperationalKind = 'task' | 'equipment' | 'person' | 'asset' | 'order' | 'quality' | 'test' | 'observation' | 'complete';
3
+ export type OperationalKind = 'task' | 'equipment' | 'person' | 'asset' | 'order' | 'quality' | 'test' | 'disposition' | 'observation' | 'complete';
4
4
  /**
5
5
  * 정규 운영 레코드 — **델타의 필드 이름 + 시각(`at`)**.
6
6
  *
@@ -34,7 +34,7 @@
34
34
  * 모르는 필드는 **조용히 버리지 않고 거부한다.** `taskID` 처럼 한 글자 틀린 이름은 통과시키면 영원히
35
35
  * 보이지 않는 손실이 된다(이 문에는 아직 옛 발신자가 없어 호환 부담도 없다).
36
36
  */
37
- import { OP_EVENT } from "./contract.js";
37
+ import { OP_EVENT, DISPOSITION_DECISION } from "./contract.js";
38
38
  /**
39
39
  * 닫아 둔 낱말과 그 이유.
40
40
  * · 작업 상태 — 성과 폴드가 `completed`·`in-progress` 로 갈린다(`kpi-fold`).
@@ -130,6 +130,32 @@ const SPECS = {
130
130
  * `propertyMeasurements` 안쪽은 재검사하지 않는다 — 그 모양은 `PropertyMeasurement` 계약이고,
131
131
  * 여기서 두 번 지키면 두 벌이 된다(설비 `motion` 과 같은 규율).
132
132
  */
133
+ /*
134
+ * **부적합 처분** — 재고 판정을 받은 것을 어떻게 하기로 정했나.
135
+ *
136
+ * ── 이 통이 늦게 생긴 이유 (2026-08-30) ──────────────────────────────────
137
+ * 사건 이름(`OP_EVENT.disposition`)과 모양(`DispositionFact`)을 먼저 만들고 **여기로 오는 길을
138
+ * 내지 않았다.** 그래서 처분 사실을 보내면 어느 통도 아니어서 떨어진 목록에도 남지 않고 사라졌다.
139
+ * MES 전문가가 소스를 읽다 찾았다.
140
+ *
141
+ * 이 파일의 위 주석이 말한 부류 그대로다 — `OP_EVENT.test` 와 `OP_EVENT.observation` 이 같은 일을
142
+ * 겪었고 이번이 셋째다. **계약에 자리를 만드는 것과 그 자리로 가는 길을 내는 것은 다른 일이다.**
143
+ *
144
+ * ── 판정과 다른 사실이다 ─────────────────────────────────────────────────
145
+ * 근거가 된 판정(`specId`)을 가리킬 수 있지만 **필수가 아니다** — 판정 없이 현장 재량으로 빼는 일이
146
+ * 정상이다. 그래서 정체성은 처분 대상(`subjectId`)이다.
147
+ */
148
+ disposition: {
149
+ eventType: OP_EVENT.disposition,
150
+ identity: 'subjectId',
151
+ /* 무엇을 어떻게 하기로 했나 — 둘 중 하나가 없으면 그 결정은 아무 데도 붙지 못한다. */
152
+ required: ['subjectId', 'decision'],
153
+ fields: {
154
+ subjectId: 'string', decision: 'string', quantity: 'number', uom: 'string',
155
+ specId: 'string', decidedBy: 'string', reason: 'string', decidedAt: 'string', recordTime: 'string'
156
+ },
157
+ enums: { decision: DISPOSITION_DECISION }
158
+ },
133
159
  test: {
134
160
  eventType: OP_EVENT.test,
135
161
  identity: 'testableObjectId',
@@ -221,6 +247,15 @@ export function operationalKindOf(record) {
221
247
  */
222
248
  if (has('testableObjectId'))
223
249
  return 'test';
250
+ /*
251
+ * **처분** — 같은 부류가 또 났다(2026-08-30). `OP_EVENT.disposition` 을 계약에 내고 여기로 오는 길을
252
+ * 내지 않아, MES 가 보내면 어느 통도 아니어서 사라졌다. 위 주석이 적은 것이 **셋째**다.
253
+ *
254
+ * 시험 결과 뒤에 둔다 — 처분은 판정을 가리킬 수 있고(`specId`), 그때 정체성은 처분 대상이지
255
+ * 시험 대상이 아니다. 앞에 두면 판정에 딸린 처분이 시험으로 읽힌다.
256
+ */
257
+ if (has('subjectId') && has('decision'))
258
+ return 'disposition';
224
259
  /*
225
260
  * ── ★ **채널을 열고 또 길을 내지 않았다** (2026-08-24) ──────────────────────
226
261
  * `OP_EVENT.observation`(`location.measured`)을 내고 상태(`LocationState.observations`)와 조회
@@ -0,0 +1 @@
1
+ export declare const CONTRACT_VERSION = "0.3.0";
@@ -0,0 +1,14 @@
1
+ /*
2
+ * 이 계약의 **판**.
3
+ *
4
+ * ── 왜 내보내나 (2026-08-30) ─────────────────────────────────────────────
5
+ * 소비처가 「내가 어느 판의 계약을 지키고 있나」를 사실로 남겨야 한다. MES 의 아웃박스가 그 값을
6
+ * 레코드에 싣는데, 계약이 내보내지 않아 **상수로 손으로 적고 있었다.** `exports` 맵이 `package.json`
7
+ * 을 막아 런타임에 읽을 수도 없다.
8
+ *
9
+ * 손으로 적은 판은 계약이 올라갈 때 같이 올라가지 않는다. 그러면 그 값은 「지킨다고 말한 판」이지
10
+ * 「실제로 지킨 판」이 아니게 되고, 그것이 어긋난 날 아무도 모른다.
11
+ *
12
+ * **정본은 `package.json` 이다.** 이 값이 그것과 같은지는 시험이 지킨다(§`version-matches`).
13
+ */
14
+ export const CONTRACT_VERSION = '0.3.0';
@@ -27,6 +27,7 @@ __export(index_exports, {
27
27
  CAPABILITY_KEYS: () => CAPABILITY_KEYS,
28
28
  CBV_BIZSTEP: () => CBV_BIZSTEP,
29
29
  CMD: () => CMD,
30
+ CONTRACT_VERSION: () => CONTRACT_VERSION,
30
31
  DISP: () => DISP,
31
32
  DISPOSITION_DECISION: () => DISPOSITION_DECISION,
32
33
  DOMAIN_CATALOG: () => DOMAIN_CATALOG,
@@ -3295,6 +3296,39 @@ var SPECS = {
3295
3296
  * `propertyMeasurements` 안쪽은 재검사하지 않는다 — 그 모양은 `PropertyMeasurement` 계약이고,
3296
3297
  * 여기서 두 번 지키면 두 벌이 된다(설비 `motion` 과 같은 규율).
3297
3298
  */
3299
+ /*
3300
+ * **부적합 처분** — 재고 판정을 받은 것을 어떻게 하기로 정했나.
3301
+ *
3302
+ * ── 이 통이 늦게 생긴 이유 (2026-08-30) ──────────────────────────────────
3303
+ * 사건 이름(`OP_EVENT.disposition`)과 모양(`DispositionFact`)을 먼저 만들고 **여기로 오는 길을
3304
+ * 내지 않았다.** 그래서 처분 사실을 보내면 어느 통도 아니어서 떨어진 목록에도 남지 않고 사라졌다.
3305
+ * MES 전문가가 소스를 읽다 찾았다.
3306
+ *
3307
+ * 이 파일의 위 주석이 말한 부류 그대로다 — `OP_EVENT.test` 와 `OP_EVENT.observation` 이 같은 일을
3308
+ * 겪었고 이번이 셋째다. **계약에 자리를 만드는 것과 그 자리로 가는 길을 내는 것은 다른 일이다.**
3309
+ *
3310
+ * ── 판정과 다른 사실이다 ─────────────────────────────────────────────────
3311
+ * 근거가 된 판정(`specId`)을 가리킬 수 있지만 **필수가 아니다** — 판정 없이 현장 재량으로 빼는 일이
3312
+ * 정상이다. 그래서 정체성은 처분 대상(`subjectId`)이다.
3313
+ */
3314
+ disposition: {
3315
+ eventType: OP_EVENT.disposition,
3316
+ identity: "subjectId",
3317
+ /* 무엇을 어떻게 하기로 했나 — 둘 중 하나가 없으면 그 결정은 아무 데도 붙지 못한다. */
3318
+ required: ["subjectId", "decision"],
3319
+ fields: {
3320
+ subjectId: "string",
3321
+ decision: "string",
3322
+ quantity: "number",
3323
+ uom: "string",
3324
+ specId: "string",
3325
+ decidedBy: "string",
3326
+ reason: "string",
3327
+ decidedAt: "string",
3328
+ recordTime: "string"
3329
+ },
3330
+ enums: { decision: DISPOSITION_DECISION }
3331
+ },
3298
3332
  test: {
3299
3333
  eventType: OP_EVENT.test,
3300
3334
  identity: "testableObjectId",
@@ -3364,6 +3398,7 @@ function operationalKindOf(record) {
3364
3398
  if (has("taskId")) return "task";
3365
3399
  if (has("orderId")) return "order";
3366
3400
  if (has("testableObjectId")) return "test";
3401
+ if (has("subjectId") && has("decision")) return "disposition";
3367
3402
  if (has("locationId") && has("propertyId")) return "observation";
3368
3403
  if (has("completeAxis")) return "complete";
3369
3404
  return void 0;
@@ -3634,6 +3669,9 @@ function retiredVocabularyIn(line) {
3634
3669
  }
3635
3670
  return hits;
3636
3671
  }
3672
+
3673
+ // src/version.ts
3674
+ var CONTRACT_VERSION = "0.3.0";
3637
3675
  // Annotate the CommonJS export names for ESM import in node:
3638
3676
  0 && (module.exports = {
3639
3677
  BIZSTEP,
@@ -3644,6 +3682,7 @@ function retiredVocabularyIn(line) {
3644
3682
  CAPABILITY_KEYS,
3645
3683
  CBV_BIZSTEP,
3646
3684
  CMD,
3685
+ CONTRACT_VERSION,
3647
3686
  DISP,
3648
3687
  DISPOSITION_DECISION,
3649
3688
  DOMAIN_CATALOG,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/ops-contract",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Operations domain contract — the standard vocabulary that producers and readers agree on (EPCIS 2.0/GS1, ISA-95, IEC 61850/ISO 50001). Types, guards, validation. No state, no engine.",
5
5
  "type": "module",
6
6
  "main": "./dist-cjs/index.cjs",