isthmus-cli 0.4.1 → 0.6.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.
- package/README.ko.md +64 -22
- package/README.md +70 -26
- package/Skills/isthmus/SKILL.md +93 -4
- package/dist/cli/check-command.d.ts +2 -25
- package/dist/cli/check-command.js +27 -181
- package/dist/cli/check-command.js.map +1 -1
- package/dist/cli/command-support.d.ts +48 -0
- package/dist/cli/command-support.js +144 -0
- package/dist/cli/command-support.js.map +1 -0
- package/dist/cli/diff-command.d.ts +1 -1
- package/dist/cli/diff-command.js +1 -1
- package/dist/cli/diff-command.js.map +1 -1
- package/dist/cli/graph-command.d.ts +1 -1
- package/dist/cli/graph-command.js +15 -14
- package/dist/cli/graph-command.js.map +1 -1
- package/dist/cli/impact-command.d.ts +5 -0
- package/dist/cli/impact-command.js +111 -0
- package/dist/cli/impact-command.js.map +1 -0
- package/dist/cli/main.js +35 -13
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/parse-arguments.d.ts +18 -0
- package/dist/cli/parse-arguments.js +48 -0
- package/dist/cli/parse-arguments.js.map +1 -0
- package/dist/cli/preflight-command.d.ts +5 -0
- package/dist/cli/preflight-command.js +83 -0
- package/dist/cli/preflight-command.js.map +1 -0
- package/dist/cli/query-command.d.ts +1 -1
- package/dist/cli/query-command.js +30 -10
- package/dist/cli/query-command.js.map +1 -1
- package/dist/cli/retentions-command.d.ts +1 -1
- package/dist/cli/retentions-command.js +11 -10
- package/dist/cli/retentions-command.js.map +1 -1
- package/dist/cli/runtime-command.d.ts +5 -0
- package/dist/cli/runtime-command.js +42 -0
- package/dist/cli/runtime-command.js.map +1 -0
- package/dist/cli/runtime-json-reader.d.ts +12 -0
- package/dist/cli/runtime-json-reader.js +37 -0
- package/dist/cli/runtime-json-reader.js.map +1 -0
- package/dist/exchange/impact-selection.d.ts +13 -0
- package/dist/exchange/impact-selection.js +35 -0
- package/dist/exchange/impact-selection.js.map +1 -0
- package/dist/exchange/kartograph-impact.d.ts +4 -0
- package/dist/exchange/kartograph-impact.js +169 -0
- package/dist/exchange/kartograph-impact.js.map +1 -0
- package/dist/exchange/messages.d.ts +51 -0
- package/dist/exchange/messages.js +126 -0
- package/dist/exchange/messages.js.map +1 -0
- package/dist/exchange/parse.d.ts +8 -0
- package/dist/exchange/parse.js +12 -8
- package/dist/exchange/parse.js.map +1 -1
- package/dist/exchange/preflight-context.d.ts +71 -0
- package/dist/exchange/preflight-context.js +317 -0
- package/dist/exchange/preflight-context.js.map +1 -0
- package/dist/exchange/producer-impact.d.ts +17 -0
- package/dist/exchange/producer-impact.js +226 -0
- package/dist/exchange/producer-impact.js.map +1 -0
- package/dist/exchange/runtime.d.ts +69 -0
- package/dist/exchange/runtime.js +160 -0
- package/dist/exchange/runtime.js.map +1 -0
- package/dist/join/message-address.d.ts +7 -0
- package/dist/join/message-address.js +37 -0
- package/dist/join/message-address.js.map +1 -0
- package/dist/join/messages.d.ts +23 -0
- package/dist/join/messages.js +80 -0
- package/dist/join/messages.js.map +1 -0
- package/dist/report/diff.d.ts +40 -33
- package/dist/report/diff.js +8 -4
- package/dist/report/diff.js.map +1 -1
- package/dist/report/impact.d.ts +62 -0
- package/dist/report/impact.js +168 -0
- package/dist/report/impact.js.map +1 -0
- package/dist/report/preflight-runtime.d.ts +42 -0
- package/dist/report/preflight-runtime.js +193 -0
- package/dist/report/preflight-runtime.js.map +1 -0
- package/dist/report/preflight-view.d.ts +146 -0
- package/dist/report/preflight-view.js +189 -0
- package/dist/report/preflight-view.js.map +1 -0
- package/dist/report/preflight.d.ts +104 -0
- package/dist/report/preflight.js +296 -0
- package/dist/report/preflight.js.map +1 -0
- package/dist/report/query.js +9 -2
- package/dist/report/query.js.map +1 -1
- package/dist/report/runtime-impact.d.ts +38 -0
- package/dist/report/runtime-impact.js +94 -0
- package/dist/report/runtime-impact.js.map +1 -0
- package/dist/report/runtime.d.ts +54 -0
- package/dist/report/runtime.js +137 -0
- package/dist/report/runtime.js.map +1 -0
- package/dist/report/sarif.d.ts +9 -2
- package/dist/report/sarif.js +4 -10
- package/dist/report/sarif.js.map +1 -1
- package/dist/report/sorted-json.d.ts +1 -1
- package/dist/report/sorted-json.js +2 -2
- package/dist/report/sorted-json.js.map +1 -1
- package/docs/BRIDGE-MESSAGES.md +102 -0
- package/docs/GRAPH-EXCHANGE.md +255 -0
- package/docs/IMPACT.md +96 -0
- package/docs/PREFLIGHT.md +295 -0
- package/docs/RUNTIME.md +172 -0
- package/docs/TOOLCHAIN.md +101 -0
- package/package.json +11 -2
- package/scripts/build-preflight-toolchain.mjs +186 -0
- package/scripts/capture-preflight.mjs +392 -0
- package/scripts/run-child.mjs +16 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# 브리지 사실 교환 형식 (버전 1)
|
|
2
|
+
|
|
3
|
+
isthmus 소유의 추가 입력/보고 계약은 [변경 사전 점검](IMPACT.md),
|
|
4
|
+
[언어 간 전이 분석과 수집](PREFLIGHT.md),
|
|
5
|
+
[런타임 통신 검증](RUNTIME.md)에 있다. 이들은 기존 bridge-facts v1 생산자 필드를
|
|
6
|
+
변경하지 않는다. 런타임에서 지원하는 transport를 정적 producer 지원으로 해석하지 않는다.
|
|
7
|
+
|
|
8
|
+
개발 중인 [BasicMessageChannel v2](BRIDGE-MESSAGES.md)는 별도 transport 문서다.
|
|
9
|
+
v1 전용 명령은 이를 거부하며, 선택적 preflight context.messages에서만 소비한다.
|
|
10
|
+
|
|
11
|
+
cartograph · kartograph · dartograph · isthmus 의 JS/TS 추출기가 **내보내고**, isthmus 가 **읽는** 형식. 이 문서가 바뀌면 네 저장소가 같이 바뀐다. 버전 1은 `experiments/phase-0/`의 Dart ↔ Swift 코퍼스를 양방향으로 조인해 검증했다.
|
|
12
|
+
|
|
13
|
+
## 원칙
|
|
14
|
+
|
|
15
|
+
- 각 도구는 **자기 언어에서 본 사실만** 낸다. 판정하지 않는다
|
|
16
|
+
- 위치는 항상 파일 · 줄 · 열. isthmus 의 모든 보고가 양쪽 위치를 가리켜야 한다
|
|
17
|
+
- 리터럴이 아닌 이름은 `dynamic: true` 로 표시하고 **버리지 않는다.** 한계를 세는 데 필요하다
|
|
18
|
+
- 키 순서는 정렬, 파일은 diff 가능해야 한다 (cartograph `GraphDocument` 와 같은 이유)
|
|
19
|
+
|
|
20
|
+
## 문서
|
|
21
|
+
|
|
22
|
+
```jsonc
|
|
23
|
+
{
|
|
24
|
+
"format": "bridge-facts",
|
|
25
|
+
"version": 1,
|
|
26
|
+
"tool": { "name": "dartograph", "version": "0.1.0" },
|
|
27
|
+
"generatedAt": "2026-09-04T12:00:00Z", // 신선도 판단용
|
|
28
|
+
"platform": "dart" | "swift" | "kotlin" | "js",
|
|
29
|
+
"target": "flutter" | "react-native" | "capacitor" | null, // 브리지 메커니즘
|
|
30
|
+
"project": "/abs/path", // POSIX realpath로 정규화한 절대 경로
|
|
31
|
+
"facts": [ Fact, ... ],
|
|
32
|
+
"limitations": [ "dynamic-channel-names: 3 channel constructors use a non-literal name", ... ]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 선택적 limitation 스코프 (v1 확장)
|
|
37
|
+
|
|
38
|
+
기존 `limitations: string[]`는 유지한다. 생산자는 그 중 특정 항목의 **공백 전체**를
|
|
39
|
+
포함하는 채널 집합을 증명할 수 있을 때만 선택적 `limitationScopes`를 추가한다.
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
"limitations": ["opaque-handler-bodies: 1 handler body could not be inspected"],
|
|
43
|
+
"limitationScopes": [{"limitationIndex": 0, "channels": ["dev.example/camera"]}]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- `limitationIndex`는 같은 문서의 `limitations` 배열에 대한 0부터 시작하는 인덱스다.
|
|
47
|
+
항목별로 하나만 허용한다. 같은 접두사의 다른 항목과 다른 문서의 공백을 덮어쓰지 않는다.
|
|
48
|
+
- `channels`는 제어 문자가 없는 비어 있지 않은 채널 문자열의 비어 있지 않은 배열이다.
|
|
49
|
+
글롭·대소문자 접기·부분 문자열 매칭을 하지 않는다. 중복은 제거하고 문자열 순으로 정규화한다.
|
|
50
|
+
- 채널 이름을 일부 발견한 것만으로 스코프를 만들지 않는다. 해당 한계가 가릴 수 있는 모든
|
|
51
|
+
채널을 포함하는 **보수적 상한**이어야 한다. 동적 이름·미해석 위임·읽기 실패 때문에
|
|
52
|
+
상한을 증명할 수 없으면 그 항목의 스코프를 생략한다. ObjC 파일에서 채널 리터럴을
|
|
53
|
+
몇 개 읽었다는 것만으로 `objective-c-sources`의 범위를 좁히지 않는다.
|
|
54
|
+
- 스코프가 없는 항목은 기존 target 범위 전체에 적용한다. 스코프 있는 항목과 공존하면
|
|
55
|
+
범위 없는 공백이 우선하며, 다른 target과 호출 측 한계는 기존 규칙대로 처리한다.
|
|
56
|
+
`target: null`은 target을 추측하지 않고 모든 target의 해당 채널에 적용한다.
|
|
57
|
+
- 잘못된 인덱스, 중복 인덱스, 빈 채널 집합, 잘못된 타입은 입력 오류로 거부한다. 잘못된
|
|
58
|
+
스코프를 빈 공백으로 읽고 error를 만들지 않는다. 문서당 최대 1,000 스코프, 정규화 전
|
|
59
|
+
채널 원소 합계 최대 10,000개다. 파일 위치는 채널 집합의 대체물이 아니다.
|
|
60
|
+
- 문자열 끝의 `[channels: …]`는 문장일 뿐 파싱하지 않는다. JSON 문자열 인코딩이 쉼표·
|
|
61
|
+
대괄호·따옴표 이스케이프를 맡는다. 같은 채널 안의 플랫폼 조건은 이 범위로 구분하지 않는다.
|
|
62
|
+
- 옛 v1 소비자는 모르는 필드를 버리고 기존 문자열을 target 전체로 적용한다. 새 소비자는
|
|
63
|
+
조인 결과의 해당 `JoinLimitation.channels`에 범위를 보존하며 check/query/graph/diff에
|
|
64
|
+
전달한다. 스코프 배열 자체가 비었으면 추가 범위가 없다는 뜻이며 기존 전체 적용이다.
|
|
65
|
+
범위만 바뀌어도 diff에서 한계 변화로 보인다. isthmus의 직접 계수에는 선택적
|
|
66
|
+
`origin: "consumer"`를 붙이고, 이 필드는 생산 문서에서 복사하지 않는다. `unjoined-*`를 생산자가 신고해
|
|
67
|
+
소비자의 자체 계수를 덮어쓰는 것은 여전히 허용하지 않는다.
|
|
68
|
+
|
|
69
|
+
## Fact
|
|
70
|
+
|
|
71
|
+
공통 필드:
|
|
72
|
+
|
|
73
|
+
```jsonc
|
|
74
|
+
{
|
|
75
|
+
"kind": "channel-create" | "channel-register" | "method-invoke" | "method-handle"
|
|
76
|
+
| "module-export" | "module-import" | "component-export" | "component-require",
|
|
77
|
+
"channel": "com.example/camera", // 귀속할 수 없으면 null. dynamic 이면 원문 표현식
|
|
78
|
+
"method": "takePhoto", // method-* 에만
|
|
79
|
+
"dynamic": false,
|
|
80
|
+
"location": { "path": "lib/camera.dart", "line": 42, "column": 5 },
|
|
81
|
+
"symbol": { // 이 사실을 담고 있는 선언 (있으면)
|
|
82
|
+
"qualifiedName": "CameraPlugin.register",
|
|
83
|
+
"usr": "s:…" // 생산 도구의 안정 식별자. Phase 0 구문 실험에서는 생략 가능
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
선택적 `sourceLanguage: "objective-c"`는 `platform: "swift"` 문서에 담긴 `.m`/`.mm`의
|
|
89
|
+
**Objective-C 구현 사실**을 구분한다. 실제 Clang 인덱스에서 확인한 `c:` USR이 있으면
|
|
90
|
+
`usr`까지 함께 싣는다. 인덱스가 없거나 선언을 유일하게 확인하지 못하면 `usr`를 생략하고
|
|
91
|
+
구문이 아는 `qualifiedName`만 둘 수 있다 — Swift의 `missing-handler-usrs`와 같은
|
|
92
|
+
대칭이다. 합성 안정 식별자(SCIP 문법 등)를 지어 진짜 신원처럼 싣지 않는다.
|
|
93
|
+
`usr`가 있는데 `c:`로 시작하지 않으면 입력 오류다.
|
|
94
|
+
그 외 값·플랫폼·확장자 조합은 입력 오류다.
|
|
95
|
+
필드가 없으면 기존 플랫폼 의미를 유지한다. 위치 확장자만으로 Objective-C라고 추측하지 않는다.
|
|
96
|
+
`sourceLanguage`는 Swift 분석 그래프 밖의 Objective-C 구현을 선언하는 생산자의 자가 선언 필드이며, 소비자는 이를 신뢰한다(생산자 신뢰 전제). 생산자가 이 라벨을 오선언해 Swift 보존 fail-closed를 우회하는 것은 소비자의 정적 분석 범위 밖이다.
|
|
97
|
+
|
|
98
|
+
`method-handle`의 `symbol`은 문자열 `case` 자체가 아니라 그것을 감싸는 타입·함수 선언이다. Swift 클로저에는 USR이 없으므로 `qualifiedName`은 `CameraPlugin.register`처럼 감싸는 선언을 가리키고, `location`은 실제 `case` 문자열을 가리킨다. cartograph의 생산 구현은 인덱스와 결합해 `usr`까지 채워야 한다. 구문 실험처럼 `usr`을 채우지 못하면 `missing-handler-usrs`를 `limitations`에 싣는다.
|
|
99
|
+
|
|
100
|
+
`location.path`는 프로젝트 루트 기준 상대 경로다. 절대 경로, `..` 상위 이동, 제어 문자를 넣지 않는다.
|
|
101
|
+
`location.line`과 `location.column`은 1부터 시작하며, `column`은 해당 줄의 UTF-8 바이트
|
|
102
|
+
오프셋에 1을 더한 값이다. 생산자는 언어 런타임의 UTF-16 또는 Unicode scalar 열을 그대로
|
|
103
|
+
내보내지 않는다.
|
|
104
|
+
소비자는 이 조건을 어긴 문서를 거부해 로컬 경로 노출과 후속 출력 문법 오염을 막는다.
|
|
105
|
+
프로젝트 경로와 채널·메서드·심볼 이름에도 제어 문자를 넣지 않는다.
|
|
106
|
+
NEL(U+0085)과 Unicode 줄·문단 구분자(U+2028/U+2029)도 허용하지 않는다.
|
|
107
|
+
식별자(프로젝트 경로·위치 경로·채널·메서드·심볼 이름·도구 이름·스코프 채널)에 유효하지 않은 유니코드 코드 포인트인 짝 없는 서러게이트(lone surrogate, U+D800~U+DFFF)도 허용하지 않으며(소비자는 `toWellFormed()`로 검증), 이를 어긴 문서는 URI 인코딩 등 하류 출력 파이프라인의 오작동을 막기 위해 파싱 단계에서 거부한다.
|
|
108
|
+
`limitations` 문자열은 원인 설명이라 문장을 자유롭게 쓸 수 있고 소비자가 내용을
|
|
109
|
+
검증하지 않는다. 소비자의 텍스트 출력(DOT·Mermaid 주석 등)에는 제어 문자를
|
|
110
|
+
제거해 넣고, JSON 출력은 인코딩이 이스케이프를 맡는다.
|
|
111
|
+
`generatedAt`은 timezone이 명시된 ISO 8601 날짜·시각이어야 한다. 생산자는 입력 offset을
|
|
112
|
+
UTC로 변환하고 밀리초 세 자리의 `YYYY-MM-DDTHH:mm:ss.SSSZ` 형식으로 정규화한다.
|
|
113
|
+
소비자는 버전 1에 정의되지 않은 추가 필드를 검증 경계에서
|
|
114
|
+
제거하고, 위치의 줄·열은 1 이상의 안전한 정수만 허용한다.
|
|
115
|
+
|
|
116
|
+
`channel: null`은 `method-handle`에서만 허용하며, "채널이 없다"가 아니라 생산자가
|
|
117
|
+
핸들러를 어느 채널에 귀속할지 **모른다**는 뜻이다. 소비자는 이 사실을 조인하지 않고,
|
|
118
|
+
호출 없는 핸들러 같은 불일치에도 포함하지 않는다. 생산자는 그 수와 원인을 정확히
|
|
119
|
+
`unattributed-method-handles:`로 시작하는 limitation으로 알려야 하며, 없으면 소비자는
|
|
120
|
+
문서를 거부한다.
|
|
121
|
+
|
|
122
|
+
### 종류별 의미
|
|
123
|
+
|
|
124
|
+
| kind | 누가 내는가 | 뜻 |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `channel-create` | Dart / JS | 호출하는 쪽이 채널 객체를 만들었다 |
|
|
127
|
+
| `channel-register` | Swift / Kotlin | 받는 쪽이 채널에 핸들러를 달았다 (`setMethodCallHandler`). 위치도 생성자가 아니라 이 호출을 가리킨다 |
|
|
128
|
+
| `method-invoke` | Dart / JS | `invokeMethod('m')` 호출 |
|
|
129
|
+
| `method-handle` | Swift / Kotlin | 핸들러 안에서 `case "m":` 또는 동등한 분기 |
|
|
130
|
+
| `module-export` | Swift / Kotlin | RN `RCT_EXPORT_MODULE(Name)`, `@ReactModule(name=)` |
|
|
131
|
+
| `module-import` | JS | `NativeModules.Name`, `TurboModuleRegistry.get('Name')` |
|
|
132
|
+
| `component-export` | Swift / Kotlin | RN `RCT_EXPORT_VIEW_PROPERTY` 등 뷰 매니저 |
|
|
133
|
+
| `component-require` | JS | `requireNativeComponent('Name')` |
|
|
134
|
+
|
|
135
|
+
RN 의 메서드는 `method-invoke`(JS: `NativeModules.Name.method()`) / `method-handle`(네이티브: `RCT_EXPORT_METHOD(method:)`, `@ReactMethod fun method`) 로 같은 종류를 쓴다. `channel` 자리에 모듈 이름이 들어간다.
|
|
136
|
+
|
|
137
|
+
`module-*`과 `component-*`는 버전 1에 예약되어 있지만 isthmus 0.1에서는 아직 조인하지
|
|
138
|
+
않는다. 0.1 소비자는 거짓 정상 결과를 막기 위해 이 네 종류를 입력 오류로 거부하며,
|
|
139
|
+
실제 조인은 RN 지원과 함께 0.2에서 제공한다.
|
|
140
|
+
|
|
141
|
+
## 조인 규칙 (isthmus 가 적용)
|
|
142
|
+
|
|
143
|
+
- `channel-create` ↔ `channel-register`: `channel` 이 같다. 플랫폼별로 따로 맞춘다 (Swift 와 Kotlin 이 각각 등록하는 것이 정상)
|
|
144
|
+
- 생성 없는 `channel-register`는 호출 측 사용을 찾지 못한 경고로 보존한다
|
|
145
|
+
- `method-invoke` ↔ `method-handle`: `(channel, method)` 가 같다
|
|
146
|
+
- `module-import` ↔ `module-export`: 0.2에서 `channel`(모듈 이름)로 조인할 예정
|
|
147
|
+
- `dynamic: true`이거나 `channel: null`인 사실은 조인하지 않고 `limitations`로 센다. 조인할 수 없다는 이유로 불일치라고 판정하지 않는다.
|
|
148
|
+
세는 주체는 소비자다. isthmus는 조인에서 제외한 dynamic 사실을 직접 세어 자신을 출처(`tool: "isthmus"`)로 밝힌 limitation으로 내보내며, 같은 위치의 중복 사실은 한 번만 센다. 생산자의 `dynamic-*` limitation은 원인을 설명하는 추가 정보이지 소비자가 신뢰의 근거로 삼는 값이 아니다. `channel: null` 핸들러도 같다. 생산자의 `unattributed-method-handles:` 신고가 없으면 문서를 거부하지만, 신고한 개수는 검증하지 않고 소비자가 실제 사실 수를 다시 센다
|
|
149
|
+
- 수신 측이 스스로 신고한 분석 공백은 심각도에 반영한다. 소비자는 `objective-c-sources:`·`shadowed-flutter-method-channel:`(등록과 핸들러를 모두 가림), `opaque-handler-bodies:`(핸들러를 가림)를 수신 측 플랫폼 문서에서 발견하면 "핸들러 없는 호출"과 "등록 없는 채널 생성"을 error가 아니라 판정 불가(`-unverified` 경고)로 보고한다. 소비자가 직접 센 `unjoined-dynamic-methods`·`unjoined-unattributed-handlers`는 핸들러를, `unjoined-dynamic-channels`는 등록을 가리는 공백으로 본다. 알려진 접두사만 인정한다. `unjoined-` 접두사는 isthmus가 직접 세고 `origin: "consumer"`를 붙인 한계에만 유효하다. 이 출처는 입력 문서에서 복사하지 않는다. 생산자가 tool 이름을 isthmus로 적거나 같은 접두사를 차용해도 자체 계수의 근거가 되지 않는다. 모르는 한계를 공백으로 넓게 해석하면 진짜 불일치가 경고로 묻힌다. 호출 측 플랫폼의 한계는 네이티브 코드를 가리지 않으므로 심각도를 바꾸지 않는다.
|
|
150
|
+
이 접두사들은 계약이다. 생산자는 문구를 바꿀 때 접두사를 유지하고, 새 공백 종류를 추가하면 소비자의 목록도 함께 갱신한다. 목록이 닫혀 있으므로 갱신 전까지는 그 공백이 error로 보고된다(안전한 방향).
|
|
151
|
+
완화 단위는 진단의 target이다. 사실은 target별로만 조인되므로 target을 가진 수신 문서가 신고한 공백은 그 target 진단의 심각도만 낮춘다. 사실이 없는(`target: null`) 수신 문서의 공백은 어느 target의 분석을 가리는지 귀속 근거가 없어 모든 target에 적용한다. 같은 target에 귀속된 수신 문서가 사실과 함께 공존해도 마찬가지다. 수신 문서 여러 개가 소스 트리를 나누어 가졌을 수 있어, 귀속 없는 문서가 본 소스가 해당 target의 핸들러를 가릴 가능성을 배제할 수 없기 때문이다. mixed-targets 문서의 한계도 선언한 target을 신뢰할 수 없어 귀속 없이 남긴다. 선택적 limitationScopes가 있으면 같은 target 안에서도 그 채널에만 적용한다. 범위가 없으면 기존 전체 적용을 유지한다. 같은 이유로 `objective-c-sources:`처럼 소비자가 직접 셀 수 없는 공백은 생산자의 신고를 그대로 믿는다. 과다 신고는 진짜 불일치를 경고로 묻고, 과소 신고는 거짓 error를 남긴다
|
|
152
|
+
- 위치는 증거이지 조인 키가 아니다. 같은 `(channel, method)` 사실이 여러 위치에 있어도 존재 여부는 키 집합으로 판단하고, 위치는 모두 증거로 보존한다
|
|
153
|
+
- 한 번의 조인에 넣는 모든 문서는 정확히 같은 `project` 문자열을 가져야 한다. 다른 프로젝트의 같은 채널 이름을 연결하지 않기 위해 불일치는 입력 오류로 거부한다
|
|
154
|
+
- 생산자는 `project`를 내보내기 전에 **POSIX realpath**(`realpath(3)`)로 정규화한다. 결과는 항상 symlink·`..`·중복 슬래시가 접힌 절대 경로다. 프로젝트 경로를 해결할 수 없거나 결과가 이 계약이 금지하는 제어 문자(NEL과 U+2028/U+2029 포함)를 포함하면 생산자는 문서를 내보내지 않고 실패한다 — 소비자에게 거부될 문서를 내보내지 않는다. 버전 1은 POSIX를 가정하며, Windows 정규화(드라이브 문자 대소문자, `\\?\` 접두사)는 Windows 지원 시 별도 합의한다. kartograph의 목표 기준은 JVM `Path.toRealPath()`다
|
|
155
|
+
- `project`는 생산자가 선언한 **조인 루트**다. 모든 사실의 `location.path`는 이 루트 기준 상대 경로이며, 모노레포에서 분석 루트와 조인 루트가 다르면 생산자가 위치를 조인 루트 기준으로 재기준화해 내보낸다. 생산 후에 문서의 `project`만 손으로 고쳐 쓰는 것은 조인 루트 선언이 아니다 — `location.path`가 다른 트리를 가리키게 되어 계약 위반이다. 선언 방법은 생산자 옵션이고 우선순위는 명시 옵션 > 자동 감지 > 분석 루트다. dartograph(0.5.0): `--project <shared-root>`는 스캔 범위를 위치 인자로 둔 채 `project`와 `location.path`를 공유 루트 기준으로 재기준화하고, 공유 루트는 realpath 정규화 후 package root를 포함하거나 동일해야 하며(위반은 사용 오류), pub workspace 자동 감지는 스캔 루트 pubspec의 `resolution: workspace` 선언 시 `workspace:` 키를 가진 가장 가까운 조상 pubspec 디렉터리(Melos의 워크스페이스 루트 정의와 동일)를 realpath로 채택한다. 자동 감지 실패(조상 루트 부재·pubspec 파싱 불가)는 분석 루트로 폴백하되 `pub-workspace-root-not-found`·`pub-workspace-pubspec-unparsed` limitation을 실어 조인 기준 어긋남을 조용히 넘기지 않는다 — 둘은 호출 측 한계라 isthmus는 심각도를 바꾸지 않고 그대로 전달한다. cartograph의 `--project`는 분석 루트 자체이므로 realpath 정규화 규칙만으로 이 정의를 만족한다. 조인 가능 여부는 소비자 설치본으로 왕복 실측했다(dartograph#38·#52: 모노레포 2패키지의 `project` 문자열 일치와 isthmus check 조인 성공, 옵션 없는 구행동 문서의 거부까지 양방향)
|
|
156
|
+
- 소비자는 정확한 문자열 일치를 유지하며 경로를 스스로 해결하지 않는다(isthmus는 JSON 파일만 읽는다). 소비자는 정규화 이행 여부를 검증할 수 없다 — 검증 가능한 것은 문서 간 `project` 문자열 일치뿐이고, 정규화 위반은 오직 조인 입력 오류로만 관측된다. realpath가 수렴시키는 것은 symlink·`..`·슬래시 축뿐이다. Unicode NFC/NFD 표기 차이, 대소문자 무시 파일시스템의 표기 차이, 마운트 별칭은 같은 디렉터리에 다른 문자열로 남고 불일치로 거부된다(안전하지만 디버깅이 필요하다). 근거: 같은 정규화가 없으면 macOS의 `/tmp`↔`/private/tmp`처럼 같은 디렉터리가 도구마다 다른 문자열이 된다(isthmus에서 재현). cartograph는 Foundation의 `resolvingSymlinksInPath().standardizedFileURL.path`가 `/private/tmp`을 `/tmp`으로 출력함을 실측하고 주입된 POSIX realpath를 채택했고(cartograph#73, 0.10.1 — 실측 입출력 쌍은 그 PR 본문 참조), dartograph의 `Directory.resolveSymbolicLinks()`는 POSIX에서 같은 기준을 만족한다
|
|
157
|
+
- 한 번의 조인 입력에는 호출 측 플랫폼(dart·js) 문서와 수신 측 플랫폼(swift·kotlin) 문서가 각각 최소 하나 있어야 한다. 한쪽만 있는 입력은 한쪽 관찰을 경계 불일치로 오독할 수 있으므로 소비자는 입력 오류로 거부한다. 사실이 없는 문서도 해당 플랫폼이 분석됐다는 근거로 인정한다
|
|
158
|
+
|
|
159
|
+
생산자는 채널 생성자와 핸들러 등록 사이의 변수 참조를 따라 채널 이름을 `channel-register`에 옮긴다. `FlutterMethodChannel` 객체를 만들기만 하고 핸들러를 달지 않은 코드는 등록 사실이 아니다.
|
|
160
|
+
|
|
161
|
+
### `target` 호환 규칙
|
|
162
|
+
|
|
163
|
+
- 사실이 없을 때만 문서의 `target`은 `null`이다
|
|
164
|
+
- 사실이 하나 이상이고 한 브리지 메커니즘만 담으면 그 값을 쓴다
|
|
165
|
+
- 버전 1에는 사실별 `target`이 없다. 한 Swift 프로젝트에 Flutter와 React Native 사실이
|
|
166
|
+
함께 있으면 생산자는 결정적인 대표값을 쓰고 정확히 `mixed-targets:`로 시작하는
|
|
167
|
+
limitation을 반드시 추가한다
|
|
168
|
+
- 소비자는 `mixed-targets` 문서에서 사실별 메커니즘을 복원할 수 없으므로 조인을 보류한다. 생산자는 위의 정확한 표기를 써야 하며, 소비자는 대소문자·앞 공백·콜론 누락처럼 명백한 변형도 fail-closed로 보류한다. 단, `non-mixed-targets`나 `mixed-targets-like`처럼 낱말 내부에 포함된 표기는 다른 의미의 산문이므로 보류 근거로 삼지 않고 단어 경계와 대소문자 무시(`(?<![\w-])mixed-targets(?![\w-])/i`)로 판정한다. CLI 명령은 빈 정상 결과를 내지 않고 도구 실패(종료 코드 2)를 반환한다. 안전한 혼합 프로젝트 지원은 문서를 target별로 나누거나 다음 형식 버전에 사실별 target을 추가한 뒤 제공한다
|
|
169
|
+
|
|
170
|
+
소비자는 `platform`과 fact 역할도 함께 검증한다. Dart/JS는 호출 측 종류만,
|
|
171
|
+
Swift/Kotlin은 수신 측 종류만 생산할 수 있다.
|
|
172
|
+
|
|
173
|
+
### 입력 자원 상한
|
|
174
|
+
|
|
175
|
+
- 한 명령은 최대 256개 교환 문서를 받는다
|
|
176
|
+
- 한 문서는 최대 100,000개 fact를 담는다
|
|
177
|
+
- CLI는 파일 하나당 UTF-16 문자열 길이 16Mi, 전체 64Mi를 넘으면 파싱 전에 거부한다
|
|
178
|
+
- 경계 그래프의 Cartesian 간선은 최대 100,000개다
|
|
179
|
+
|
|
180
|
+
## 되돌려 주는 형식: 외부 보존 근거
|
|
181
|
+
|
|
182
|
+
isthmus `retentions --for <tool>` 의 출력. 자매 도구의 `--external-retentions <path>` 가 읽는다.
|
|
183
|
+
|
|
184
|
+
```jsonc
|
|
185
|
+
{
|
|
186
|
+
"format": "external-retentions",
|
|
187
|
+
"version": 0,
|
|
188
|
+
"producedBy": { "name": "isthmus", "version": "x.y.z" },
|
|
189
|
+
"generatedAt": "…",
|
|
190
|
+
"retentions": [
|
|
191
|
+
{
|
|
192
|
+
"symbol": { "usr": "s:…", "qualifiedName": "CameraPlugin.takePhoto" },
|
|
193
|
+
"reason": "bridge",
|
|
194
|
+
"evidence": {
|
|
195
|
+
"channel": "com.example/camera",
|
|
196
|
+
"method": "takePhoto",
|
|
197
|
+
"caller": { "platform": "dart", "path": "lib/camera.dart", "line": 42 },
|
|
198
|
+
"callers": [
|
|
199
|
+
{ "platform": "dart", "path": "lib/camera.dart", "line": 42 },
|
|
200
|
+
{ "platform": "dart", "path": "lib/widget.dart", "line": 7 }
|
|
201
|
+
],
|
|
202
|
+
"callersOmitted": 3
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
자매 도구는 이것을 `RetentionReason.externalBridge` 로 매핑하고, `--explain` 에서 `evidence` 를 그대로 문장으로 만든다.
|
|
210
|
+
|
|
211
|
+
- `caller` 은 대표 호출 위치다. 결정적 순서(플랫폼·경로·줄·열)의 첫 호출이며
|
|
212
|
+
v0 초안부터 있던 필드라 옛 소비자가 계속 읽는다.
|
|
213
|
+
- 선택 `callers` 는 이 근거의 **전체** 호출 위치(대표 포함)를 같은 결정적 순서로
|
|
214
|
+
실는다. 호출이 둘 이상일 때만 두어, 호출이 하나인 근거는 기존 문서와 바이트가
|
|
215
|
+
같다. 근거당 상한은 100개이며, 문서 전체의 호출 위치 총상한은 1,000,000개다.
|
|
216
|
+
- 상한을 넘은 호출은 조용히 버리지 않고 선택 `callersOmitted` (비음수 정수,
|
|
217
|
+
0이면 생략)로 밝힌다. `omittedObjectiveCHandlers` 와 같은 계수 공개 원칙이다.
|
|
218
|
+
- 소비 도구는 모르는 필드를 무시한다(Swift `JSONDecoder` 의 기본 동작). 그래서
|
|
219
|
+
이 확장은 생산자(isthmus)를 먼저 배포해도 안전하고, 소비 도구가 `callers` 를
|
|
220
|
+
문장으로 펼치는 것은 별도 구현 사항이다.
|
|
221
|
+
|
|
222
|
+
cartograph의 보존 문서는 **Swift 그래프 선언**을 대상으로 완전해야 한다. 명시적
|
|
223
|
+
`sourceLanguage: "objective-c"` 구현은 조인·진단·query의 증거로 남기지만 Swift 보존 대상은
|
|
224
|
+
아니므로 그 목록에 넣지 않는다. Kotlin 핸들러를 cartograph 보존에서 제외하는 것과 같은
|
|
225
|
+
범위 구분이며, symbol 없는 Swift 핸들러를 조용히 버리는 예외가 아니다. ObjC가 Swift로
|
|
226
|
+
위임하는 관계는 별도 증거가 필요하고, ObjC 핸들러 이름으로 Swift USR을 만들지 않는다.
|
|
227
|
+
`omittedObjectiveCHandlers`(선택적 비음수 정수)에 목록에서 제외한 매치 ObjC 핸들러 수를
|
|
228
|
+
(target, channel, method, source location)별로 센다. 0이면 키를 생략한다. cartograph는
|
|
229
|
+
이 수를 외부 보존 근거의 한계로 알려 빈 목록을 Swift와 ObjC 전체의 보존 결과로 오인하지 않게 한다.
|
|
230
|
+
이 필드를 모르는 옛 isthmus는 ObjC 매치도 심볼 없는 Swift로 보아 retentions에서 실패한다.
|
|
231
|
+
따라서 ObjC 사실을 내는 생산자보다 이 확장을 지원하는 소비자를 먼저 배포한다.
|
|
232
|
+
|
|
233
|
+
이 문서는 대상 범위 안에서 부분적으로 만들지 않는다. 소비 도구가 읽을 수 있는 수신 측 문서가 입력에 없거나, 호출자가 있는데도 `symbol`이 없어 근거로 바꿀 수 없는 매치 핸들러가 있으면 isthmus는 일부만 담은 목록 대신 도구 실패(종료 코드 2)로 끝낸다. 근거가 빠진 목록은 소비자에게 살아 있는 핸들러를 미사용으로 보이게 하기 때문이다. 문서 전체 호출 위치 총상한(1,000,000개)을 초과한 경우에도 부분 근거를 내지 않고 입력을 좁히도록 안내하며 종료 코드 2로 실패한다.
|
|
234
|
+
|
|
235
|
+
## 자매 도구가 해야 할 일 (선행 작업)
|
|
236
|
+
|
|
237
|
+
| 도구 | 명령 | 낼 것 | 읽을 것 |
|
|
238
|
+
|---|---|---|---|
|
|
239
|
+
| cartograph | `bridges --format json` | Swift 의 `FlutterMethodChannel(name:)`, `setMethodCallHandler`, `case "…"`, `RCT_EXPORT_*`, `@objc(…)` | `--external-retentions` |
|
|
240
|
+
| dartograph | `bridges --format json` | `MethodChannel(…)`, `invokeMethod(…)`, Pigeon 산출물 | (없음 — Dart 쪽이 부르는 쪽) |
|
|
241
|
+
| kartograph | `bridges --format json` | `MethodChannel(…)`, `setMethodCallHandler`, `when (call.method)`, `@ReactModule`, `@ReactMethod` | `--external-retentions` |
|
|
242
|
+
| isthmus 내장 | `extract-js` | `NativeModules.*`, `TurboModuleRegistry.get`, `requireNativeComponent` | — |
|
|
243
|
+
|
|
244
|
+
**cartograph가 첫 번째 생산 구현이다.** PR #11에서 SwiftSyntax 스캐너와 `bridges --format json`이 버전 1로 구현됐다.
|
|
245
|
+
|
|
246
|
+
cartograph의 버전 1 구현은 `symbol.usr`을 붙이기 위해 인덱스 스토어를 요구한다. 인덱스가 없으면 불완전한 문서를 내보내지 않고 도구 실패(종료 코드 2)로 끝난다. 이는 문서 형식의 limitation이 아니라 생산 명령의 선행 조건이다.
|
|
247
|
+
|
|
248
|
+
## Phase 0 결정
|
|
249
|
+
|
|
250
|
+
- **Swift `case` 귀속**: 감싸는 타입·함수의 `qualifiedName`과, 생산 구현이 가진 안정 식별자(`usr`)를 `symbol`에 넣는다. 사실의 `location`은 `case` 문자열 위치다. Phase 0 SwiftSyntax 실험은 USR을 만들 수 없어 그 수를 `missing-handler-usrs`로 보고한다
|
|
251
|
+
- **Pigeon · Turbo Modules codegen**: 버전 1에는 별도 `codegen-resolved` 종류나 플래그를 추가하지 않는다. 생성 코드의 리터럴도 같은 채널·메서드 사실이고 조인 규칙이 같기 때문이다. 버전 1은 생성 여부를 계약에 싣지 않으며, 필요해지면 조인 키를 바꾸지 않는 선택 필드로 추가한다. 소비자는 경로만 보고 사용자 작성 코드라고 가정하지 않는다
|
|
252
|
+
- **한 단계 상수 추적**: `const kChannel = '…'`와 Swift `static let`처럼 같은 파일의 문자열 상수 한 단계는 정적 사실로 낸다. 그 이상이거나 보간된 표현식은 원문과 `dynamic: true`로 보존한다
|
|
253
|
+
- **구문 해석 한계**: Dart의 해석하지 못한 `invokeMethod` receiver는 `unresolved-receiver-invocations:`, Swift의 named-function handler는 `opaque-handler-bodies:` limitation으로 센다. 로컬 선언이 import된 `FlutterMethodChannel`을 가리면 `shadowed-flutter-method-channel:`로 알리고 사실 생성을 보류한다
|
|
254
|
+
- **Swift 조건부 컴파일**: Flutter를 import한 파일에 `#if`가 있으면 활성 구성을 추측하지 않고 compiler-indexed 추출이 필요하다고 실패한다
|
|
255
|
+
- **버전 1 승격**: `expected/dart.json`과 `expected/swift.json`을 실제 추출기로 만들고, 채널 1개·메서드 1개 연결, 핸들러 없는 호출 1개, 호출 없는 핸들러 2개를 `expected/join.json`으로 대조해 충족했다
|
package/docs/IMPACT.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# 변경 전 브리지 영향 점검
|
|
2
|
+
|
|
3
|
+
`impact`는 현재 개발 소스에 추가된 명령이다. npm 0.5.0 발행본에는 없으며, checkout에서
|
|
4
|
+
`npm ci && npm run build` 후 `node dist/cli/main.js impact ...`로 실행한다.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
isthmus impact --file ios/Runner/CameraPlugin.swift dart.json swift.json
|
|
8
|
+
isthmus impact --symbol 'CameraPlugin.handle' dart.json swift.json --compact
|
|
9
|
+
isthmus impact --changes changes.json dart.json swift.json --strict
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
파일·심볼·변경 목록 중 한 가지 방식만 선택한다. 파일은 producer의 `location.path`와
|
|
13
|
+
같은 프로젝트 상대 경로다. 심볼은 `qualifiedName` 또는 USR과 정확히 일치하며,
|
|
14
|
+
같은 이름이 여러 위치에 있으면 모두 보고한다. 선택 값에 경로 정규화·접두 매칭·rename
|
|
15
|
+
추론을 적용하지 않는다. 심볼의 전역 유일성을 이름만으로 주장하지 않는다.
|
|
16
|
+
|
|
17
|
+
## 변경 목록 입력
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"format": "isthmus-changes",
|
|
22
|
+
"version": 1,
|
|
23
|
+
"files": ["ios/Runner/CameraPlugin.swift", "lib/camera.dart"],
|
|
24
|
+
"symbols": ["CameraPlugin.handle"]
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
두 목록은 합집합이며 하나는 생략 가능하다. 정규화 전 합계 1~10,000개를 허용한다.
|
|
29
|
+
파일은 bridge-facts의 상대 경로 조건, 심볼은 안전한 비어 있지 않은 문자열 조건을 따른다.
|
|
30
|
+
중복 제거 후 문자열 순으로 정렬한다. 알 수 없는 필드는 버린다. 입력 파일당 16 MiB,
|
|
31
|
+
변경 목록과 bridge-facts 전체 합계 64 MiB UTF-16 텍스트 길이 예산을 적용한다.
|
|
32
|
+
|
|
33
|
+
## 결과와 해석
|
|
34
|
+
|
|
35
|
+
`isthmus-impact` v1은 다음을 보존한다.
|
|
36
|
+
|
|
37
|
+
- `project`: 증거 상대 경로의 기준. CLI 실행 디렉터리와 동일하다고 추측하지 않는다.
|
|
38
|
+
파일 존재를 확인하지 않은 snapshot 위치를 실제 로컬 파일 링크로 만들지 않는다.
|
|
39
|
+
|
|
40
|
+
- `selection` / `unmatchedSelectors`: 요청 목록과 관찰하지 못한 선택. 삭제된 코드는
|
|
41
|
+
삭제 전 snapshot으로 조회한다. 새 snapshot에서 없다는 것은 영향이 없다는 증거가 아니다.
|
|
42
|
+
- `selectedFacts`: 실제 선택된 위치·플랫폼·target·종류·심볼·동적 여부. 동적/미귀속 사실도
|
|
43
|
+
이 목록과 `summary.unresolvedSelectedFacts`에 남는다.
|
|
44
|
+
- `channels`: 관련 생성·등록 위치와 범위를 넓힌 `reason`.
|
|
45
|
+
- `methods`: 메서드별 Dart 호출과 Swift 핸들러. 생성·등록 변경은 그 채널의 모든 메서드로
|
|
46
|
+
넓힌다(`channel-wiring`). 메서드 변경은 해당 논리 키만 선택한다(`method`).
|
|
47
|
+
- `reviewFiles`: 선택 지점, 관련 호출·핸들러·배선 파일의 중복 없는 목록.
|
|
48
|
+
- `issues`: 기존 check 정책으로 계산한 관련 진단. 다른 채널의 진단은 섞지 않는다.
|
|
49
|
+
- `limitations` / `relevantLimitations`: 전체 한계와 선택에 관련된 한계. 스코프 없는 한계는
|
|
50
|
+
해당 target 전체에 적용한다. 미관찰·미해석 선택은 범위를 좁힐 수 없어 전체 한계를 보존한다.
|
|
51
|
+
- `inputs`: 생산 도구·버전·플랫폼·target·생성 시각. 파일 입력 순서와 무관하게 정렬한다.
|
|
52
|
+
|
|
53
|
+
`summary.observedFacts`는 전체 입력 fact 수, `selectedFacts`는 중복 제거한 선택 근거 수다.
|
|
54
|
+
`--compact`는 JSON 공백만 줄이며 근거·한계를 생략하지 않는다.
|
|
55
|
+
|
|
56
|
+
| 코드 | 의미 |
|
|
57
|
+
|---|---|
|
|
58
|
+
| 0 | 보고서 생성 성공. `unobserved`도 기본 모드에서는 보고한다. |
|
|
59
|
+
| 1 | `--strict`에서 관련 error·미검증 진단·관련 분석 한계·미해석 선택·미관찰 선택이 남음. |
|
|
60
|
+
| 2 | 읽기·JSON·교환 계약·입력 예산·조인 보류 등 실행 실패. 부분 보고서 없음. |
|
|
61
|
+
| 64 | 인수 또는 직접 입력한 파일/심볼 선택 오류. |
|
|
62
|
+
|
|
63
|
+
이 명령의 strict는 check strict보다 분석 공백에 엄격하다. 기존 check/diff 동작은 유지한다.
|
|
64
|
+
|
|
65
|
+
## 런타임 관찰 연결
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
isthmus impact --file lib/dynamic.dart dart.json swift.json \
|
|
69
|
+
--runtime runtime.json --revision current-source-revision --compact
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
두 옵션은 함께 지정한다. runtime의 project는 정적 입력과 같아야 하고 revision은 호출자가
|
|
73
|
+
선언한 분석 revision과 같아야 한다. 정적 bridge-facts v1에는 revision 필드가 없으므로,
|
|
74
|
+
CI/호출자가 같은 소스에서 사실을 생산했다는 문맥을 제공해야 한다. 소비자가 이를 추측하지 않는다.
|
|
75
|
+
|
|
76
|
+
선택된 호출 파일·정확한 fact 위치 또는 선택된 정적 주소와 관련된 런타임 이벤트를 모은다.
|
|
77
|
+
동적 Dart 호출에서 관찰된 구체적인 MethodChannel 주소로 Swift 핸들러 후보를 찾으면
|
|
78
|
+
`reason: runtime-observation`으로 관련 메서드와 검토 파일을 넓힌다. 정적 invocation 사실을
|
|
79
|
+
합성하지 않고 `runtime` 필드에 별도 근거를 둔다. 정적 미해석 수와 기존 진단은 그대로 남는다.
|
|
80
|
+
|
|
81
|
+
런타임 run의 platform이 ios/macos이고 transport가 MethodChannel인 경우에만 현재 Swift
|
|
82
|
+
정적 핸들러 후보를 찾는다. native 심볼이나 엔진이 실제 실행됐다고 확정하지 않는다.
|
|
83
|
+
BasicMessageChannel과 다른 OS는 `unsupported`, 정적 후보가 없으면 `unobserved`다.
|
|
84
|
+
revision이 오래되면 이벤트를 연결하지 않고 `stale`로 보존한다.
|
|
85
|
+
|
|
86
|
+
주소·인스턴스별 호출을 묶고 호출 위치는 20개까지만 보여 주며 `callersOmitted`를 기록한다.
|
|
87
|
+
`reviewFiles`는 표시 생략과 무관하게 관찰한 전체 관련 파일을 유지한다. 런타임 실패·대기·
|
|
88
|
+
중단·유실·오래된 기록·관련 관찰 부재·정적 후보 공백도 strict 실패 조건이다.
|
|
89
|
+
|
|
90
|
+
## 현재 경계
|
|
91
|
+
|
|
92
|
+
`scope: "bridge"`, `complete: false`는 항상 명시한다. 현재 기능은 브리지에 직접 등장한
|
|
93
|
+
코드의 변경 범위를 찾는다. 언어 내부 helper→handler→Dart 화면으로 이어지는 전이 경로,
|
|
94
|
+
실제 엔진·등록 수명과 테스트 시나리오의 포괄성은 아직 검증하지 않는다. 런타임 관찰은
|
|
95
|
+
위 방식으로 후보를 넓히는 근거이며, 전체 의존성의 완전성이나 실제 핸들러 실행 증명은 아니다.
|
|
96
|
+
이들은 [전체 목표](COMPETITIVENESS.md)의 남은 구현이며 이 명령의 존재로 완료 처리하지 않는다.
|