@g1cloud/bpmn-modeler-next 5.0.0-beta.3 → 5.0.0-beta.31

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.
@@ -1,9 +1,41 @@
1
+ export declare const I18N_NS = "http://www.omg.org/spec/BPMN/non-normative/extensions/i18n/1.0";
2
+ export declare const I18N_PREFIX = "i18n";
3
+ /** 번역 칸 moddle 타입. */
4
+ export declare const I18N_TRANSLATION_TYPE = "i18n:Translation";
5
+ type Any = any;
6
+ /**
7
+ * 라벨 문구가 실제로 사는 (객체, 키) — bpmn-js `LabelUtil.getLabelAttr` 와 같은 판정.
8
+ * 주석은 `text`, 그룹은 `categoryValueRef.value`, 나머지는 `name`.
9
+ */
10
+ export declare function labelSlot(bo: Any): {
11
+ target: Any;
12
+ key: string;
13
+ } | undefined;
14
+ /** 요소의 번역 칸 전부(`extensionElements` 안의 `i18n:Translation`). */
15
+ export declare function translationsOf(bo: Any): Any[];
16
+ /** 번역 칸의 로케일. */
17
+ export declare function translationLocale(t: Any): string | undefined;
18
+ /** 요소의 확인 요청 표식 전부(`bwl:ReviewRequest` — 상태 표식 · C-1). */
19
+ export declare function reviewRequestsOf(bo: Any): Any[];
1
20
  /**
2
- * 레거시 bpmn-i18n-moddle 호환 (백로그 B-4).
21
+ * 문서 언어 설정(B-4) — 설계 언어(고객에게 전달하는 설계 문서의 언어 · `name` 이 따른다) + 병기 언어(함께 최신으로
22
+ * 유지할 언어들). 호스트 그룹 설정(`designLocale` · `companionLocales`)과 같은 이름이라 그대로 넘긴다.
23
+ * 미설정(`undefined`) = 기능 끔.
24
+ */
25
+ export interface BpmnDocumentLocales {
26
+ designLocale: string;
27
+ companionLocales: string[];
28
+ }
29
+ /** 문서 언어 목록 — 설계 언어가 맨 앞 · 병기 언어에서 설계 언어와 중복은 뺀다. */
30
+ export declare function documentLocalesOf(config: BpmnDocumentLocales): string[];
31
+ /** 루트 `<bpmn:definitions xml:lang>` — 번역 칸으로 판정되지 않는 `name` 의 언어. */
32
+ export declare function rootLangOf(definitions: Any): string | undefined;
33
+ /**
34
+ * 요소 라벨의 문서 언어별 문구(B-4 ④) — 표시 판정(`view/i18nDisplay` `resolve`)과 **같은 규칙**이다:
35
+ * `name` 의 언어가 그 언어면 `name` → 아니면 그 언어의 번역 칸(`xml:lang` 있는 칸 · 문구 있음) → 없으면 빠진다
36
+ * (= 미번역). `name` 의 언어 = 같은 문구의 번역 칸 로케일 → 없으면 루트 `xml:lang` → 설계 언어.
3
37
  *
4
- * 스키마 자체는 유지한다 — 기존 운영 문서(실측 112곳 확장)와의 호환 필수.
5
- * 재설계 대상은 스키마가 아니라 적용 방식: 레거시 bpmnTranslationService 의
6
- * commandStack 우회 변조를 표시용/저장용 라벨 분리로 대체한다.
38
+ * 에이전트 대면 read(`describeProcess` 의 `names`/`texts`)와 에이전트 쓰기의 「바뀌었나」 판정이 같이 쓴다.
7
39
  */
8
- export declare const I18N_NS = "http://www.omg.org/spec/BPMN/non-normative/extensions/i18n/1.0";
9
- export declare const I18N_PREFIX = "i18n";
40
+ export declare function localeTextsOf(bo: Any, config: BpmnDocumentLocales, rootLang?: string): Record<string, string>;
41
+ export {};
@@ -0,0 +1,64 @@
1
+ type Any = any;
2
+ /** 문자 체계 — 판정에 쓰는 것은 한글 · 가나뿐이다. */
3
+ export type ScriptKind = 'hangul' | 'kana' | 'mixed' | 'han' | 'other';
4
+ export interface ScriptInference {
5
+ script: ScriptKind;
6
+ /** 문자 체계로 정해지는 언어 — 한글 = ko · 가나 = ja · 그 밖 = 없음. */
7
+ locale?: 'ko' | 'ja';
8
+ }
9
+ /** 문구의 문자 체계 판정. 한글과 가나가 함께 있으면 `mixed`(판정 불가). */
10
+ export declare function inferScriptLocale(text: string): ScriptInference;
11
+ /**
12
+ * 루트 판정의 근거.
13
+ * `kept` 이미 있다 · `matched` 번역 칸과 같은 이름들의 공통 언어 · `script` 문자 체계가 한 언어로 모인다 ·
14
+ * `ambiguous` 공통 언어가 여럿이고 문자 체계로도 못 고른다 · `mixed` 근거가 서로 다른 언어 · `no-evidence` 근거 없음
15
+ */
16
+ export type RootLocaleReason = 'kept' | 'matched' | 'script' | 'ambiguous' | 'mixed' | 'no-evidence';
17
+ /** 계획 항목이 가리키는 요소 — 미리보기 화면이 사람에게 보여 줄 최소 정보. */
18
+ export interface LocaleBackfillElement {
19
+ id: string;
20
+ /** moddle 타입(`bpmn:Task` 등). */
21
+ type: string;
22
+ /** 계획 시점의 라벨 문구. 적용은 이 문구가 그대로일 때만 쓴다. */
23
+ name: string;
24
+ }
25
+ export interface LocaleBackfillPlan {
26
+ root: {
27
+ /** 지금 루트 `xml:lang`. */
28
+ current?: string;
29
+ /** 새로 쓸 값 — `current` 가 있거나 판정 불가면 없다. */
30
+ proposed?: string;
31
+ reason: RootLocaleReason;
32
+ };
33
+ /** 더할 번역 칸(본문 = 그 요소의 `name`). */
34
+ additions: Array<LocaleBackfillElement & {
35
+ locale: string;
36
+ }>;
37
+ /** 언어 기록이 없고 문자 체계로도 못 가르는 이름 — 루트를 따른다 · 사람 확인 대상. */
38
+ undecided: Array<LocaleBackfillElement & {
39
+ script: ScriptKind;
40
+ }>;
41
+ /** 이름이 언어 있는 칸 어느 것과도 다르다(칸은 있다) — 백필 대상 아님(별도 정리). */
42
+ drift: LocaleBackfillElement[];
43
+ /**
44
+ * 이름이 언어 있는 칸과 같은 요소의 분포 — 키 = 일치한 로케일(정렬 · `+` 연결, 예 `ja` · `ja+ko`).
45
+ * 루트 판정의 근거이고, 판정 불가 문서에서 사람이 루트를 고를 근거다.
46
+ */
47
+ matchedBy: Record<string, number>;
48
+ /**
49
+ * 칸 언어 ↔ 문자 체계 불일치(레거시 복사본 번역 흔적 · F8) — **기록 대상 아님**(별도 정리).
50
+ * `script` = `kana` · `hangul` 은 그 칸 언어가 아닌 문자 · `han` 은 ko 칸이 한자만이고 다른 칸과 같은 문구(참고값 · 사람 판단).
51
+ */
52
+ scriptMismatch: Array<LocaleBackfillElement & {
53
+ locale: string;
54
+ script: ScriptKind;
55
+ }>;
56
+ }
57
+ /** 언어 백필 계획 — 문서를 바꾸지 않는다. */
58
+ export declare function planLocaleBackfill(definitions: Any): LocaleBackfillPlan;
59
+ /**
60
+ * 계획을 문서에 쓴다 — 바꾼 곳 수를 돌려준다. **멱등**이고 계획 뒤 문서가 바뀌었어도 덮지 않는다:
61
+ * 루트가 이미 있으면 루트를 건너뛰고, 요소는 지금도 「언어 기록 없음 + 이름이 계획 때 그대로」일 때만 쓴다.
62
+ */
63
+ export declare function applyLocaleBackfill(definitions: Any, plan: LocaleBackfillPlan): number;
64
+ export {};
@@ -2,6 +2,7 @@ import { BpmnDocumentModelerLike } from '../core/modelerLike';
2
2
  import { SymbolicOp } from './symbolicOp';
3
3
  import { ApplyBpmnOpsResult, ResolveIssue } from './resolver';
4
4
  import { GeometryFinding } from './geometryAudit';
5
+ import { BpmnDocumentLocales } from '../adapter/i18n';
5
6
  /** op 출처 — 감사·정책 분기용. `gui` = 호스트 채팅 패널, `rest` = 외부 브리지. */
6
7
  export type BpmnOpOrigin = 'agent' | 'gui' | 'rest';
7
8
  export interface BpmnOpBatch {
@@ -54,6 +55,11 @@ export type ApplyBatchOutcome =
54
55
  export interface ApplyBpmnOpBatchOptions {
55
56
  /** 적용 시점의 권위 버전 — 호스트(영속 소유자)가 공급한다. */
56
57
  currentVersion: number;
58
+ /**
59
+ * 그 문서가 속한 그룹의 문서 언어 설정(B-4 ④) — 호스트가 공급한다(문서에 없다). 있으면 문구는 `names`/`texts`
60
+ * 로 문서 언어 전부를 받고, 없으면 평문만 받는다(`applyBpmnOps` 의 같은 옵션).
61
+ */
62
+ i18n?: BpmnDocumentLocales;
57
63
  }
58
64
  /**
59
65
  * 배치를 캔버스에 적용하고 영속 페이로드를 낸다.
@@ -64,4 +70,4 @@ export interface ApplyBpmnOpBatchOptions {
64
70
  * 적용은 단일 undo 에피소드다(`applyBpmnOps` → `command/undoEpisode`) — 배치 수십 op 가
65
71
  * 사용자 undo 1회로 되감긴다.
66
72
  */
67
- export declare function applyBpmnOpBatch(modeler: BpmnDocumentModelerLike, batch: BpmnOpBatch, { currentVersion }: ApplyBpmnOpBatchOptions): Promise<ApplyBatchOutcome>;
73
+ export declare function applyBpmnOpBatch(modeler: BpmnDocumentModelerLike, batch: BpmnOpBatch, { currentVersion, i18n }: ApplyBpmnOpBatchOptions): Promise<ApplyBatchOutcome>;
@@ -1,4 +1,6 @@
1
1
  import { ModdleElement, ParseWarning } from '../adapter/xml';
2
+ import { BpmnDocumentLocales } from '../adapter/i18n';
3
+ import { ReviewRequestValue } from '../command/setTranslations';
2
4
  import { BwlAfterColumn, BwlBand, BwlPlacement, BwlPoolOrder } from '../core/hints';
3
5
  import { IrDefinition } from '../core/ir';
4
6
  /** 파생 뷰가 표현하지 못한 요소 — 침묵 폐기 금지(에이전트가 미표현 존재를 알아야 안전). */
@@ -41,15 +43,40 @@ export interface BwlHintView {
41
43
  /** 주석 id → 앵커 기준 방위 (앵커 부재분은 필터됨) */
42
44
  placements: Record<string, BwlPlacement>;
43
45
  }
46
+ /** 확인 요청 표식 한 건(B-4 ④) — `id` 요소의 `locale` 칸에 걸렸다. `sourceLocale` 이 `previous` → 지금 문구로 고쳐졌다. */
47
+ export interface ReviewRequestView extends ReviewRequestValue {
48
+ id: string;
49
+ }
50
+ /**
51
+ * 다국어 판독 결과(B-4 ④) — 문서 언어 설정이 있을 때만 낸다. IR 밖 채널(위 헤더 「다국어」).
52
+ * 에이전트는 이 값이 있으면 문구를 `names`/`texts` 로 문서 언어 전부를 써야 한다.
53
+ */
54
+ export interface I18nView {
55
+ designLocale: string;
56
+ /** 병기 언어(설계 언어 제외 · 중복 제거). */
57
+ companionLocales: string[];
58
+ /**
59
+ * 정리할 확인 요청 — 문서 언어 칸에 걸린 것만. `previous`(고치기 전 원문) → 지금 `names[sourceLocale]` 를 대조해
60
+ * 오타 수준이면 `translation.confirm`, 내용이 바뀌었으면 그 요소 update 에 `names` 로 번역을 고친다.
61
+ */
62
+ reviewRequests: ReviewRequestView[];
63
+ }
44
64
  export interface DescribeProcessResult {
45
65
  definition: IrDefinition;
46
66
  hints: BwlHintView;
47
67
  omitted: IrOmission[];
68
+ /** 문서 언어 설정이 있을 때만 있다(B-4 ④). */
69
+ i18n?: I18nView;
70
+ }
71
+ /** 판독 옵션 — 문서 밖에서만 알 수 있는 입력. */
72
+ export interface DescribeProcessOptions {
73
+ /** 그룹의 문서 언어 설정(B-4 ④) — 문서에 없어 호스트가 공급한다. 없으면 다국어 판독을 하지 않는다. */
74
+ i18n?: BpmnDocumentLocales;
48
75
  }
49
76
  export interface DescribeProcessXmlResult extends DescribeProcessResult {
50
77
  /** 입력 XML 파싱 경고 — 실운영 문서는 빈 배열이어야 한다(M1 수용 기준 1). */
51
78
  warnings: ParseWarning[];
52
79
  }
53
- export declare function describeProcess(definitions: ModdleElement): DescribeProcessResult;
80
+ export declare function describeProcess(definitions: ModdleElement, options?: DescribeProcessOptions): DescribeProcessResult;
54
81
  /** XML 문자열 입력 래퍼 — 헤드리스 read 경로(호스트 `describeProcess` API·테스트). */
55
- export declare function describeProcessXml(xml: string): Promise<DescribeProcessXmlResult>;
82
+ export declare function describeProcessXml(xml: string, options?: DescribeProcessOptions): Promise<DescribeProcessXmlResult>;
@@ -613,6 +613,8 @@ export declare function segmentShapeClearance(a: GeometryPoint, b: GeometryPoint
613
613
  /**
614
614
  * 한 간선(또는 그 후보 경로)이 다른 간선들과 교차하는 수. **끝점을 공유하는 간선은 제외**한다 —
615
615
  * 같은 노드에서 나가는 팬아웃·같은 노드로 드는 합류는 꺾이는 자리가 겹쳐 보여도 교차가 아니다.
616
+ * ⚠ `segmentsCross` 가 이미 접촉을 안 세므로 이 제외가 실제로 막는 것은 **내부 진성 교차**뿐이다(B-146) —
617
+ * 그쪽은 `sharedEndpointCrossingPairsOf` 가 따로 센다. 이 제외를 풀지 말 것: 위에 라우팅 채택 게이트가 선다.
616
618
  */
617
619
  export declare function edgeCrossCount(edge: GeometryEdge, others: readonly GeometryEdge[]): number;
618
620
  /**
@@ -625,6 +627,18 @@ export declare function crossingCost(edge: GeometryEdge, others: readonly Geomet
625
627
  export declare function sceneCrossingCost(scene: GeometryScene): number;
626
628
  /** 장면 전체의 교차 쌍(id 오름차순 정렬, 결정론적). 길이가 곧 교차 수다. */
627
629
  export declare function edgeCrossingPairsOf(scene: GeometryScene): Array<[string, string]>;
630
+ /**
631
+ * **끝점을 공유하는** 쌍의 진성 교차(B-146 ⓐ′ · 관측 전용) — `edgeCrossingPairsOf` 가 통째로 빼는 쌍 중
632
+ * 간선 **내부**에서 가로지르는 것. 둘을 합치면 장면의 진성 교차 전량이고 겹치는 쌍은 없다.
633
+ *
634
+ * 표본 98 에서 사람이 고친 교차가 이것이었다 — 게이트웨이 → 오류 노드 갈래와 오류 노드 → 상류 되돌아감이
635
+ * 오류 노드를 함께 끝점으로 가져 판정·계수가 모두 침묵했다(사람은 두 간선의 출입 변을 맞바꿨다).
636
+ * 🔴 **라우팅·채택 게이트의 입력이 아니다** — `edgeCrossCount` 의 제외를 바꾸면 그 위의 우회 채택·교차 보정과
637
+ * `--check` 기대값이 함께 움직인다. 이 함수는 보이게만 한다.
638
+ * ⚠ 「공유」는 각 어댑터의 `endpoints` 를 따른다 — 데이터 연관은 두 어댑터가 다르게 담는다(저장본 = 저장소만 ·
639
+ * 캔버스 = 활동 + 저장소).
640
+ */
641
+ export declare function sharedEndpointCrossingPairsOf(scene: GeometryScene): Array<[string, string]>;
628
642
  /**
629
643
  * 교차 쌍을 **에이전트가 판단할 수 있는 재료로 분해**한다(B-43). 개수만으로는 "이 정도가 정상인가" 를
630
644
  * 가릴 수 없고, 지금까지 세 회차에서 같은 마찰이 났다(표본 34·36 은 재료 없이도 결론이 맞았으나
@@ -9,6 +9,13 @@ export interface DocumentIndex {
9
9
  categoryOf(id: string): ElementCategory | undefined;
10
10
  /** 참여자에 속한 레인 id 목록 — **y순(위→아래)** 계약. lanes 재선언 diff 의 순서 비교 기준 */
11
11
  lanesOf(participantId: string): readonly string[];
12
+ /**
13
+ * 참여자에 속한 레인 id 목록 — **선언순**(laneSet 순서 = `describe` 가 내는 순서 · A-6). lanes 재선언은 이 순서도
14
+ * 받는다(B-153 — 선언순 ≠ y순 풀에서 `describe` 순서를 그대로 쓴 재선언이 거부됐다 · 레거시 레인 풀 19/50).
15
+ * 중첩 레인(`childLaneSet`)은 부모 뒤에 깊이 우선으로 잇는다(`lanesOf` 와 같은 집합).
16
+ * 선택 — 공개 타입이라 외부 구현을 깨지 않으려고 둔다(없으면 y순만 받는 종전 동작).
17
+ */
18
+ laneDeclarationOrderOf?(participantId: string): readonly string[];
12
19
  /** 플로우 노드가 속한 참여자 id (풀 없는 단일 프로세스 문서면 undefined) */
13
20
  participantOf(nodeId: string): string | undefined;
14
21
  /**
@@ -26,6 +33,11 @@ export interface DocumentIndex {
26
33
  * 어휘 밖 정의면 undefined.
27
34
  */
28
35
  eventDefinitionOf(nodeId: string): IrEventDefinition | undefined;
36
+ /**
37
+ * 요소에 확인 요청 표식(`bwl:reviewRequest`)이 걸린 언어들 (B-4 ④) — `translation.confirm` 이 없는 표식을
38
+ * 지목하면 거부한다(읽은 뒤 사람이 이미 정리했을 수 있다). 없으면 빈 배열.
39
+ */
40
+ reviewRequestLocalesOf(id: string): readonly string[];
29
41
  }
30
42
  /** elementRegistry 어댑터 — 하강 직전 실문서 상태를 대변한다. */
31
43
  export declare function buildRegistryIndex(registry: ElementRegistry): DocumentIndex;
@@ -2,11 +2,12 @@ import { BpmnModelerLike } from '../../core/modelerLike';
2
2
  import { SymbolicOp } from '../symbolicOp';
3
3
  import { ResolveIssue } from './issues';
4
4
  import { GeometryFinding } from '../geometryAudit';
5
+ import { BpmnDocumentLocales } from '../../adapter/i18n';
5
6
  import { lowerOps } from './lower';
6
7
  export type { ResolveIssue } from './issues';
7
8
  export type { DocumentIndex, ElementCategory } from './documentIndex';
8
9
  export { buildRegistryIndex } from './documentIndex';
9
- export { validateOps } from './validate';
10
+ export { validateOps, type ValidateOpsOptions } from './validate';
10
11
  export { lowerOps, resolverServicesOf, type ResolverServices } from './lower';
11
12
  export { forwardThreePointCandidates, planCrossPoolRoute, planEdgeRepair, planLabelLineClearance, planLabelPlacement, planMiddleSegmentShifts, planOppositeOverlapRepair, planSegmentDetours, planBackEdgeBoundaryClearance, repairIntroducedBoundaryHugging, repairIntroducedCrossings, repairIntroducedEdgeCrossings, repairIntroducedFakeMerges, repairIntroducedOppositeOverlaps, repairIntroducedSharedSegmentLabels, followMovedEdgeLabels, redockIntroducedDetachedEndpoints, repairIntroducedLabelLines, repairIntroducedShapeLabelLines, planShapeLabelClearance, rerouteIntroducedCrossPoolEdges, } from './routeRepair';
12
13
  export interface ApplyBpmnOpsResult {
@@ -57,6 +58,28 @@ export interface ApplyBpmnOpsResult {
57
58
  */
58
59
  crossPoolMessages: number;
59
60
  };
61
+ /**
62
+ * **끝점을 공유하는** 간선끼리의 내부 진성 교차(B-146 ⓐ′) — 위 `edgeCrossings` 가 세지 않는 쌍이다(둘을 합치면
63
+ * 진성 교차 전량 · 겹치는 쌍 없음). 표본 98 에서 사람이 고친 교차가 이것이었다(게이트웨이 → 오류 노드 갈래 ×
64
+ * 오류 노드 → 상류 되돌아감). **관측 전용** — 보정 단계의 채택 게이트는 이 값을 보지 않는다.
65
+ */
66
+ sharedEndpointCrossings: {
67
+ before: number;
68
+ after: number;
69
+ /** 하강 뒤 남은 쌍(간선 id, 오름차순). */
70
+ pairs: Array<[string, string]>;
71
+ };
60
72
  };
61
73
  }
62
- export declare function applyBpmnOps(modeler: BpmnModelerLike, ops: readonly SymbolicOp[], lower?: typeof lowerOps): ApplyBpmnOpsResult;
74
+ /** 배치 적용 옵션 — 문서 밖에서만 알 수 있는 입력. */
75
+ export interface ApplyBpmnOpsOptions {
76
+ /**
77
+ * 그룹의 문서 언어 설정(B-4 ④). 있으면 다국어 그룹이다 — 문구는 `names`/`texts` 로 문서 언어 전부를 받고
78
+ * (평문 거부) 하강 뒤 번역 칸을 같은 undo 에피소드에서 기록한다. 없으면 기능이 꺼진 그룹이다(평문만).
79
+ * 🔴 **문서에는 이 설정이 없다**(루트 `xml:lang` 은 만들 당시의 설계 언어일 뿐이다) — 호스트가 그룹 설정을 공급한다.
80
+ */
81
+ i18n?: BpmnDocumentLocales;
82
+ /** 테스트 시임 — 롤백 경로를 결정론적으로 검증하기 위한 하강 함수 주입점. 실사용은 기본값. */
83
+ lower?: typeof lowerOps;
84
+ }
85
+ export declare function applyBpmnOps(modeler: BpmnModelerLike, ops: readonly SymbolicOp[], options?: ApplyBpmnOpsOptions): ApplyBpmnOpsResult;
@@ -167,6 +167,59 @@ export type ResolveIssue =
167
167
  type: string;
168
168
  dropped: string[];
169
169
  }
170
+ /** 평문(`name`/`text`)과 로케일 맵(`names`/`texts`)을 함께 줬다 — 어느 쪽이 진실인지 모호하다(🔁 `condition-name-conflict`). */
171
+ | {
172
+ kind: 'locale-texts-conflict';
173
+ opIndex: number;
174
+ id: string;
175
+ field: string;
176
+ }
177
+ /**
178
+ * 다국어 그룹(문서 언어 설정 있음)에 평문을 줬다 — 언어를 모르는 문구는 설계 언어 칸에 다른 언어가 들어가는 경로다
179
+ * (켠 뒤의 에이전트 배치가 언어 기록 없는 이름 · drift 를 만드는 근인). `names`/`texts` 로 문서 언어 전부를 준다.
180
+ * 🔴 **조건 라벨(`condition`)도 여기다** — name 채널의 별칭(A-5)이라 다국어 그룹에선 그 플로우의 `names` 로 쓴다.
181
+ */
182
+ | {
183
+ kind: 'locale-texts-required';
184
+ opIndex: number;
185
+ id: string;
186
+ field: string;
187
+ locales: string[];
188
+ }
189
+ /** 기능이 꺼진 그룹(문서 언어 미설정)에 로케일 맵이나 `translation.confirm` 을 줬다 — 평문을 쓴다. */
190
+ | {
191
+ kind: 'locale-texts-not-enabled';
192
+ opIndex: number;
193
+ id: string;
194
+ field: string;
195
+ }
196
+ /** 로케일 맵에 문서 언어가 빠졌다 — 에이전트 쓰기는 문서 언어 **전부**를 함께 쓴다(그래야 표식이 안 생긴다). */
197
+ | {
198
+ kind: 'locale-texts-incomplete';
199
+ opIndex: number;
200
+ id: string;
201
+ field: string;
202
+ missing: string[];
203
+ }
204
+ /** 문서 언어(설계 + 병기) 밖의 언어 — 표시도 판정도 안 하는 칸이라 아무도 못 보고 못 고친다. */
205
+ | {
206
+ kind: 'locale-not-in-document';
207
+ opIndex: number;
208
+ id: string;
209
+ field: string;
210
+ locale: string;
211
+ locales: string[];
212
+ }
213
+ /**
214
+ * `translation.confirm` 의 대상 칸에 확인 요청 표식이 없다 — 읽은 뒤 사람이 이미 정리했거나 언어를 잘못 짚었다.
215
+ * 다시 읽어 `i18n.reviewRequests` 를 본다.
216
+ */
217
+ | {
218
+ kind: 'review-request-missing';
219
+ opIndex: number;
220
+ id: string;
221
+ locale: string;
222
+ }
170
223
  /** 하강 실행 중 실패 — 실행분은 롤백됨(원자성 2차 방어) */
171
224
  | {
172
225
  kind: 'lower-failed';
@@ -0,0 +1,24 @@
1
+ import { BpmnDocumentLocales } from '../../adapter/i18n';
2
+ import { SymbolicOp } from '../symbolicOp';
3
+ import { DocumentIndex } from './documentIndex';
4
+ import { ResolveIssue } from './issues';
5
+ /** 하강 뒤 한 요소에 할 번역 쓰기 — 같은 요소를 여러 op 가 건드리면 합친다. */
6
+ export interface LocaleWrite {
7
+ /** 문서 언어 전부의 문구. `null` = 라벨 제거(칸 · 표식 전부). 없으면 문구를 쓰지 않는다. */
8
+ texts?: Record<string, string> | null;
9
+ /** 문구를 바꾸지 않고 해소할 확인 요청의 언어들(`translation.confirm`). */
10
+ confirm?: string[];
11
+ }
12
+ /**
13
+ * 다국어 검증(순수). `config` = 그룹의 문서 언어 설정 — 없으면 기능이 꺼진 그룹이다.
14
+ * 페이로드 shape(맵이 객체인가 · 값이 비지 않은 문자열인가)도 여기서 본다 — 패스 0 은 kind 별 고정 자리만 안다.
15
+ */
16
+ export declare function validateLocaleTexts(ops: readonly SymbolicOp[], index: DocumentIndex, config: BpmnDocumentLocales | undefined): ResolveIssue[];
17
+ /**
18
+ * 하강 준비 — 검증 통과가 전제다. 반환 `ops` 는 평문만 남은 사본(설계 언어 문구 · `translation.confirm` 제외)이고,
19
+ * `writes` 는 하강 뒤 요소별로 할 번역 쓰기다. `config` 가 없으면(기능 끔) 입력을 그대로 돌려준다.
20
+ */
21
+ export declare function localizeOps(ops: readonly SymbolicOp[], index: DocumentIndex, config: BpmnDocumentLocales | undefined): {
22
+ ops: SymbolicOp[];
23
+ writes: Map<string, LocaleWrite>;
24
+ };
@@ -39,6 +39,14 @@ export declare function resolverServicesOf(modeler: BpmnModelerLike): ResolverSe
39
39
  * B-33 → B-34 ⓐ 가 그룹 경계 보정에서 닫은 것과 같은 클래스다.
40
40
  *
41
41
  * `selfId` = 이미 존재하는 주석(update)의 자기 도형·자기 연관을 장애물에서 뺀다 — 안 빼면 늘 "막힘"이다.
42
+ *
43
+ * **B-144** — 앵커가 레인 안이면 클램프 상자와 대안 방위의 `inside` 를 **앵커 레인 대역**으로 좁힌다(풀 클램프와
44
+ * 같은 규칙 — 레인 경계에서 클램프하고 앵커와 겹치면 오른쪽으로 비낀다). 🔴 **레인 안에 막히지 않은 자리가 없으면
45
+ * 종전대로 풀 안 빈 자리(이웃 레인일 수 있다)로 간다** — 레인 소속을 막힌 자리(겹침·관통)와 바꾸지 않는다
46
+ * (반사실: 레인 안 막힌 자리로 두는 안은 findings 가 는 케이스가 두 배였다 · 66 vs 33 / 방위 합성 6,208).
47
+ * 그것도 없으면 레인 안 선언 자리(막힘 — 판정기가 보고한다). ⚠ 잔존 대가 = 그 폴백의 이웃 레인 착지(방위 합성
48
+ * 0.6% — 사람 기저 0.4~1.0% 수준) · 레인 클램프가 오른쪽 비끼기를 더 자주 발동시켜 **가로로 풀 밖** 착지가
49
+ * 12 → 16(그 분기가 풀 우변을 넘는 것은 이 축 이전부터다 — 원장 B-144 곁).
42
50
  */
43
51
  export declare function annotationSpot(elementRegistry: ElementRegistry, anchor: {
44
52
  x: number;
@@ -97,13 +105,22 @@ export declare function repairIntroducedAnnotationOverlaps(services: Pick<Resolv
97
105
  */
98
106
  export declare function repairIntroducedPoolOverlaps(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeKeys: ReadonlySet<string>, sceneBefore: GeometryScene): void;
99
107
  /**
100
- * 🔴 **B-120 — 문서 전체가 한 우변에 맞춰져 있던 풀들을 배치 뒤에도 한 우변으로 되돌린다.**
108
+ * 🔴 **B-120 — 배치 전 한 우변에 맞춰져 있던 풀 무리를 배치 뒤에도 한 우변으로 되돌린다.**
101
109
  *
102
110
  * 증언(표본 86) = *"레인 끝부분이 걸린건 위쪽의 세개의 레인이 정렬되있어서야"*. 배치는 두 풀만 늘렸고 **늘리는 경로가 둘인데
103
111
  * 기준이 다르다**(`shiftGroups` = 내용 우단 +150 · `createSpace` = 밀기 폭) — 한 우변이 셋으로 갈렸다.
104
- * 🔴 **판별자는 「양」이 아니라 「대상」이다** — 우변이 같은 풀이 몇 개인가가 아니라 **문서 전체가 한 우변이었나**. N20 전이 =
105
- * 전체 정렬 무리는 사람이 **3/3 반응** · 부분 무리는 **0/5**. 부분 일치는 도구 문서에서도 우연히 생긴다(저장본 부분 무리 19/69 ↔
106
- * 전체 정렬 0/69). ⇒ 발동은 **배치 전 모든 풀(≥2)의 우변이 같을 때만**이다.
112
+ *
113
+ * 무리 = 배치 전 우변이 허용 오차 안에서 같은 풀들(크기 ≥2). 범위는 두 단계로 넓어졌다:
114
+ * - **ⓐ 문서 전체가 한 우변**(2026-09-28) — N20 전이 = 전체 정렬 무리는 사람이 **3/3 반응** · 부분 무리는 **0/5**.
115
+ * - **ⓓ 부분 무리 — 블랙박스 멤버가 없을 때만**(2026-10-06 · 표본 99) — 증언 = *"끝부분이 어긋난건 보였는데 … 맞추는게
116
+ * 맞아보이긴해"*. N20 = 사람 문서(풀 ≥2) 98 중 **부분 무리 문서 25** · ⓐ 뒤 현행이 깨는 무리는 **부분 무리뿐**(계보 7/23) ·
117
+ * 합성 재고(블랙박스 없는 부분 무리 1,899) = 깨짐 376 → 복원 **312** · 판정 키 · 교차 · 끝점 공유 교차 · 흐름 간선 변화 **0**.
118
+ * 🔴 **블랙박스 멤버가 있는 부분 무리는 뺀다** — 계보 반사실에서 블랙박스 풀이 **570 → 2155** 로 늘고 그 우변에 붙던 메시지가
119
+ * 다시 그려져 끝점 공유 교차가 **1 → 3** 이 됐다(그 회차의 사람은 맞추지 않았다). ⚠ 아래 가드 ②는 그 메시지를 못 막았다 —
120
+ * 이 패스 시점엔 우변에 안 붙어 있었고 뒤 라우팅이 붙인다(추정). ⚠ 판별은 **이 패스 시점**의 모델이다(배치가 빈 풀에 노드를
121
+ * 넣었으면 더는 블랙박스가 아니다).
122
+ * ⚠ 부분 일치는 도구 문서에서도 우연히 생긴다(저장본 19/69) — 그런 무리는 대개 처음부터 같은 폭이라 되돌려도 원래 모양이다
123
+ * (계보 102 재하강에서 기하가 바뀐 문서 = 표본 99 하나).
107
124
  *
108
125
  * **목표 우변 = max(base 우변, 각 풀의 산출 내용 우단 + 150)** — 사람 값이 있는 회차(표본 85 = 2338)를 **정확히** 낸 규칙은
109
126
  * 이것 하나였다(무리 최댓값 · 풀별 base 여백 보존은 둘 다 2473). base 우변 아래로는 **안 줄인다**.
@@ -54,6 +54,11 @@ export declare function forwardThreePointCandidates(edge: GeometryEdge, shapes:
54
54
  * 목표 열은 목표 중심에 도킹 편이 0/±25/±40 — 그 열에 다른 메시지가 이미 중심 도킹해 있으면(같은 방향·같은
55
55
  * 종류라 finding 은 아니지만 두 화살표가 한 선으로 합쳐 보인다 — 사용자도 -19 비껴 놓았다) 편이 후보가 피한다.
56
56
  * 채택은 호출자 몫: 관통 0 을 먼저, 그다음 다른 간선과의 공선 공유가 없는 후보를 고른다.
57
+ *
58
+ * 🔴 B-141. 목표 도킹 열이 **출발 도형 폭 안**이면(같은 열 메시지) 후보를 내지 않는다 — 되돌아오는 세로 구간이
59
+ * 출발 도형을 관통하고, 도킹 편이(최대 ±40)로는 반폭을 못 벗어난다. 그 관통은 호출자의 관통 셈과 판정이 둘 다
60
+ * **자기 끝점을 빼서** 보이지 않아 그대로 채택됐다(표본 93 갈고리 · 계보 102 재하강 = 8회차 10간선 · 전부 이 후보 ·
61
+ * 사람 문서 0/1040 · 사람은 6/7 을 고쳤다). 위 전례는 목표 열 ≠ 출발 열이었다 — 같은 열이면 ㄷ 우회(B-9 ②)로 넘긴다.
57
62
  */
58
63
  export declare function farSideChannelCandidates(edge: GeometryEdge, shapes: readonly GeometryShape[]): GeometryPoint[][];
59
64
  /**
@@ -99,16 +104,75 @@ only?: ReadonlySet<string>): Set<string>;
99
104
  * 읽을 수 없는 중첩이 늘지 않을 때만 채택(🔁 「채택 조건에 대가가 없다」 클래스를 미리 막는다).
100
105
  */
101
106
  export declare function repairIntroducedEdgeCrossings(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforePairs: ReadonlySet<string>, beforeEdgeIds: ReadonlySet<string>): void;
107
+ /**
108
+ * 공유 노드 `node` 쪽 끝을 네 변 중심으로 옮긴 경로 후보(순수). 반대쪽 끝은 **지금 도킹점 · 그 변 그대로** 둔다 —
109
+ * 그 끝은 이 쌍의 교차와 무관하고, 옮기면 그쪽 이웃과의 관계가 새로 생긴다. 반대쪽 도킹점이 변 위가 아니면 후보 없음.
110
+ */
111
+ export declare function redockCandidates(edge: GeometryEdge, node: GeometryShape, shapesById: ReadonlyMap<string, GeometryShape>): GeometryPoint[][];
112
+ /**
113
+ * 🔴 **이 배치가 만든 「끝점을 공유하는」 시퀀스 플로우 교차를 공유 노드의 출입 변을 바꿔 없앤다**(B-146 ⓑ).
114
+ *
115
+ * 표본 98 의 사람 수정 형태 그대로다 — 실패 갈래(게이트웨이 → 오류 노드)가 오류 노드 왼쪽 면으로, 되돌아감(오류 노드 →
116
+ * 상류)이 윗면에서 나가 서로 가로질렀고, 사람은 **실패 갈래를 윗면으로 · 되돌아감을 왼쪽 면에서** 내도록 맞바꿨다
117
+ * (도형 무이동 · 간선 직접 2). 🔴 **한 간선만 옮겨서는 안 풀린다** — 어느 쪽만 옮겨도 다른 쪽과 공선으로 마주 달린다.
118
+ * 그래서 B-90 ⓑ(한 간선씩)와 달리 **쌍을 함께** 고른다.
119
+ *
120
+ * **범위**(B-146 수단 결정) — 시퀀스 × 시퀀스(노드 루프)만 · 블랙박스 풀을 함께 끝점으로 가진 메시지는 보류(사람 기저 0 ·
121
+ * 증언 없음) · 이 배치가 만든 쌍만(`beforeSharedPairs` 밖) · **이 배치가 만든 간선만 움직인다**(B-90 ⓑ 규약 — 기존 간선은
122
+ * 지금 경로 하나만 후보다). 도형은 옮기지 않는다 — 경로로 안 풀리는 형상(g39 = 새 게이트웨이가 기존 우회선 아래)은 남는다.
123
+ *
124
+ * **대가 판정**(B-90 ⓑ 와 같은 자세 — 계산이 곧 판별) — 이 쌍이 더는 교차하지 않고 · 둘이 공선으로 겹치지 않고 · 컨테이너
125
+ * 안이고 · 끝점 공유 교차 총수가 줄고 · **종전 교차 쌍이 새로 생기지 않고** · 문서 판정에 **새 키가 0** 일 때만. 통과한
126
+ * 조합 중 **길이 + 꺾임** 이 가장 작은 것을 고른다(사람 수정본은 도구 산출과 길이가 같고 꺾임만 달랐다).
127
+ */
128
+ export declare function repairIntroducedSharedEndpointCrossings(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeSharedPairs: ReadonlySet<string>, beforeEdgeIds: ReadonlySet<string>): void;
129
+ /**
130
+ * 🔴 **이 배치가 만든 메시지 중 선재 블랙박스 풀로 가고 짝 열이 풀 가로 범위 밖인 것을 짝 쪽 가장자리에 다시 도킹한다**(B-150).
131
+ * `beforeEdgeIds` · `beforeShapeIds` = 하강 전 간선 · 도형 id(선재 풀 판별 · 새 메시지 판별).
132
+ */
133
+ export declare function redockIntroducedBlackboxMessages(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeEdgeIds: ReadonlySet<string>, beforeShapeIds: ReadonlySet<string>): void;
102
134
  /**
103
135
  * 풀 횡단 메시지플로우의 6점 경로 계획(순수). `null` = 이 간선은 대상이 아니거나(중간 풀 없음 ·
104
136
  * 끝점 해소 불가) 더 나은 열이 없다.
105
137
  */
106
138
  export declare function planCrossPoolRoute(edge: GeometryEdge, scene: GeometryScene): GeometryPoint[] | null;
139
+ /**
140
+ * ── 승객 간선의 base 경로 평행이동 (B-133 ⓒ) ──
141
+ *
142
+ * 하강 전에 있던 간선 중 **양 끝 도형이 같은 델타로 움직였는데 경로는 다시 깔린 것**을 base 경로 + 델타로
143
+ * 되돌린다. 한 짝의 끝점이 **서로 다른 단계**에서 옮겨지면(밀기 `createSpace` 는 노드 쪽만 · B-75 추종은 풀
144
+ * 쪽만 · 짝 동반은 짝 쪽만) 단계마다 bpmn-js `connection.layout` 이 사람이 그린 경로를 버리고 다시 깐다. 순
145
+ * 변위가 같으므로 평행이동이면 사람 경로가 그대로 산다. 실측(표본 103 · 사람 문서 + 증언 *"선이 껶여있는
146
+ * 부분이 걸렸어"*) = 쌓인 송신 노드의 **옆면 출발 3점**이 위 노드를 관통하는 2점으로 다시 깔리고, 우회 보정이
147
+ * 그것을 **8점 지그재그**로 감싸 이웃 메시지와 130px 겹쳤다(`edge-overlap-unrelated`).
148
+ *
149
+ * 안전 성질 2(선재 경로 불가침)의 **복원판**이다 — 선재 경로를 이 배치가 이미 지운 자리에서, 사람이 그린 것을
150
+ * 되살린다. 자리 = 도형을 옮기는 패스(lower · 풀 · 그룹) **전부 뒤**, 경로 보정 **앞** — 되살린 경로도 뒤
151
+ * 보정의 입력이 된다.
152
+ *
153
+ * 조건(전부) — ① 하강 전에 있던 **시퀀스 · 메시지 흐름**(연관은 뺀다 — N20 에서 base 의 끊긴 주석 연관만 되살려
154
+ * 판정을 늘렸고 이득은 0 이었다) ② 이 배치가 끝점을 바꾸지 않았다(재배선 · 끝점 id) ③ 양 끝 도형 **크기 불변** ·
155
+ * 이동 델타가 **같고 0 이 아니다** ④ 지금 경로 ≠ base + 델타 ⑤ 지금 경로가 이미 **관통 없는 2점 축 직선이면
156
+ * 둔다**(N20 — 다시 깐 직선을 사람의 꺾인 경로로 되돌리면 2점 메시지만 잃었다) ⑥ base + 델타 경로가 도형을
157
+ * 관통하지 않는다(관통하면 뒤 우회 보정에 맡긴다).
158
+ *
159
+ * N20(합성 재고 = 풀 안 시퀀스 전량 끼우기 3 op · 사람 문서 183 → 3,884 · 저장본 109 → 4,466) — 사람: 배치가 만든
160
+ * 판정 **−50** · 새 판정 4(전부 교환) · 교차 −25 · 2점 메시지 무변 · 순악화 **0** / 저장본: **−58** · 교차 감소 ·
161
+ * 2점 메시지 무변 · 순악화 **0**(교차 +1 4건은 종류 다름 중첩 해소와 교환).
162
+ */
163
+ export declare function restorePassengerEdgeRoutes(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, sceneBefore: GeometryScene, reroutedEdgeIds: ReadonlySet<string>): void;
107
164
  /**
108
165
  * 이 배치가 **새로 만든** 메시지플로우 중 풀을 횡단하는 것을 재경로한다(하강 직후, undo 에피소드
109
166
  * 안). `beforeEdgeIds` = 하강 전 간선 id — 그 밖의 것만 대상(안전 성질 2: 선재 경로 불가침).
110
167
  */
111
168
  export declare function rerouteIntroducedCrossPoolEdges(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeEdgeIds: ReadonlySet<string>): void;
169
+ /**
170
+ * 🔴 **이 배치가 만들었거나 경로를 바꾼 메시지가 관계없는 풀 안을 가로로 지나면 그 가로를 끝점의 풀 안으로 옮긴다**(B-151) — 끝점 풀 안이
171
+ * 막히면 풀 밖 채널(풀 사이 틈 · 더미 바깥)로(B-151 ⓐ) · 채택이 없던 메시지는 한 번 더 본다.
172
+ * 선재 메시지는 base 보다 그 길이가 `UNRELATED_POOL_RUN_MIN` 이상 **는** 것만 — base 의 가로는 사람이 둔 것이다.
173
+ * `beforeEdgeIds` = 하강 전 간선 id · `sceneBefore` = 하강 전 장면(선재 경로 · 풀).
174
+ */
175
+ export declare function repairIntroducedUnrelatedPoolRuns(services: Pick<ResolverServices, 'elementRegistry' | 'modeling'>, beforeEdgeIds: ReadonlySet<string>, sceneBefore: GeometryScene): void;
112
176
  /** 안쪽 구간(첫·끝 도킹 구간 제외)이 컨테이너 경계선과 나란히 이 거리 안에서 달리면 "경계에 붙었다"고 본다. */
113
177
  export declare const BOUNDARY_CLEARANCE = 30;
114
178
  /** 웨이포인트의 **안쪽 구간**(도킹 구간 제외)이 어느 컨테이너의 경계선과 나란히 `BOUNDARY_CLEARANCE` 안에서 겹쳐 달리는가. */
@@ -1,4 +1,13 @@
1
+ import { BpmnDocumentLocales } from '../../adapter/i18n';
1
2
  import { SymbolicOp } from '../symbolicOp';
2
3
  import { DocumentIndex } from './documentIndex';
3
4
  import { ResolveIssue } from './issues';
4
- export declare function validateOps(ops: readonly SymbolicOp[], index: DocumentIndex): ResolveIssue[];
5
+ /** 검증 옵션 — 문서 밖에서만 알 수 있는 입력. */
6
+ export interface ValidateOpsOptions {
7
+ /**
8
+ * 그룹의 문서 언어 설정(B-4 ④) — 있으면 다국어 그룹이다(문구 = `names`/`texts` 로 문서 언어 전부 · 평문 거부).
9
+ * 없으면 기능이 꺼진 그룹이다(평문만 · 맵 거부). 그룹 설정은 문서에 없으므로 호스트가 공급한다.
10
+ */
11
+ i18n?: BpmnDocumentLocales;
12
+ }
13
+ export declare function validateOps(ops: readonly SymbolicOp[], index: DocumentIndex, options?: ValidateOpsOptions): ResolveIssue[];
@@ -1,5 +1,5 @@
1
1
  import { BwlAfterColumn, BwlBand, BwlPlacement, BwlPoolOrder } from '../core/hints';
2
- import { IrAssociation, IrDataAssociation, IrDataStore, AddableFlowNodeType, IrEventDefinition, IrFlowCondition, IrGroup, IrLane, IrMessageFlow, IrNode, IrParticipant, IrSequenceFlow, IrTextAnnotation } from '../core/ir';
2
+ import { IrLocaleTexts, IrAssociation, IrDataAssociation, IrDataStore, AddableFlowNodeType, IrEventDefinition, IrFlowCondition, IrGroup, IrLane, IrMessageFlow, IrNode, IrParticipant, IrSequenceFlow, IrTextAnnotation } from '../core/ir';
3
3
  /** LLM 이 산출하는 단일 심볼릭 op. `kind` 로 판별한다. */
4
4
  export type SymbolicOp =
5
5
  /** 풀 추가. `order` = 풀 세로 순서 힌트(생략 시 op 순서가 곧 배치 순서). */
@@ -57,6 +57,7 @@ export type SymbolicOp =
57
57
  kind: 'participant.update';
58
58
  id: string;
59
59
  name?: string | null;
60
+ names?: IrLocaleTexts;
60
61
  lanes?: IrLane[];
61
62
  }
62
63
  /** `lane` = 레인 재배정(이동). `type` = 태스크/이벤트 타입 전환(bpmn-js replace 하강 — 외래 확장 속성 유실 가능성은 불변식 2 가 완충). */
@@ -67,6 +68,7 @@ export type SymbolicOp =
67
68
  kind: 'node.update';
68
69
  id: string;
69
70
  name?: string | null;
71
+ names?: IrLocaleTexts;
70
72
  lane?: string;
71
73
  type?: AddableFlowNodeType;
72
74
  eventDefinition?: IrEventDefinition;
@@ -85,6 +87,7 @@ export type SymbolicOp =
85
87
  kind: 'sequenceFlow.update';
86
88
  id: string;
87
89
  name?: string | null;
90
+ names?: IrLocaleTexts;
88
91
  condition?: IrFlowCondition | null;
89
92
  source?: string;
90
93
  target?: string;
@@ -92,16 +95,19 @@ export type SymbolicOp =
92
95
  kind: 'messageFlow.update';
93
96
  id: string;
94
97
  name?: string | null;
98
+ names?: IrLocaleTexts;
95
99
  } | {
96
100
  kind: 'dataStore.update';
97
101
  id: string;
98
102
  name?: string | null;
103
+ names?: IrLocaleTexts;
99
104
  }
100
105
  /** `text` 는 주석의 본질이라 제거 불가(주석 삭제 = element.remove). `placement: null` = 힌트 제거(휴리스틱 복귀). */
101
106
  | {
102
107
  kind: 'annotation.update';
103
108
  id: string;
104
109
  text?: string;
110
+ texts?: IrLocaleTexts;
105
111
  placement?: BwlPlacement | null;
106
112
  }
107
113
  /** `members` 는 전체 재선언(생략 = 보존) — resolver 가 바운즈를 재산출한다. */
@@ -109,8 +115,20 @@ export type SymbolicOp =
109
115
  kind: 'group.update';
110
116
  id: string;
111
117
  name?: string | null;
118
+ names?: IrLocaleTexts;
112
119
  members?: string[];
113
120
  }
121
+ /**
122
+ * **확인만** — 그 요소의 `locale` 칸에 걸린 확인 요청(`bwl:reviewRequest`)을 문구를 바꾸지 않고 해소한다.
123
+ * 다른 언어가 고쳐졌지만 이 칸의 번역은 여전히 맞을 때(오타 수준 수정) 쓴다. 번역을 고쳐야 하면 이것이 아니라
124
+ * 그 요소의 `*.update` 에 `names`/`texts` 를 준다 — 문구가 **실제로 바뀐 칸**의 표식만 해소된다.
125
+ * 다국어 그룹에서만 받는다 · 그 칸에 표식이 없으면 거부한다(읽은 뒤 사람이 이미 정리했을 수 있다).
126
+ */
127
+ | {
128
+ kind: 'translation.confirm';
129
+ id: string;
130
+ locale: string;
131
+ }
114
132
  /** 요소 제거(카테고리 무관 — 도형·연결 균일, 부착 연결은 연쇄 제거). */
115
133
  | {
116
134
  kind: 'element.remove';
@@ -120,5 +138,5 @@ export type SymbolicOp =
120
138
  * 심볼릭 op 종류의 닫힌 집합(단일 출처). resolver·JSON Schema(`schema.ts`)가 공유한다.
121
139
  * 아래 컴파일타임 단언이 이 튜플과 `SymbolicOp['kind']` 의 일치를 강제 — 한쪽만 늘리면 타입 에러.
122
140
  */
123
- export declare const SYMBOLIC_OP_KINDS: readonly ["participant.add", "participant.update", "node.add", "node.update", "sequenceFlow.add", "sequenceFlow.update", "messageFlow.add", "messageFlow.update", "dataStore.add", "dataStore.update", "dataAssociation.add", "association.add", "annotation.add", "annotation.update", "group.add", "group.update", "element.remove"];
141
+ export declare const SYMBOLIC_OP_KINDS: readonly ["participant.add", "participant.update", "node.add", "node.update", "sequenceFlow.add", "sequenceFlow.update", "messageFlow.add", "messageFlow.update", "dataStore.add", "dataStore.update", "dataAssociation.add", "association.add", "annotation.add", "annotation.update", "group.add", "group.update", "translation.confirm", "element.remove"];
124
142
  export type SymbolicOpKind = (typeof SYMBOLIC_OP_KINDS)[number];