isthmus-cli 0.5.0 → 0.7.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.
Files changed (146) hide show
  1. package/README.ko.md +170 -45
  2. package/README.md +178 -36
  3. package/Skills/isthmus/SKILL.md +104 -5
  4. package/compatibility.json +10 -0
  5. package/dist/cli/check-command.d.ts +2 -2
  6. package/dist/cli/check-command.js +22 -9
  7. package/dist/cli/check-command.js.map +1 -1
  8. package/dist/cli/command-support.d.ts +20 -2
  9. package/dist/cli/command-support.js +38 -9
  10. package/dist/cli/command-support.js.map +1 -1
  11. package/dist/cli/diff-command.js +4 -3
  12. package/dist/cli/diff-command.js.map +1 -1
  13. package/dist/cli/doctor-command.d.ts +11 -0
  14. package/dist/cli/doctor-command.js +128 -0
  15. package/dist/cli/doctor-command.js.map +1 -0
  16. package/dist/cli/extract-js-command.d.ts +33 -0
  17. package/dist/cli/extract-js-command.js +233 -0
  18. package/dist/cli/extract-js-command.js.map +1 -0
  19. package/dist/cli/graph-command.js +13 -6
  20. package/dist/cli/graph-command.js.map +1 -1
  21. package/dist/cli/impact-command.d.ts +5 -0
  22. package/dist/cli/impact-command.js +111 -0
  23. package/dist/cli/impact-command.js.map +1 -0
  24. package/dist/cli/init-command.d.ts +11 -0
  25. package/dist/cli/init-command.js +115 -0
  26. package/dist/cli/init-command.js.map +1 -0
  27. package/dist/cli/main.js +67 -1
  28. package/dist/cli/main.js.map +1 -1
  29. package/dist/cli/mcp-server.d.ts +16 -0
  30. package/dist/cli/mcp-server.js +534 -0
  31. package/dist/cli/mcp-server.js.map +1 -0
  32. package/dist/cli/preflight-command.d.ts +5 -0
  33. package/dist/cli/preflight-command.js +83 -0
  34. package/dist/cli/preflight-command.js.map +1 -0
  35. package/dist/cli/query-command.js +13 -6
  36. package/dist/cli/query-command.js.map +1 -1
  37. package/dist/cli/retentions-command.js +14 -7
  38. package/dist/cli/retentions-command.js.map +1 -1
  39. package/dist/cli/runtime-command.d.ts +5 -0
  40. package/dist/cli/runtime-command.js +42 -0
  41. package/dist/cli/runtime-command.js.map +1 -0
  42. package/dist/cli/runtime-json-reader.d.ts +12 -0
  43. package/dist/cli/runtime-json-reader.js +37 -0
  44. package/dist/cli/runtime-json-reader.js.map +1 -0
  45. package/dist/cli/serve-command.d.ts +13 -0
  46. package/dist/cli/serve-command.js +43 -0
  47. package/dist/cli/serve-command.js.map +1 -0
  48. package/dist/exchange/capture-config.d.ts +18 -0
  49. package/dist/exchange/capture-config.js +100 -0
  50. package/dist/exchange/capture-config.js.map +1 -0
  51. package/dist/exchange/impact-selection.d.ts +13 -0
  52. package/dist/exchange/impact-selection.js +35 -0
  53. package/dist/exchange/impact-selection.js.map +1 -0
  54. package/dist/exchange/kartograph-impact.d.ts +4 -0
  55. package/dist/exchange/kartograph-impact.js +169 -0
  56. package/dist/exchange/kartograph-impact.js.map +1 -0
  57. package/dist/exchange/messages.d.ts +36 -0
  58. package/dist/exchange/messages.js +96 -0
  59. package/dist/exchange/messages.js.map +1 -0
  60. package/dist/exchange/parse.d.ts +69 -0
  61. package/dist/exchange/parse.js +127 -20
  62. package/dist/exchange/parse.js.map +1 -1
  63. package/dist/exchange/preflight-context.d.ts +71 -0
  64. package/dist/exchange/preflight-context.js +317 -0
  65. package/dist/exchange/preflight-context.js.map +1 -0
  66. package/dist/exchange/producer-impact.d.ts +17 -0
  67. package/dist/exchange/producer-impact.js +226 -0
  68. package/dist/exchange/producer-impact.js.map +1 -0
  69. package/dist/exchange/runtime.d.ts +69 -0
  70. package/dist/exchange/runtime.js +160 -0
  71. package/dist/exchange/runtime.js.map +1 -0
  72. package/dist/extract/js-document.d.ts +22 -0
  73. package/dist/extract/js-document.js +230 -0
  74. package/dist/extract/js-document.js.map +1 -0
  75. package/dist/extract/js-scan.d.ts +93 -0
  76. package/dist/extract/js-scan.js +1205 -0
  77. package/dist/extract/js-scan.js.map +1 -0
  78. package/dist/extract/lexer.d.ts +26 -0
  79. package/dist/extract/lexer.js +316 -0
  80. package/dist/extract/lexer.js.map +1 -0
  81. package/dist/join/join.d.ts +66 -1
  82. package/dist/join/join.js +171 -1
  83. package/dist/join/join.js.map +1 -1
  84. package/dist/join/message-address.d.ts +7 -0
  85. package/dist/join/message-address.js +37 -0
  86. package/dist/join/message-address.js.map +1 -0
  87. package/dist/join/messages.d.ts +26 -0
  88. package/dist/join/messages.js +87 -0
  89. package/dist/join/messages.js.map +1 -0
  90. package/dist/report/check-report.d.ts +20 -3
  91. package/dist/report/check-report.js +240 -8
  92. package/dist/report/check-report.js.map +1 -1
  93. package/dist/report/codequality.d.ts +45 -0
  94. package/dist/report/codequality.js +74 -0
  95. package/dist/report/codequality.js.map +1 -0
  96. package/dist/report/diff.d.ts +25 -3
  97. package/dist/report/diff.js +91 -10
  98. package/dist/report/diff.js.map +1 -1
  99. package/dist/report/graph.d.ts +10 -4
  100. package/dist/report/graph.js +60 -6
  101. package/dist/report/graph.js.map +1 -1
  102. package/dist/report/impact.d.ts +62 -0
  103. package/dist/report/impact.js +168 -0
  104. package/dist/report/impact.js.map +1 -0
  105. package/dist/report/preflight-runtime.d.ts +42 -0
  106. package/dist/report/preflight-runtime.js +193 -0
  107. package/dist/report/preflight-runtime.js.map +1 -0
  108. package/dist/report/preflight-view.d.ts +146 -0
  109. package/dist/report/preflight-view.js +216 -0
  110. package/dist/report/preflight-view.js.map +1 -0
  111. package/dist/report/preflight.d.ts +104 -0
  112. package/dist/report/preflight.js +331 -0
  113. package/dist/report/preflight.js.map +1 -0
  114. package/dist/report/query.d.ts +11 -4
  115. package/dist/report/query.js +84 -17
  116. package/dist/report/query.js.map +1 -1
  117. package/dist/report/retentions.d.ts +17 -5
  118. package/dist/report/retentions.js +85 -8
  119. package/dist/report/retentions.js.map +1 -1
  120. package/dist/report/rules.d.ts +16 -0
  121. package/dist/report/rules.js +30 -0
  122. package/dist/report/rules.js.map +1 -0
  123. package/dist/report/runtime-impact.d.ts +38 -0
  124. package/dist/report/runtime-impact.js +94 -0
  125. package/dist/report/runtime-impact.js.map +1 -0
  126. package/dist/report/runtime.d.ts +54 -0
  127. package/dist/report/runtime.js +137 -0
  128. package/dist/report/runtime.js.map +1 -0
  129. package/dist/report/sarif.d.ts +2 -8
  130. package/dist/report/sarif.js +2 -10
  131. package/dist/report/sarif.js.map +1 -1
  132. package/dist/report/sorted-json.d.ts +1 -1
  133. package/dist/report/sorted-json.js +2 -2
  134. package/dist/report/sorted-json.js.map +1 -1
  135. package/docs/BRIDGE-EVENTS.md +87 -0
  136. package/docs/BRIDGE-MESSAGES.md +117 -0
  137. package/docs/GRAPH-EXCHANGE.md +382 -0
  138. package/docs/IMPACT.md +96 -0
  139. package/docs/MCP.md +62 -0
  140. package/docs/PREFLIGHT.md +311 -0
  141. package/docs/RUNTIME.md +172 -0
  142. package/docs/TOOLCHAIN.md +101 -0
  143. package/package.json +14 -2
  144. package/scripts/build-preflight-toolchain.mjs +186 -0
  145. package/scripts/capture-preflight.mjs +380 -0
  146. package/scripts/run-child.mjs +17 -0
package/README.ko.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # isthmus
2
2
 
3
3
  크로스플랫폼 앱에서 **언어 경계를 넘는 호출**을 그래프로 잇는 도구.
4
- [cartograph](https://github.com/ictechgy/cartograph)(Swift) · kartograph(Kotlin, 예정) ·
4
+ [cartograph](https://github.com/ictechgy/cartograph)(Swift) · [kartograph](https://github.com/ictechgy/kartograph)(Kotlin) ·
5
5
  [dartograph](https://github.com/ictechgy/dartograph)(Dart)가 각자 그린 지도를 하나로 붙인다.
6
6
 
7
7
  [English](README.md)
@@ -22,43 +22,61 @@ React Native나 Flutter 앱의 네이티브 코드는 JS/Dart가 **문자열 이
22
22
 
23
23
  isthmus는 각 언어 도구가 내보낸 **브리지 사실**(채널 이름 · 메서드 이름 · 등록 지점 ·
24
24
  호출 지점)을 문자열 키로 조인해서, 경계를 넘는 간선을 만들고 위 세 가지를 답한다.
25
- 그리고 그 결과를 cartograph/kartograph에 **보존 근거로 돌려준다** — "Swift
25
+ 그리고 그 결과를 cartograph에 **보존 근거로 돌려준다** — "Swift
26
26
  `CameraHandler.takePhoto`는 `lib/camera.dart:42`가 채널 `com.example/camera`로 부르므로
27
27
  보존".
28
28
 
29
29
  ## 상태
30
30
 
31
- **0.5.0.** CLI 사용성을 통일하고 진단 가독성을 높였다: 모든 명령에서 옵션이 입력
32
- 파일 앞뒤 어디에 와도 동작하고(`-`로 시작하는 경로는 `--` 구분자 뒤에 쓴다),
33
- `-h`/`--help`는 임의 위치에서 이기며 `help <command>`가 명령별 사용법을 낸다.
34
- 조인이 보류되면 어느 명령이든 관찰한 fact 수·문서 수를 함께 알리고
35
- (check·graph·query·retentions·diff), query의 `notFound`·`ambiguous`는 원인 줄을
36
- stderr에 출력한다. query `qualifiedName`은 `:`까지 이스케이프해 `:`와 `#` 기준
37
- 분해가 가역이다. 내부로는 공유 CLI 인프라를 check-command에서 분리하고 SARIF 지문
38
- 해싱을 주입해 report 계층이 Node 내장 모듈에 의존하지 않으며, diff 문서 형태를
39
- `BridgeDiffDocument`로 명시했다. 출력 문서·종료 코드·필드는 불변이고 0.4.1과 값이
40
- 달라지는 것은 이름에 `:`를 포함하는 채널·메서드의 `qualifiedName`뿐이다.
41
- 0.4.0의 추가 사항 GitHub code scanning용 check SARIF 2.1.0 렌더링(`check --format sarif`,
42
- additive, 소스 이동에 강한 논리 지문), 브리지가 없는 프로젝트와 아무것도
43
- 관찰하지 못한 실행을 구분하는 check summary 관찰량(`observedFacts`·`observedLimitations`),
44
- 다중 호출자 보존 근거(`evidence.callers` — 근거당 상한·명시적 `callersOmitted` 계수,
45
- 단일 호출자는 기존과 바이트 동일), 인덱스 없이 빌드된 Objective-C 핸들러의 usr 없는
46
- `qualifiedName` 신원 0.3.0의 핵심 계약·명령은 그대로 유지된다. 외부 입력·
47
- 혼합 target·그래프 크기·Dart/Swift Phase 0 추출 경계는 fail-closed를 유지하고,
48
- 조인하지 못한 사실은 소비자 쪽에서 다시 세며 근거를 만들지 못한 보존 대상은 조용히
49
- 사라지는 대신 실패로 보고한다. 수신 측이 신고한 분석 공백은 불일치가 아니라 판정
50
- 불가로 보고되고 공백 완화는 target을 넘지 않는다. 다음 단계는 실제 Flutter 앱
51
- 도그푸딩과 React Native 지원이다.
52
-
53
- 정식 producer는 cartograph 0.5.3 이상과 dartograph 0.1.1 이상이다. 두 도구의 실제 출력과
54
- 공개 battery 플러그인의 Swift USR·Dart 호출 근거 왕복을 검증했다.
31
+ **0.6.0**은 Flutter Dart Swift/Kotlin 변경 사전 점검, MethodChannel·Pigeon/BasicMessageChannel
32
+ 사실, 명시한 런타임 시나리오 대조와 내용 기반 수집 캐시를 지원한다. `impact
33
+ --file`/`--symbol`/`--changes`(정보 손실 없는 `--compact` JSON과 분석 공백도 실패시키는
34
+ `--strict` 포함), producer 전이 영향 경로를 다루는 `preflight <context.json>`의
35
+ `--summary`/`--explain`, `verify-runtime --expectations`가 추가됐다. 실제 macOS·Android
36
+ 검증 앱에서 공개 플러그인 API를 실행했다. 설정과 측정 범위는 [사전 점검](docs/PREFLIGHT.md),
37
+ [런타임 검증](docs/RUNTIME.md), [고정 소스 구축](docs/TOOLCHAIN.md)을 참조한다.
38
+
39
+ npm 발행본은 **0.7.0**이다. 공개 호환 producer 세트는 cartograph **0.18.0**,
40
+ kartograph **0.10.2**, dartograph **0.14.0**이다 설치 명령·고정 예제·CI 예시는
41
+ [호환 버전](docs/COMPATIBILITY.md)을 참조한다. MethodChannel 조인과 보존 근거 왕복은
42
+ cartograph 0.5.3 이상·dartograph 0.1.1 이상부터 지원하며, 이전 공개 세트
43
+ (cartograph 0.15.1·dartograph 0.10.0·isthmus 0.6.0)로 왕복을 다시 확인했다.
44
+ React Native 모듈·컴포넌트 사실(`module-import`↔`module-export`,
45
+ `component-require`↔`component-export`) `react-native` target 안에서 이름으로
46
+ 조인된다. 선택적 `mechanism` 필드가 core와 Expo 해석 경로를 구분한다. Expo의
47
+ `requireNativeModule` 계열 수입은 TurboModuleRegistry 폴백으로 core·Expo 양쪽
48
+ 수출에 닿지만, `requireNativeViewManager`는 mechanism이 일치해야 한다. 같은 이름이
49
+ 다른 mechanism으로만 관찰되면 상대편 부재 대신 `*-mechanism-mismatch` 경고로 보고된다.
50
+ 부재를 허용하는 조회(`requireOptionalNativeModule`,
51
+ `TurboModuleRegistry.get`/`getNullable`)로 부른 수입은 `optional: true`를 싣고,
52
+ 부재 모듈의 호출자가 전부 부재를 허용하면 error 대신
53
+ `module-import-without-export-optional` 경고가 나온다.
54
+ `isthmus extract-js`가 JS/TS 소스에서 호출 사실을 추출하고(`NativeModules.*`,
55
+ `TurboModuleRegistry.get*`, `requireNativeComponent`/`codegenNativeComponent`,
56
+ `requireNativeModule` 계열 호출, 해석된 멤버 호출), cartograph와 kartograph는
57
+ Expo Modules DSL(`Module`/`definition()`, `Name`, `Function`, `View`,
58
+ `@ExpoModule`/`@JS`)을 스캔해 그 수출에 `mechanism: "expo"`를 표시한다 —
59
+ `GRAPH-EXCHANGE.md`에 적힌 토큰 스캔 관찰 범위 안에서 end-to-end RN 조인이
60
+ 재현된다. EventChannel v2 전송은 자매 저장소 전반에 구현됐고, `check`가 v2
61
+ Bridge·Event 문서를 직접 소비해 transport별 진단을 낸다.
62
+ 보존 근거보내기는 현재 cartograph(Swift)를 대상으로 한다.
63
+ 앱 전체 적용 범위와 최초 외부 사용자 구축은 아직 검증하지 않았다.
64
+
65
+ 변경 예측은 고정된 공개 정밀도 코퍼스 — `battery_plus`·`shared_preferences_foundation`·
66
+ `url_launcher_macos`와 **LocalSend** 앱, 파일/심볼/버전 diff 15케이스 — 로 측정한다.
67
+ 최근 실행은 **TP 83 / FN 0 / FP 0**을 기록했고, 최초의 앱 수준 Dart↔Swift↔Kotlin
68
+ 3방향 조인을 포함한다([`experiments/real-corpus/`](experiments/real-corpus/)).
69
+ 이 수치는 스텁 컴파일 Swift와 소스 스캔 Kotlin 위의 정적 브리지 경계이며,
70
+ 런타임 실행·앱 전체 정밀도는 측정하지 않았다.
55
71
 
56
72
  | 문서 | 내용 |
57
73
  |---|---|
58
74
  | [`docs/PRD.md`](docs/PRD.md) | 무엇을 · 누구를 위해 · 어디까지 |
59
75
  | [`docs/PLAN.md`](docs/PLAN.md) | 단계별 계획. **cartograph와 dartograph에 선행 작업이 있다** |
60
76
  | [`docs/GRAPH-EXCHANGE.md`](docs/GRAPH-EXCHANGE.md) | 자매 도구가 내보내는 브리지 사실의 형식. 자매 저장소들이 공유하는 계약 |
77
+ | [`docs/COMPATIBILITY.md`](docs/COMPATIBILITY.md) | 공개 호환 버전, 고정 예제, CI 설정 |
61
78
  | [`docs/RESEARCH.md`](docs/RESEARCH.md) | 확인된 사실 · 확인되지 않은 주장 |
79
+ | [`experiments/real-corpus/`](experiments/real-corpus/) | 고정 공개 플러그인·앱 정밀도 코퍼스(TP/FN/FP 계수) |
62
80
  | [`experiments/phase-0/`](experiments/phase-0/) | Dart·Swift 임시 추출기, 고정 JSON, 손 조인 검증 |
63
81
 
64
82
  ## 의존 관계
@@ -83,23 +101,60 @@ npm install --global isthmus-cli
83
101
  isthmus --help
84
102
  ```
85
103
 
86
- 설치 없이 한 번 실행할 때는 package 이름을 명시한다.
104
+ 설치 없이 한 번 실행할 때는 패키지 이름을 명시한다.
87
105
 
88
106
  ```bash
89
107
  npx isthmus-cli --help
90
108
  ```
91
109
 
92
- `npx isthmus`는 이름이 같은 다른 package를 설치하므로 사용하면 안 된다.
110
+ `npx isthmus`는 이름이 같은 다른 패키지를 설치하므로 사용하면 안 된다.
93
111
 
94
112
  ## 사용
95
113
 
96
- isthmus는 자매 도구를 직접 실행하지 않는다. 도구가 만든 JSON 파일을 전달하면 된다.
114
+ `impact --file`·`--symbol`·`--changes` 사전 점검과 정보 손실 없는 `--compact`, 분석
115
+ 공백도 실패시키는 `--strict`를 제공한다. 빌드·계약·현재 브리지 한정 범위는
116
+ [변경 사전 점검](docs/IMPACT.md)을 참조한다.
117
+ Android 개발 지원은 `selection.kotlin`과 kartograph snapshot을 사용한다. Kotlin Method/Basic
118
+ 사실을 Dart 소비자에 연결하고 Android 실행은 Kotlin 후보에만 대조한다.
119
+ [Android 수집 설정](docs/PREFLIGHT.md#android-수집)과 [선택적 Kotlin 도구 구축](docs/TOOLCHAIN.md)을 참조한다.
120
+ `verify-runtime --expectations`는 revision·시나리오·플랫폼·엔진 인스턴스별
121
+ 통신 기록을 대조한다([계약](docs/RUNTIME.md)). 선택적 [Flutter 수집기](packages/isthmus_runtime/README.md)는
122
+ 실제 macOS·Android 앱에서 핸들러와 `url_launcher_macos 3.2.2`·
123
+ `shared_preferences_android 2.4.1`의 Pigeon 생성 API로 검증했다.
124
+ 언어 내부 전이 영향 연결과 스냅샷 수집은 구현됐으며, 더 넓은 앱 적용 범위와
125
+ 처음 설치하는 사용자의 재현 절차는 검증 중이다.
126
+
127
+ 0.6.0의 `preflight <context.json> --strict --compact`는 producer의 전이 영향과
128
+ 브리지를 연결한다. 별도 수집 workflow는 명시된 입력의 내용 해시로 캐시를 재사용하며,
129
+ 실제 producer를 사용한 합성 소스 검증을 통과했다. 사용법·지원 경계·CI 설정은
130
+ [언어 간 변경 사전 점검](docs/PREFLIGHT.md)을 참조한다. 실제 앱 전체 검증은 남아 있다.
131
+ `isthmus init [capture.json]`은 그 capture 설정 scaffold를 쓰고(`--toolchain`을 주면
132
+ 구축한 `toolchain.json`의 실제 producer 명령을 채운다), `isthmus doctor <capture.json>`은
133
+ 설정을 검증하고 참조한 실행 파일이 `PATH`나 지정 경로에 있는지 확인만 한다 — 실행하지 않는다.
134
+ runtime JSON과 `--expectations <checks.json>`를 함께 주면 같은 revision의 실행과
135
+ 전이 분석을 대조하고, 네이티브 후보·미관찰 경계·시나리오 누락을 기존 정적 공백과 함께 보고한다.
136
+
137
+ `preflight <context.json> --summary --strict --compact`로 작은 개요를 읽고,
138
+ `--explain <exact-producer-symbol-id>`로 한 심볼의 전체 원인 경로를 조회한다.
139
+ summary는 목록당 기본 20개(`--limit 1..100`)를 표시하며 생략한 항목도 검토 상태에 반영한다.
140
+ 선택적 [Basic/Pigeon v2 입력](docs/BRIDGE-MESSAGES.md)은 literal 주소와
141
+ 증명된 prefix 후보를 연결한다. prefix의 suffix·instance 배선 불확실성은 유지한다.
142
+ 이 추가 기능은 위에 나열한 공개 producer 버전의 `bridges --messages`로 사용할 수 있다.
143
+ 에이전트 클라이언트는 `isthmus serve`(MCP stdio 서버)로 같은 명령을 도구로 호출할 수 있다 —
144
+ check·query·graph·diff·impact·preflight·retentions를 노출한다.
145
+ [MCP 서버 계약](docs/MCP.md)을 본다.
146
+ 검증된 개발 조합을 재현하거나 도구를 직접 감사하려면 로컬 Git의 고정 commit에서
147
+ 구축하는 [도구 구축 절차](docs/TOOLCHAIN.md)를 쓴다. Dart AOT 실행 파일, impact와
148
+ Basic을 함께 제공하는 cartograph, 격리 설치된 isthmus 패키지를 준비한다.
149
+
150
+ isthmus CLI는 각 도구가 만든 JSON 파일을 읽는다. 선택적 수집 workflow는 설정에
151
+ 명시한 준비·producer 명령을 실행한다.
97
152
 
98
153
  ```bash
99
154
  isthmus check dart-bridges.json swift-bridges.json
100
155
  ```
101
156
 
102
- 전체 명령과 현재 package 버전은 다음과 같이 확인한다.
157
+ 전체 명령과 현재 패키지 버전은 다음과 같이 확인한다.
103
158
 
104
159
  ```bash
105
160
  isthmus --help
@@ -120,6 +175,20 @@ isthmus check dart-bridges.json swift-bridges.json --strict
120
175
  `-h`/`--help`는 어느 위치에 있든 도움말을 내고, `isthmus help <command>`로 명령의
121
176
  사용법을 볼 수 있으며, 모르는 명령은 루트 도움말을 출력한다.
122
177
 
178
+ ### React Native 호출 측 사실
179
+
180
+ React Native 앱에서는 `isthmus extract-js`가 호출 측 문서를 직접 만든다 — JS/TS
181
+ 파일이나 디렉터리를 넘기고, 결과를 자매 도구가 낸 Swift·Kotlin 문서와 함께
182
+ `check`에 넣는다:
183
+
184
+ ```bash
185
+ isthmus extract-js src/ --project . > js-bridges.json
186
+ isthmus check js-bridges.json ios-bridges.json android-bridges.json
187
+ ```
188
+
189
+ 출력은 자매 도구가 만드는 것과 같은 `bridge-facts` 버전 1 문서라 모든 소비
190
+ 명령이 그대로 받는다.
191
+
123
192
  ### SARIF 출력
124
193
 
125
194
  check 결과를 GitHub code scanning(또는 그 밖의 SARIF 2.1.0 소비자)에 올리려면
@@ -129,14 +198,32 @@ isthmus-check JSON 대신 SARIF를 요청한다.
129
198
  isthmus check dart-bridges.json swift-bridges.json --format sarif > isthmus.sarif
130
199
  ```
131
200
 
132
- 기본값은 `--format json`으로 버전이 붙은 isthmus-check 문서를 유지한다. SARIF는 같은
201
+ 기본값인 `--format json`은 버전이 붙은 isthmus-check 문서를 유지한다. SARIF는 같은
133
202
  조인 결과의 additive·isthmus 소유 렌더링이다. 모든 이슈는 check 진단 코드를 규칙 id로
134
203
  하는 결과가 되고, 첫 증거 끝점이 주 위치가 되며(프로젝트 상대 경로가 퍼센트 인코딩된
135
204
  저장소 상대 URI가 된다), 나머지 끝점은 관련 위치로 실린다. 베이스라인이 억제한 이슈는
136
205
  `external` suppression을 달고 나온다. 결과마다 논리 이슈 식별자(code·target·channel·
137
206
  method)의 `partialFingerprints` 해시가 있어 소스 줄이 움직여도 중복 판정이 베이스라인
138
- 억제와 같은 기준으로 살아남는다. `--strict`·`--baseline`·`--update-baseline`은 두 형식
139
- 모두와 조합되고 문서화된 종료 코드 동작을 유지한다.
207
+ 억제와 같은 기준으로 살아남는다.
208
+
209
+ ### GitLab Code Quality 출력
210
+
211
+ check 결과를 GitLab 머지 요청 위젯에 표시하려면 Code Quality 아티팩트를 대신 낸다.
212
+
213
+ ```bash
214
+ isthmus check dart-bridges.json swift-bridges.json --format codequality > gl-code-quality-report.json
215
+ ```
216
+
217
+ 억제되지 않은 모든 이슈가 첫 증거 끝점의 발견 하나가 된다. `check_name`은 `isthmus:`에
218
+ 진단 코드를 붙인 값이고, `severity`는 error를 `major`로·warning을 `minor`로 내리며,
219
+ `fingerprint`는 SARIF와 같은 논리 이슈 해시를 재사용해 실행 사이의 병합 판정이 유지된다.
220
+ 이 형식에는 억제 개념이 없으므로 베이스라인이 받아들인 이슈는 새 발견으로 다시 뜨지
221
+ 않도록 목록에서 제외한다 — 베이스라인을 머지 요청 파이프라인에만 적용하고 기본
222
+ 브랜치에는 적용하지 않으면, GitLab 비교가 받아들인 이슈를 해당 머지 요청이 "고친"
223
+ 것으로 표시할 수 있다는 점에 유의한다.
224
+
225
+ `--strict`·`--baseline`·`--update-baseline`은 모든 형식과 조합되고 문서화된 종료
226
+ 코드 동작을 유지한다.
140
227
 
141
228
  ### 베이스라인
142
229
 
@@ -176,11 +263,11 @@ cartograph dead --external-retentions external-retentions.json
176
263
  ```
177
264
 
178
265
  `retentions`는 핸들러의 USR을 우선 사용하고 없으면 `qualifiedName`을 남긴다. 메서드를
179
- 여러 위치에서 호출하면 근거가 전체 호출 위치를 `callers`로 실고(대표 `caller`는 옛
266
+ 여러 위치에서 호출하면 근거가 전체 호출 위치를 `callers`로 싣고(대표 `caller`는 옛
180
267
  소비자를 위해 유지), 근거당 100개 상한을 넘은 호출은 조용히 버리지 않고
181
268
  `callersOmitted`로 계수를 밝힌다. `mixed-targets` 문서는 v1에서 사실별 target을
182
269
  복원할 수 없어 모든 소비 명령이 종료 코드 2로 조인을 보류하며, 이때 몇 개의 문서에서
183
- 관찰한 fact 몇 개가 조인되지 못했는지를 함께 알린다. 먼저 생산 단계에서
270
+ 관찰한 사실 몇 개가 조인되지 못했는지를 함께 알린다. 먼저 생산 단계에서
184
271
  target별 문서로 분리해야 한다.
185
272
 
186
273
  cartograph는 Swift 심볼만 보존하므로 `--for cartograph`는 수신 측 Swift 문서를 최소
@@ -211,7 +298,7 @@ node scripts/verify-public-flutter-plugin.mjs \
211
298
 
212
299
  공개 플러그인 검증은 원본 `addMethodCallDelegate` 구현에서 나온 Swift USR과 원본
213
300
  Dart 호출 위치 세 곳을 확인하고, cartograph `--explain`이 해당 심볼의 대표 근거를
214
- 읽는지 검증한다. 이미 public인 플러그인 handler의 dead 상태 전환을 억지로 만들지는
301
+ 읽는지 검증한다. 이미 public인 플러그인 핸들러의 dead 상태 전환을 억지로 만들지는
215
302
  않는다. 그 전환과 `setMethodCallHandler` 경로는
216
303
  `verify-cartograph-roundtrip.mjs`의 합성 코퍼스가 별도로 검증한다.
217
304
 
@@ -254,7 +341,44 @@ isthmus graph dart-bridges.json swift-bridges.json --format mermaid
254
341
  핸들러를 놓쳤을 수 있다고 스스로 신고해 없는 것인지 못 본 것인지 판정할 수 없음
255
342
  - `unregistered-channel-creation-unverified` (warning): 같은 이유로 등록 여부를 판정할 수 없음
256
343
 
257
- `summary`는 이슈 계수와 함께 관찰량을 싣는다. `observedFacts`는 입력 문서 전체의 fact
344
+ React Native 이름 경계도 같은 방향으로 보고한다 `require`/`import`에 맞는
345
+ `export`가 없으면 error, `export`에 호출자가 없으면 warning이다.
346
+
347
+ - `module-import-without-export` (error) / `-unverified` (warning)
348
+ - `module-import-without-export-optional` (warning): 호출자가 전부 부재 허용
349
+ API를 썼으므로 수출 부재가 크래시가 아니라 기능 저하임
350
+ - `module-export-without-import` (warning)
351
+ - `component-require-without-export` (error) / `-unverified` (warning)
352
+ - `component-export-without-require` (warning)
353
+ - `module-import-mechanism-mismatch`, `module-export-mechanism-mismatch`,
354
+ `component-require-mechanism-mismatch`, `component-export-mechanism-mismatch`
355
+ (warning): 이름이 상대편에 있지만 호환되지 않는 core/Expo 해석 경로로만
356
+ 관찰됨
357
+
358
+ `check`는 BasicMessageChannel·EventChannel bridge-facts v2 문서도 직접 소비해
359
+ 같은 짝 규칙으로 transport별 진단을 보고한다.
360
+
361
+ - `unhandled-message-send` (error) / `-unverified` (warning): Dart Basic send에
362
+ 대응 네이티브 메시지 핸들러가 없음
363
+ - `message-handler-without-send` (warning): 네이티브 Basic 핸들러에 대응 Dart
364
+ send가 없음
365
+ - `unhandled-stream-listen` (error) / `-unverified` (warning): Dart Event 스트림
366
+ listener에 대응 네이티브 스트림 핸들러가 없음
367
+ - `stream-handler-without-listen` (warning): 네이티브 Event 핸들러에 대응 Dart
368
+ listener가 없음
369
+
370
+ dynamic `channelPrefix` 경로는 판정이 아니라 후보다 — 항상
371
+ `dynamic-message-address`·`dynamic-stream-address` 소비자 한계로 실리고, 관찰된
372
+ 상대가 없으면 `unmatched-message-boundary`·`unmatched-stream-boundary`가 더해지며,
373
+ 증명된 prefix가 없는 동적 주소는 `unresolved-message-addresses` 한계에 포함된다.
374
+ 빠진 쪽을 prefix 후보가 덮는 literal 경계도 error 대신 같은 후보 한계로 내린다.
375
+ `summary`는 v2 입력이 있을 때만 `matchedMessages`·`matchedStreams`(literal 매치만)를
376
+ 더한다. `query`는 v2 경계를 `message`·`stream` kind 주체로, `graph`는 literal v2 경계를
377
+ `message`·`stream` 간선으로, `diff`는 literal v2 경계의 추가·삭제와 v2 진단을 생산한다.
378
+ `retentions --for cartograph`는 literal v2 Swift 핸들러를 method 없는 근거로 보존하고,
379
+ `impact`는 여전히 v1 입력만 받고 version 2를 거부한다.
380
+
381
+ `summary`는 이슈 계수와 함께 관찰량을 싣는다. `observedFacts`는 입력 문서 전체의 사실
258
382
  총수이고 `observedLimitations`는 보고된 분석 한계 수다. 이로써 브리지가 없는 프로젝트와
259
383
  아무것도 관찰하지 못한 실행이 같은 보고서를 내지 않는다 — `observedFacts`가 0이면
260
384
  생산자가 서술할 것을 아무것도 보지 못했다는 뜻이다.
@@ -277,15 +401,15 @@ target에 적용한다. 호출 측 한계는 네이티브 코드를 가리지
277
401
  보존된다. 생산자가 tool 이름을 isthmus로 적어도 자체 계수를 신뢰하지 않으며,
278
402
  `unjoined-*`는 소비자가 직접 붙인 `origin: "consumer"`가 있어야 완화 근거가 된다.
279
403
 
280
- 선택적 fact 필드 `sourceLanguage: "objective-c"`는 `.m`/`.mm`의 ObjC 구현을 Swift
404
+ 선택적 사실 필드 `sourceLanguage: "objective-c"`는 `.m`/`.mm`의 ObjC 구현을 Swift
281
405
  그래프와 구분한다. 이 사실에는 symbol을 붙이지 않는다. 매치는 check/query/graph에
282
406
  남고 Swift 보존 목록에서는 제외되며, `omittedObjectiveCHandlers`가 제외 수를 알린다.
283
407
  표식 없는 Swift 핸들러가 호출자가 있는데 symbol 없이 매치되면 여전히 종료 코드 2로
284
408
  실패한다. 이 확장을 지원하는 소비자를 먼저 배포해야 한다. 옛 소비자는 스코프를
285
409
  버리고 넓게 완화하며 ObjC 보존 생성은 실패한다.
286
410
 
287
- 모든 이슈는 관찰된 위치를 `evidence`로 제공한다. 동적 이름, 해석하지 못한 receiver나
288
- handler 본문, USR 누락, 입력 생성 시각 차이, 혼합 target은 `limitations`에 출처와
411
+ 모든 이슈는 관찰된 위치를 `evidence`로 제공한다. 동적 이름, 해석하지 못한 수신자나
412
+ 핸들러 본문, USR 누락, 입력 생성 시각 차이, 혼합 target은 `limitations`에 출처와
289
413
  함께 남긴다. 이 도구는 삭제 가능 여부를 판정하지 않는다.
290
414
 
291
415
  isthmus 출력 문서는 버전 1 안에서 필드 추가나 새 이슈 code를 호환 변경으로 다룬다.
@@ -310,8 +434,8 @@ isthmus 출력 문서는 버전 1 안에서 필드 추가나 새 이슈 code를
310
434
  | `2` | 파일 읽기, JSON, 교환 계약, project 불일치, 플랫폼 구성 누락, 보류된 조인, 크기 상한(입력 텍스트·그래프 간선·베이스라인 항목), 베이스라인 파일 오류·쓰기 실패, 만들 수 없는 보존 근거 등 도구 실패. stderr가 원인을 구분 |
311
435
  | `64` | 잘못된 명령·옵션·입력 개수 또는 `query`의 `notFound`·`ambiguous` |
312
436
 
313
- 저장소 checkout에서 개발할 때는 먼저 `npm ci`를 실행한다. 개발 검증은 타입 체크,
314
- 제품 코드 90% 커버리지, clean build, 실제 CLI·package 계약을 함께 실행한다.
437
+ 저장소 checkout에서 개발할 때는 먼저 `npm ci`를 실행한다. 개발 검증은 타입 체크와 clean build를 실행하고 제품 코드 90% 커버리지를
438
+ 강제하며 실제 CLI·패키지 계약 검증을 함께 수행한다.
315
439
 
316
440
  ```bash
317
441
  npm run verify
@@ -361,15 +485,16 @@ JSON으로 출력한다. 연결에는 호출자와 핸들러 위치가 포함된
361
485
 
362
486
  `--strict`는 새로 관찰된 error가 있을 때만 1이다. 기존 오류·경고·분석 한계만 있으면
363
487
  0이므로 성공 코드가 삭제 안전성이나 완전한 분석을 뜻하지 않는다. `resolvedIssues`도
364
- 이전 불일치가 더 이상 관찰되지 않는다는 뜻이며, 동적 전환·추출기 변경 때문인지 한계를
488
+ 이전 불일치가 더 이상 관찰되지 않는다는 뜻이며, 동적 전환·추출기 변경 때문인지를 한계와
365
489
  함께 확인해야 한다. `--strict`은 인자 위치와 무관하게 인식하며 두 번 이상 줄 수 없다.
366
490
 
367
- 현재 diff Flutter Dart·Swift 문서만 받는다. 시점에 플랫폼이 모두 필요하며,
491
+ `diff`는 호출 문서(Flutter Dart 또는 React Native JS)와 수신 문서(Swift
492
+ 또는 Kotlin)를 받는다. 한 비교에는 네이티브 언어 하나만 사용하며 각 시점에 호출/수신 문서가 모두 필요하다.
368
493
  양 시점의 `project`와 플랫폼·도구별 문서 개수가 같아야 한다. 한 checkout의 같은
369
494
  경로에서 각 revision을 빌드해 JSON을 보관한다. 일부 파일만 추출한 결과와 전체 결과를
370
495
  비교하지 말고 같은 분석 설정을 사용한다. 입력 파일은 합계 256개, 텍스트 길이 제한은
371
496
  기존 CLI와 동일하다. 혼합 target이나 비교 불가능한 입력은 종료 코드 2로 거부한다.
372
- `generatedAt`은 fact 추출 시각이며 revision 순서가 아니다. 비교 방향은 `--before`와
497
+ `generatedAt`은 사실 추출 시각이며 revision 순서가 아니다. 비교 방향은 `--before`와
373
498
  `--after` 인자로 결정되므로 사용자가 올바른 revision의 파일을 지정해야 한다.
374
499
 
375
500
  ## 코딩 에이전트 skill
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # isthmus
2
2
 
3
3
  **Cross-language bridge calls in cross-platform apps, joined into one graph.**
4
- [cartograph](https://github.com/ictechgy/cartograph) (Swift) · kartograph (Kotlin, planned) ·
4
+ [cartograph](https://github.com/ictechgy/cartograph) (Swift) · [kartograph](https://github.com/ictechgy/kartograph) (Kotlin) ·
5
5
  [dartograph](https://github.com/ictechgy/dartograph) (Dart) each draw their own map; isthmus
6
6
  joins them into one.
7
7
 
@@ -24,46 +24,70 @@ see these strings. As a result:
24
24
 
25
25
  isthmus joins the **bridge facts** each language tool exports (channel names, method names,
26
26
  registration sites, invocation sites) by string key, builds the edges that cross the boundary,
27
- and answers those three questions. It then hands the result **back to cartograph/kartograph as
27
+ and answers those three questions. It then hands the result **back to cartograph as
28
28
  retention evidence** — "keep Swift `CameraHandler.takePhoto`, because `lib/camera.dart:42`
29
29
  calls it over channel `com.example/camera`".
30
30
 
31
31
  ## Status
32
32
 
33
- **0.5.0.** Makes the CLI consistent and its diagnostics more legible: options may appear
34
- before or after the input files in every command (with a `--` escape for `-`-leading paths),
35
- `-h`/`--help` wins from any position alongside `help <command>`, deferred joins report how
36
- many observed facts could not be joined (check, graph, query, retentions, and diff),
37
- `notFound`/`ambiguous` queries state their cause on stderr, and query `qualifiedName` escapes
38
- `:` so splitting on the first `:` and `#` always recovers the names. Internally, shared CLI
39
- infrastructure moved out of check-command, the SARIF fingerprint hash is injected so the
40
- report layer stays free of Node built-ins, and the diff document shape is named
41
- (`BridgeDiffDocument`). Output documents, exit codes, and fields are unchanged — the only
42
- value that differs from 0.4.1 is the `qualifiedName` of channels or methods whose names
43
- contain `:`.
44
- The 0.4.0 additions — SARIF 2.1.0 rendering of check results for GitHub code scanning
45
- (`check --format sarif`, additive, with logic-key fingerprints that survive source line
46
- moves), observation volume in the check summary (`observedFacts`, `observedLimitations`),
47
- multi-caller retention evidence (`evidence.callers` with a per-retention cap and an explicit
48
- `callersOmitted` count, byte-identical for single callers), and usr-less `qualifiedName`
49
- identity for Objective-C handlers built without an index — and the core 0.3.0 contract and
50
- commands remain intact. isthmus keeps fail-closed behavior for external input, mixed targets,
51
- graph size, and the Dart/Swift Phase 0 extraction boundary. Facts that could not be joined
52
- are re-counted on the consumer side, and retention subjects whose evidence cannot be built
53
- are refused loudly, so neither disappears silently. Coverage gaps a receiver reports about
54
- itself come back as undecidable, not as mismatches, and gap mitigation never leaks across
55
- targets. Next: dogfooding it on a real Flutter app, and React Native support.
56
-
57
- The supported producers are cartograph 0.5.3+ and dartograph 0.1.1+. Both were verified on
58
- their real output, and on a Swift USR Dart invocation evidence round trip over the public
59
- battery plugin.
33
+ **0.6.0** supports Flutter Dart Swift/Kotlin change preflight, MethodChannel and
34
+ Pigeon/BasicMessageChannel facts, declared runtime scenarios, and content-based capture reuse.
35
+ It adds `impact --file`/`--symbol`/`--changes` (with lossless `--compact` JSON and a
36
+ gap-aware `--strict` gate), `preflight <context.json>` with `--summary`/`--explain` over
37
+ producer impact paths, and `verify-runtime --expectations`. Actual macOS and Android fixture
38
+ apps have exercised public plugin APIs. See [preflight](docs/PREFLIGHT.md),
39
+ [runtime verification](docs/RUNTIME.md), and
40
+ [building from pinned source commits](docs/TOOLCHAIN.md) for setup and measured limits.
41
+
42
+ The published npm version is **0.7.0**. The compatible public producer set is
43
+ cartograph **0.18.0**, kartograph **0.10.2**, and dartograph **0.14.0** — see
44
+ [compatible versions (Korean)](docs/COMPATIBILITY.md) for install commands, a
45
+ fixed end-to-end example, and a CI sketch. MethodChannel joins and the retention
46
+ round trip are supported from cartograph 0.5.3+ and dartograph 0.1.1+ — exercised on a
47
+ public battery plugin and the round trip was re-verified on the previous public
48
+ set (cartograph 0.15.1, dartograph 0.10.0, isthmus 0.6.0).
49
+ React Native module/component facts (`module-import`↔`module-export`,
50
+ `component-require`↔`component-export`) join by name inside the `react-native`
51
+ target. An optional `mechanism` field keeps the core and Expo resolution paths
52
+ apart: Expo `requireNativeModule`-family imports reach core and Expo exports
53
+ through the TurboModuleRegistry fallback, while `requireNativeViewManager`
54
+ requires a mechanism match, and a name observed only through a different
55
+ mechanism is reported as a `*-mechanism-mismatch` warning instead of a missing
56
+ counterpart. Imports made through absence-tolerant lookups
57
+ (`requireOptionalNativeModule`, `TurboModuleRegistry.get`/`getNullable`) carry
58
+ `optional: true`; when every caller of a missing module tolerates absence, the
59
+ finding is the `module-import-without-export-optional` warning rather than an
60
+ error. `isthmus extract-js` extracts caller-side facts from JS/TS sources
61
+ (`NativeModules.*`, `TurboModuleRegistry.get*`,
62
+ `requireNativeComponent`/`codegenNativeComponent`,
63
+ `requireNativeModule`-family calls, and resolved member calls), and cartograph
64
+ and kartograph scan the Expo Modules DSL (`Module`/`definition()`, `Name`,
65
+ `Function`, `View`, `@ExpoModule`/`@JS`) and mark those exports
66
+ `mechanism: "expo"` — end-to-end RN joins are reproducible within the
67
+ token-scan observation scope documented in `GRAPH-EXCHANGE.md`.
68
+ EventChannel v2 transport is implemented across the sister repositories, and
69
+ `check` now consumes the v2 Bridge/Event documents directly with
70
+ transport-specific diagnostics. Retention export currently targets cartograph
71
+ (Swift). Full application coverage and first-time external setup remain
72
+ unverified.
73
+
74
+ Change predictions are measured against a pinned public precision corpus —
75
+ `battery_plus`, `shared_preferences_foundation`, `url_launcher_macos`, and the
76
+ **LocalSend** app — over 15 file/symbol/version-diff cases. The latest run
77
+ recorded **TP 83 / FN 0 / FP 0**, including the first app-level
78
+ Dart↔Swift↔Kotlin join ([`experiments/real-corpus/`](experiments/real-corpus/)).
79
+ These are static bridge-boundary numbers over stub-compiled Swift and
80
+ source-scanned Kotlin; runtime execution and full-app precision are not
81
+ measured.
60
82
 
61
83
  | Document | Contents |
62
84
  |---|---|
63
85
  | [`docs/PRD.md`](docs/PRD.md) | What, for whom, how far |
64
86
  | [`docs/PLAN.md`](docs/PLAN.md) | Step-by-step plan. **cartograph and dartograph have prerequisite work** |
65
87
  | [`docs/GRAPH-EXCHANGE.md`](docs/GRAPH-EXCHANGE.md) | The bridge-facts format the sister tools export — the contract shared across the sister repositories |
88
+ | [`docs/COMPATIBILITY.md`](docs/COMPATIBILITY.md) | Compatible public versions, fixed example, and CI setup |
66
89
  | [`docs/RESEARCH.md`](docs/RESEARCH.md) | Confirmed facts vs. unconfirmed claims |
90
+ | [`experiments/real-corpus/`](experiments/real-corpus/) | Pinned public-plugin/app precision corpus (TP/FN/FP counts) |
67
91
  | [`experiments/phase-0/`](experiments/phase-0/) | Temporary Dart/Swift extractors, pinned JSON, hand-join verification |
68
92
 
69
93
  Internal documents are maintained in Korean, the maintainer's working language.
@@ -101,7 +125,52 @@ Do not use `npx isthmus` — that installs a different package with the same nam
101
125
 
102
126
  ## Usage
103
127
 
104
- isthmus never runs the sister tools itself. You hand it the JSON files they produced:
128
+ Change preflight is available with `impact --file`, `--symbol`, or `--changes`, plus
129
+ lossless `--compact` JSON and a gap-aware `--strict` gate. See
130
+ [change preflight](docs/IMPACT.md) for the build command, contract, and current
131
+ bridge-only scope.
132
+ Android development support uses `selection.kotlin` and a kartograph snapshot. It connects
133
+ Kotlin Method/Basic facts to Dart consumers and matches Android observations only to Kotlin
134
+ candidates. See [Android capture](docs/PREFLIGHT.md#android-수집) and [toolchain builds](docs/TOOLCHAIN.md).
135
+ `verify-runtime --expectations` checks recorded calls
136
+ by revision, scenario, platform, and engine instance. See the [runtime contract](docs/RUNTIME.md).
137
+ The optional [Flutter recorder](packages/isthmus_runtime/README.md) has been exercised
138
+ in real macOS and Android apps, including the Pigeon-generated APIs of
139
+ `url_launcher_macos 3.2.2` and `shared_preferences_android 2.4.1`. Transitive producer impact
140
+ and snapshot capture are implemented; broader application coverage and first-time setup remain
141
+ under validation.
142
+
143
+ 0.6.0 exposes `preflight <context.json> --strict --compact` to compose
144
+ producer impact paths across the bridge. A separate capture workflow caches declared
145
+ input content and has passed a synthetic source test with real producers. See
146
+ [cross-language preflight](docs/PREFLIGHT.md) for the contract, CI setup, and remaining
147
+ real-application validation.
148
+ `isthmus init [capture.json]` scaffolds that capture config — `--toolchain` fills real
149
+ producer commands from a built `toolchain.json` — and `isthmus doctor <capture.json>`
150
+ validates the config and checks that the referenced executables resolve on `PATH` or at
151
+ the given path, without running them.
152
+ To combine that context with recorded execution, pass runtime JSON files and
153
+ `--expectations <checks.json>`. Preflight reports revision alignment, native candidates,
154
+ and static boundaries missing observations or declared scenarios; existing static gaps remain visible.
155
+
156
+ Use `preflight <context.json> --summary --strict --compact` for a bounded overview, then
157
+ `--explain <exact-producer-symbol-id>` for a complete path to one symbol. Summary defaults
158
+ to 20 items per collection (`--limit 1..100`); omitted items still affect review status.
159
+ Optional [Basic/Pigeon v2 inputs](docs/BRIDGE-MESSAGES.md) connect literal addresses and
160
+ proven prefix candidates. Prefix matches preserve unresolved
161
+ suffix and instance wiring. These additions are available in the public producer
162
+ versions listed above (`bridges --messages`).
163
+ Agent clients can call the same commands over `isthmus serve`, an MCP stdio server
164
+ that exposes check, query, graph, diff, impact, preflight, and retentions as tools.
165
+ See the [MCP server contract](docs/MCP.md).
166
+ To reproduce a verified development combination or audit the toolchain, build the tools
167
+ from pinned local Git commits with the
168
+ [toolchain build workflow (Korean)](docs/TOOLCHAIN.md). It produces a standalone
169
+ Dart executable, a cartograph executable with both impact and message support,
170
+ and an isolated installation of the isthmus package.
171
+
172
+ The isthmus CLI reads JSON produced by the sister tools. The optional capture workflow
173
+ runs the preparation and producer commands declared in its configuration:
105
174
 
106
175
  ```bash
107
176
  isthmus check dart-bridges.json swift-bridges.json
@@ -127,6 +196,20 @@ ends option parsing: `isthmus query -- -unusual-name dart-bridges.json swift-bri
127
196
  `-h`/`--help` shows help from any position, `isthmus help <command>` names a command's usage,
128
197
  and an unknown command prints the root help.
129
198
 
199
+ ### React Native caller facts
200
+
201
+ For a React Native app, `isthmus extract-js` produces the caller-side document
202
+ itself — pass JS/TS files or directories, and feed the result to `check`
203
+ alongside the Swift and Kotlin documents the sister tools emit:
204
+
205
+ ```bash
206
+ isthmus extract-js src/ --project . > js-bridges.json
207
+ isthmus check js-bridges.json ios-bridges.json android-bridges.json
208
+ ```
209
+
210
+ The output is the same `bridge-facts` version 1 document the sister tools
211
+ produce, so every consuming command accepts it unchanged.
212
+
130
213
  ### SARIF output
131
214
 
132
215
  To upload check results to GitHub code scanning (or any SARIF 2.1.0 consumer), ask for SARIF
@@ -143,9 +226,27 @@ additive, isthmus-owned rendering of the same join: every issue becomes a result
143
226
  endpoints as related locations, and baseline-suppressed issues carry an `external`
144
227
  suppression. Results include a
145
228
  `partialFingerprints` hash of the logical issue identity (code, target, channel, method), so
146
- deduplication survives source line moves exactly like baseline suppression. `--strict`,
147
- `--baseline`, and `--update-baseline` combine with either format and keep their documented
148
- exit-code behavior.
229
+ deduplication survives source line moves exactly like baseline suppression.
230
+
231
+ ### GitLab Code Quality output
232
+
233
+ To surface check results in GitLab merge request widgets, emit a Code Quality
234
+ artifact instead:
235
+
236
+ ```bash
237
+ isthmus check dart-bridges.json swift-bridges.json --format codequality > gl-code-quality-report.json
238
+ ```
239
+
240
+ Every unsuppressed issue becomes one finding at its first evidence endpoint: `check_name`
241
+ is `isthmus:` plus the issue code, `severity` maps errors to `major` and warnings to
242
+ `minor`, and `fingerprint` reuses the same logical-issue hash as SARIF, so GitLab merges
243
+ findings across runs. The format has no suppression concept, so baseline-accepted issues
244
+ are left out rather than resurfaced as new findings — note that when the baseline is
245
+ applied on merge request pipelines but not on the default branch, GitLab's comparison
246
+ can present those accepted issues as "fixed" by the merge request.
247
+
248
+ `--strict`, `--baseline`, and `--update-baseline` combine with any format and keep their
249
+ documented exit-code behavior.
149
250
 
150
251
  ### Baselines
151
252
 
@@ -249,7 +350,9 @@ When the same method exists on several channels, `query` returns candidates inst
249
350
  one; feed a returned `qualifiedName` back into the same subject position to disambiguate. A
250
351
  `qualifiedName` is `target:` followed by percent-escaped components — `%`, `#`, and `:` are
251
352
  escaped — so splitting on the first `:` and on `#` and decoding the parts always recovers the
252
- channel and method names. A `notFound` or `ambiguous` query exits 64 and prints a one-line
353
+ channel and method names. Module and component subjects carry a `module:`/`component:` kind
354
+ segment before the name so a channel, a module, and a component that share a name stay
355
+ resolvable. A `notFound` or `ambiguous` query exits 64 and prints a one-line
253
356
  cause on stderr, so a script can tell a bad invocation from a missing name without parsing
254
357
  stdout.
255
358
  `graph` emits matched edges only and preserves the input `limitations` as a JSON field or as
@@ -272,6 +375,42 @@ The output is `isthmus-check` version 1 JSON, reporting these facts:
272
375
  - `unregistered-channel-creation-unverified` (warning): registration undecidable for the same
273
376
  reason
274
377
 
378
+ React Native name boundaries follow the same pairing — a `require`/`import` with no
379
+ matching `export` is an error, an `export` with no caller is a warning:
380
+
381
+ - `module-import-without-export` (error) / `-unverified` (warning)
382
+ - `module-import-without-export-optional` (warning): every caller used an absence-tolerant
383
+ API, so the app degrades rather than crashes when the export is missing
384
+ - `module-export-without-import` (warning)
385
+ - `component-require-without-export` (error) / `-unverified` (warning)
386
+ - `component-export-without-require` (warning)
387
+ - `module-import-mechanism-mismatch`, `module-export-mechanism-mismatch`,
388
+ `component-require-mechanism-mismatch`, `component-export-mechanism-mismatch` (warnings):
389
+ the name exists on the other side but only through an incompatible core/Expo
390
+ resolution path
391
+
392
+ `check` also consumes BasicMessageChannel and EventChannel bridge-facts v2 documents and
393
+ reports transport-specific diagnostics with the same pairing:
394
+
395
+ - `unhandled-message-send` (error) / `-unverified` (warning): a Dart Basic send has no
396
+ native message handler
397
+ - `message-handler-without-send` (warning): a native Basic handler has no Dart send
398
+ - `unhandled-stream-listen` (error) / `-unverified` (warning): a Dart Event stream listener
399
+ has no native stream handler
400
+ - `stream-handler-without-listen` (warning): a native Event handler has no Dart listener
401
+
402
+ A dynamic `channelPrefix` route is a candidate, not a verdict — it is carried as the
403
+ `dynamic-message-address`/`dynamic-stream-address` consumer limitation and, with no observed
404
+ counterpart, additionally as `unmatched-message-boundary`/`unmatched-stream-boundary`; a
405
+ dynamic address with no proven prefix is counted as `unresolved-message-addresses`. A literal
406
+ boundary whose missing side is covered by a prefix candidate is downgraded the same way
407
+ rather than reported as an error. The summary adds `matchedMessages`/`matchedStreams` (literal
408
+ matches only) when v2 inputs are present. `query` resolves v2 boundaries as `message`/`stream`
409
+ kind subjects, `graph` emits `message`/`stream` edges for literal matches, and `diff` reports
410
+ added/removed literal v2 boundaries plus v2 diagnostics. `retentions --for cartograph` keeps
411
+ literal v2 Swift handlers with method-less evidence; `impact` still requires v1 inputs and
412
+ rejects version 2.
413
+
275
414
  The `summary` carries the issue counts plus observation volume: `observedFacts` is the total
276
415
  number of facts across all input documents and `observedLimitations` counts the reported
277
416
  analysis limitations. This keeps a project with no bridges and a run that observed nothing
@@ -393,8 +532,11 @@ analyzed". `resolvedIssues` likewise means a previous mismatch is no longer obse
393
532
  the limitations to see whether a dynamic transition or an extractor change caused it.
394
533
  `--strict` is recognized at any argument position and cannot be given more than once.
395
534
 
396
- `diff` currently accepts only Flutter Dart/Swift documents. Both platforms are required at
397
- each point in time, and the two snapshots must agree on `project` and on the per-platform,
535
+ `diff` accepts caller documents (Flutter Dart or React Native JS) plus either Swift or Kotlin
536
+ receiver documents. Keep one native language per comparison.
537
+ Both sender and receiver documents are required at
538
+ each point in time, and the two snapshots must agree on `project`, on the observed set of
539
+ bridge targets, and on the per-platform,
398
540
  per-tool document counts. Build each revision from the same checkout path and keep the JSON.
399
541
  Do not compare a partial extraction against a full one; use the same analysis settings. Input
400
542
  files are capped at 256 total, and the text size limits match the rest of the CLI. Mixed