isthmus-cli 0.5.0 → 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.
Files changed (80) hide show
  1. package/README.ko.md +49 -28
  2. package/README.md +55 -31
  3. package/Skills/isthmus/SKILL.md +93 -4
  4. package/dist/cli/command-support.d.ts +1 -1
  5. package/dist/cli/command-support.js +2 -2
  6. package/dist/cli/command-support.js.map +1 -1
  7. package/dist/cli/impact-command.d.ts +5 -0
  8. package/dist/cli/impact-command.js +111 -0
  9. package/dist/cli/impact-command.js.map +1 -0
  10. package/dist/cli/main.js +15 -0
  11. package/dist/cli/main.js.map +1 -1
  12. package/dist/cli/preflight-command.d.ts +5 -0
  13. package/dist/cli/preflight-command.js +83 -0
  14. package/dist/cli/preflight-command.js.map +1 -0
  15. package/dist/cli/runtime-command.d.ts +5 -0
  16. package/dist/cli/runtime-command.js +42 -0
  17. package/dist/cli/runtime-command.js.map +1 -0
  18. package/dist/cli/runtime-json-reader.d.ts +12 -0
  19. package/dist/cli/runtime-json-reader.js +37 -0
  20. package/dist/cli/runtime-json-reader.js.map +1 -0
  21. package/dist/exchange/impact-selection.d.ts +13 -0
  22. package/dist/exchange/impact-selection.js +35 -0
  23. package/dist/exchange/impact-selection.js.map +1 -0
  24. package/dist/exchange/kartograph-impact.d.ts +4 -0
  25. package/dist/exchange/kartograph-impact.js +169 -0
  26. package/dist/exchange/kartograph-impact.js.map +1 -0
  27. package/dist/exchange/messages.d.ts +51 -0
  28. package/dist/exchange/messages.js +126 -0
  29. package/dist/exchange/messages.js.map +1 -0
  30. package/dist/exchange/parse.d.ts +8 -0
  31. package/dist/exchange/parse.js +12 -8
  32. package/dist/exchange/parse.js.map +1 -1
  33. package/dist/exchange/preflight-context.d.ts +71 -0
  34. package/dist/exchange/preflight-context.js +317 -0
  35. package/dist/exchange/preflight-context.js.map +1 -0
  36. package/dist/exchange/producer-impact.d.ts +17 -0
  37. package/dist/exchange/producer-impact.js +226 -0
  38. package/dist/exchange/producer-impact.js.map +1 -0
  39. package/dist/exchange/runtime.d.ts +69 -0
  40. package/dist/exchange/runtime.js +160 -0
  41. package/dist/exchange/runtime.js.map +1 -0
  42. package/dist/join/message-address.d.ts +7 -0
  43. package/dist/join/message-address.js +37 -0
  44. package/dist/join/message-address.js.map +1 -0
  45. package/dist/join/messages.d.ts +23 -0
  46. package/dist/join/messages.js +80 -0
  47. package/dist/join/messages.js.map +1 -0
  48. package/dist/report/diff.js +4 -3
  49. package/dist/report/diff.js.map +1 -1
  50. package/dist/report/impact.d.ts +62 -0
  51. package/dist/report/impact.js +168 -0
  52. package/dist/report/impact.js.map +1 -0
  53. package/dist/report/preflight-runtime.d.ts +42 -0
  54. package/dist/report/preflight-runtime.js +193 -0
  55. package/dist/report/preflight-runtime.js.map +1 -0
  56. package/dist/report/preflight-view.d.ts +146 -0
  57. package/dist/report/preflight-view.js +189 -0
  58. package/dist/report/preflight-view.js.map +1 -0
  59. package/dist/report/preflight.d.ts +104 -0
  60. package/dist/report/preflight.js +296 -0
  61. package/dist/report/preflight.js.map +1 -0
  62. package/dist/report/runtime-impact.d.ts +38 -0
  63. package/dist/report/runtime-impact.js +94 -0
  64. package/dist/report/runtime-impact.js.map +1 -0
  65. package/dist/report/runtime.d.ts +54 -0
  66. package/dist/report/runtime.js +137 -0
  67. package/dist/report/runtime.js.map +1 -0
  68. package/dist/report/sorted-json.d.ts +1 -1
  69. package/dist/report/sorted-json.js +2 -2
  70. package/dist/report/sorted-json.js.map +1 -1
  71. package/docs/BRIDGE-MESSAGES.md +102 -0
  72. package/docs/GRAPH-EXCHANGE.md +255 -0
  73. package/docs/IMPACT.md +96 -0
  74. package/docs/PREFLIGHT.md +295 -0
  75. package/docs/RUNTIME.md +172 -0
  76. package/docs/TOOLCHAIN.md +101 -0
  77. package/package.json +11 -2
  78. package/scripts/build-preflight-toolchain.mjs +186 -0
  79. package/scripts/capture-preflight.mjs +392 -0
  80. package/scripts/run-child.mjs +16 -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,36 +22,25 @@ 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.6.0**이며 기능은 호환되는 producer 개발 commit이 필요하다. 발행된
40
+ Dart/Swift MethodChannel 절차는 cartograph 0.5.3 이상과 dartograph 0.1.1 이상을 사용하며
41
+ 공개 battery 플러그인으로 검증했다. Message channel 지원은 현재 개발 producer가 필요하다.
42
+ React Native·EventChannel 추출은 계획이고 보존 근거 내보내기는 현재 cartograph(Swift)
43
+ 대상으로 한다. 전체 적용 범위와 최초 외부 사용자 구축은 아직 검증하지 않았다.
55
44
 
56
45
  | 문서 | 내용 |
57
46
  |---|---|
@@ -93,7 +82,38 @@ npx isthmus-cli --help
93
82
 
94
83
  ## 사용
95
84
 
96
- isthmus는 자매 도구를 직접 실행하지 않는다. 도구가 만든 JSON 파일을 전달하면 된다.
85
+ `impact --file`·`--symbol`·`--changes` 사전 점검과 정보 손실 없는 `--compact`, 분석
86
+ 공백도 실패시키는 `--strict`를 제공한다. 빌드·계약·현재 브리지 한정 범위는
87
+ [변경 사전 점검](docs/IMPACT.md)을 참조한다.
88
+ Android 개발 지원은 `selection.kotlin`과 Kartograph snapshot을 사용한다. Kotlin Method/Basic
89
+ 사실을 Dart 소비자에 연결하고 Android 실행은 Kotlin 후보에만 대조한다.
90
+ [Android 수집 설정](docs/PREFLIGHT.md#android-수집)과 [선택적 Kotlin 도구 구축](docs/TOOLCHAIN.md)을 참조한다.
91
+ `verify-runtime --expectations`는 revision·시나리오·플랫폼·엔진 인스턴스별
92
+ 통신 기록을 대조한다([계약](docs/RUNTIME.md)). 선택적 [Flutter 수집기](packages/isthmus_runtime/README.md)는
93
+ 실제 macOS·Android 앱에서 핸들러와 `url_launcher_macos 3.2.2`·
94
+ `shared_preferences_android 2.4.1`의 Pigeon 생성 API로 검증했다.
95
+ 언어 내부 전이 영향 연결과 스냅샷 수집은 구현됐으며, 더 넓은 앱 적용 범위와
96
+ 처음 설치하는 사용자의 재현 절차는 검증 중이다.
97
+
98
+ 0.6.0의 `preflight <context.json> --strict --compact`는 producer의 전이 영향과
99
+ 브리지를 연결한다. 별도 수집 workflow는 명시된 입력의 내용 해시로 캐시를 재사용하며,
100
+ 실제 producer를 사용한 합성 소스 검증을 통과했다. 사용법·지원 경계·CI 설정은
101
+ [언어 간 변경 사전 점검](docs/PREFLIGHT.md)을 참조한다. 실제 앱 전체 검증은 남아 있다.
102
+ runtime JSON과 `--expectations <checks.json>`를 함께 주면 같은 revision의 실행과
103
+ 전이 분석을 대조하고, native 후보·미관찰 경계·시나리오 누락을 기존 정적 공백과 함께 보고한다.
104
+
105
+ `preflight <context.json> --summary --strict --compact`로 작은 개요를 읽고,
106
+ `--explain <exact-producer-symbol-id>`로 한 심볼의 전체 원인 경로를 조회한다.
107
+ summary는 목록당 기본 20개(`--limit 1..100`)를 표시하며 생략한 항목도 검토 상태에 반영한다.
108
+ 선택적 [Basic/Pigeon v2 입력](docs/BRIDGE-MESSAGES.md)은 개발 producer로 literal 주소와
109
+ 증명된 prefix 후보를 연결한다. prefix의 suffix·instance 배선 불확실성은 유지한다.
110
+ 이 추가 기능에는 개발 producer가 필요하다.
111
+ 호환 개발 도구를 로컬 Git의 고정 commit에서 새로 구축하는 방법은
112
+ [도구 구축 절차](docs/TOOLCHAIN.md)에 있다. Dart AOT 실행 파일, impact와 Basic을 함께
113
+ 제공하는 Cartograph, 격리 설치된 isthmus 패키지를 준비한다.
114
+
115
+ isthmus CLI는 각 도구가 만든 JSON 파일을 읽는다. 선택적 수집 workflow는 설정에
116
+ 명시한 준비·producer 명령을 실행한다.
97
117
 
98
118
  ```bash
99
119
  isthmus check dart-bridges.json swift-bridges.json
@@ -364,7 +384,8 @@ JSON으로 출력한다. 연결에는 호출자와 핸들러 위치가 포함된
364
384
  이전 불일치가 더 이상 관찰되지 않는다는 뜻이며, 동적 전환·추출기 변경 때문인지 한계를
365
385
  함께 확인해야 한다. `--strict`은 인자 위치와 무관하게 인식하며 두 번 이상 줄 수 없다.
366
386
 
367
- 현재 diff는 Flutter Dart·Swift 문서만 받는다. 시점에 플랫폼이 모두 필요하며,
387
+ 개발 소스의 diff는 Flutter DartSwift 또는 Kotlin 문서를 받는다. 비교에는 native 언어
388
+ 하나만 사용하며 각 시점에 호출/수신 문서가 모두 필요하다.
368
389
  양 시점의 `project`와 플랫폼·도구별 문서 개수가 같아야 한다. 한 checkout의 같은
369
390
  경로에서 각 revision을 빌드해 JSON을 보관한다. 일부 파일만 추출한 결과와 전체 결과를
370
391
  비교하지 말고 같은 분석 설정을 사용한다. 입력 파일은 합계 256개, 텍스트 길이 제한은
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,39 +24,26 @@ 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.6.0**; these features require compatible producer commits.
43
+ The published Dart/Swift MethodChannel workflow uses cartograph 0.5.3+ and dartograph
44
+ 0.1.1+; message-channel support currently needs development producers.
45
+ React Native and EventChannel extraction remain planned. Retention export currently targets
46
+ cartograph (Swift). Full application coverage and first-time external setup remain unverified.
60
47
 
61
48
  | Document | Contents |
62
49
  |---|---|
@@ -101,7 +88,43 @@ Do not use `npx isthmus` — that installs a different package with the same nam
101
88
 
102
89
  ## Usage
103
90
 
104
- isthmus never runs the sister tools itself. You hand it the JSON files they produced:
91
+ Change preflight is available with `impact --file`, `--symbol`, or `--changes`, plus
92
+ lossless `--compact` JSON and a gap-aware `--strict` gate. See
93
+ [change preflight](docs/IMPACT.md) for the build command, contract, and current
94
+ bridge-only scope.
95
+ Android development support uses `selection.kotlin` and a Kartograph snapshot. It connects
96
+ Kotlin Method/Basic facts to Dart consumers and matches Android observations only to Kotlin
97
+ candidates. See [Android capture](docs/PREFLIGHT.md#android-수집) and [toolchain builds](docs/TOOLCHAIN.md).
98
+ `verify-runtime --expectations` checks recorded calls
99
+ by revision, scenario, platform, and engine instance. See the [runtime contract](docs/RUNTIME.md).
100
+ The optional [Flutter recorder](packages/isthmus_runtime/README.md) has been exercised
101
+ in real macOS and Android apps, including the Pigeon-generated APIs of
102
+ `url_launcher_macos 3.2.2` and `shared_preferences_android 2.4.1`. Transitive producer impact
103
+ and snapshot capture are implemented; broader application coverage and first-time setup remain
104
+ under validation.
105
+
106
+ 0.6.0 exposes `preflight <context.json> --strict --compact` to compose
107
+ producer impact paths across the bridge. A separate capture workflow caches declared
108
+ input content and has passed a synthetic source test with real producers. See
109
+ [cross-language preflight](docs/PREFLIGHT.md) for the contract, CI setup, and remaining
110
+ real-application validation.
111
+ To combine that context with recorded execution, pass runtime JSON files and
112
+ `--expectations <checks.json>`. Preflight reports revision alignment, native candidates,
113
+ and static boundaries missing observations or declared scenarios; existing static gaps remain visible.
114
+
115
+ Use `preflight <context.json> --summary --strict --compact` for a bounded overview, then
116
+ `--explain <exact-producer-symbol-id>` for a complete path to one symbol. Summary defaults
117
+ to 20 items per collection (`--limit 1..100`); omitted items still affect review status.
118
+ Optional [Basic/Pigeon v2 inputs](docs/BRIDGE-MESSAGES.md) connect literal addresses and
119
+ proven prefix candidates with development producers. Prefix matches preserve unresolved
120
+ suffix and instance wiring. These additions require development producers.
121
+ To build compatible development tools from pinned local Git commits, use the
122
+ [toolchain build workflow (Korean)](docs/TOOLCHAIN.md). It produces a standalone
123
+ Dart executable, a Cartograph executable with both impact and message support,
124
+ and an isolated installation of the isthmus package.
125
+
126
+ The isthmus CLI reads JSON produced by the sister tools. The optional capture workflow
127
+ runs the preparation and producer commands declared in its configuration:
105
128
 
106
129
  ```bash
107
130
  isthmus check dart-bridges.json swift-bridges.json
@@ -393,7 +416,8 @@ analyzed". `resolvedIssues` likewise means a previous mismatch is no longer obse
393
416
  the limitations to see whether a dynamic transition or an extractor change caused it.
394
417
  `--strict` is recognized at any argument position and cannot be given more than once.
395
418
 
396
- `diff` currently accepts only Flutter Dart/Swift documents. Both platforms are required at
419
+ Development `diff` accepts Flutter Dart plus either Swift or Kotlin documents. Keep one native language per comparison.
420
+ Both sender and receiver documents are required at
397
421
  each point in time, and the two snapshots must agree on `project` and on the per-platform,
398
422
  per-tool document counts. Build each revision from the same checkout path and keep the JSON.
399
423
  Do not compare a partial extraction against a full one; use the same analysis settings. Input
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: isthmus
3
3
  description: >-
4
- Trace Flutter Dart-to-Swift bridge callers, compare bridge snapshots, or produce
5
- cartograph retention evidence with isthmus. Use for native MethodChannel handler
6
- changes; React Native and Kotlin extraction are not supported.
4
+ Inspect file or symbol changes across Flutter Dart-to-Swift/Kotlin bridges, trace callers,
5
+ compare snapshots, or produce cartograph retention evidence. Use before changing
6
+ MethodChannel or Pigeon/Basic handlers and their Dart callers; RN extraction is not supported.
7
7
  ---
8
8
 
9
9
  # isthmus
@@ -16,11 +16,88 @@ analysis evidence; it does not authorize source edits, deletion, publishing, or
16
16
  Use `isthmus-cli` 0.1.4+ (`isthmus` binary), cartograph 0.5.3+ and dartograph 0.1.1+.
17
17
  Obtain both Dart and Swift bridge-facts JSON with identical project roots and analysis scope.
18
18
  Swift production requires a built compiler index; use `cartograph bridges --target flutter --format json`.
19
+ Android preflight requires development Kotlin support: obtain Kotlin facts using
20
+ `kartograph bridges --project <root> --target flutter --graph-file <snapshot.json>`.
21
+ For Basic/Pigeon add `--messages`. A snapshot must come from the same prepared source/build;
22
+ source-only names are not compiler symbol identities. Use `selection.kotlin` in the context.
23
+ The capture workflow accepts `kartograph` and `kartographSnapshot`; Android-only capture does
24
+ not require cartograph. Fingerprint the Kartograph launcher and its runtime library directory.
25
+ For an app-local Pigeon package, explicitly include its source in Dartograph's `source_packages`
26
+ configuration and in capture inputs; see `docs/PREFLIGHT.md`. A runtime success alone does not
27
+ establish a static path from the Kotlin implementation through the generated Dart API to the app caller.
19
28
  Do not fabricate missing facts or rewrite project identifiers just to make a join pass.
20
29
  If inputs are missing, identify the required files and proceed with independent authorized work.
21
30
 
22
31
  ## Choose the requested operation
23
32
 
33
+ - Trace transitive cross-language impact when a current producer context is available
34
+ (development source, after 0.5.0):
35
+ `isthmus preflight <context.json> --summary --strict --compact`.
36
+ Add `--revision <expected-capture-revision>` when the workflow supplied that revision.
37
+ Read the whole-report `summary` and `requiresReview`, then bounded collections
38
+ `{total, items, omitted}` for roots, affected symbols, review files and limitations.
39
+ Default display limit is 20; `--limit <1..100>` only applies to summary. Omitted
40
+ entries were analyzed and still affect status. For a requested symbol's complete
41
+ root-to-target path, replace `--summary` with `--explain <exact-key-or-producer-id>`
42
+ from the preview. Exact qualifiedName is also accepted; ambiguous matches return
43
+ candidates with code 64. `result.path` is a full array; each step's relations are
44
+ bounded. Describe those actual steps; do not substitute a similarly named root
45
+ from summary. A path through shared registration may be broader than the requested
46
+ method's execution path. Do not combine summary and explain. Omit both flags only when the full
47
+ report is needed; it includes `affected[].via`, `boundaries` and all evidence.
48
+ Distinguish language use edges
49
+ from bridge evidence; dependency boundaries do not imply changes to every caller
50
+ of an unchanged native handler. Do not repeat queries for evidence already present.
51
+ Use the source-generation workflow to refresh stale context; do not hand-author
52
+ missing producer identities or analyses. The separate `scripts/capture-preflight.mjs`
53
+ workflow executes configured preparation and producer commands, so use the project's
54
+ established configuration and existing authorization. Its fingerprint covers declared
55
+ inputs; inspect that scope before reusing the cache. The `.sources.json` sidecar retains
56
+ original producer reports. Report paths are relative to `project`; verify existence
57
+ before making local links. Code 1 preserves a usable report with gaps; `noChanges`
58
+ only means no modeled source selection. To also verify runtime evidence, use
59
+ `isthmus preflight <context.json> <runtime.json> [more...] --expectations <checks.json> --summary --strict --compact`.
60
+ Read `runtime.aligned` as well as `runtime.verification.status`, `unobservedBoundaries`,
61
+ `uncoveredBoundaries`, and route `staticStatus`. Passing unrelated scenarios or matching
62
+ old expectations/logs cannot verify the current change. Basic/Pigeon static support
63
+ requires optional v2 `context.messages` from both producers; without it Basic is
64
+ unsupported for static matching. `matching: prefix` remains a possible address
65
+ family, with unresolved suffix/instance wiring even after a successful runtime call.
66
+ Address matches remain native candidates. Raw message limitations appear separately
67
+ as `messageLimitations` when message inputs are present.
68
+ Android observations match Kotlin candidates; iOS/macOS observations match Swift candidates.
69
+ In a mixed native context, one platform's success does not cover the other platform.
70
+ Follow each route's candidateKey; do not group native candidates by channel name alone.
71
+ For a runtime-only dynamic route, follow `candidateKey` into `runtime.candidates` for
72
+ native source evidence. In summary/explanation, `candidates.items[].handlers` is a
73
+ bounded collection of source locations/symbols; report its `omitted` count. The full
74
+ report uses `handlersOmitted` for its native-candidate display cap.
75
+ `passedChecks` counts expectations, not distinct scenarios. In summary/explanation,
76
+ `runtime.verification.declaredScenarioPlatforms` counts unique declared scenario/platform
77
+ pairs; it is not a count of passing scenarios. Multiple checks can share one scenario.
78
+ - Preflight a source change: check `isthmus --help` for `impact` (added after the
79
+ published 0.5.0; currently requires a build of the development source).
80
+ Run `isthmus impact --file <project-relative-path> <dart.json> <swift.json> --strict --compact`;
81
+ for a precise producer symbol use `--symbol <qualifiedName-or-usr>` instead.
82
+ For multiple files, pass `--changes <json>` with
83
+ `{"format":"isthmus-changes","version":1,"files":["lib/camera.dart","ios/Camera.swift"]}`.
84
+ Read `reviewFiles`, `methods` (callers and handlers), `issues`, `selectedFacts`,
85
+ `unmatchedSelectors`, and `relevantLimitations` in one response. Channel wiring
86
+ changes include every observed method on that channel. Compact output loses no evidence.
87
+ Impact already includes both sides; do not repeat a channel query just to retrieve
88
+ the same endpoints. Evidence paths are relative to report `project`, not the CLI
89
+ working directory. Make local file links only after confirming the files exist;
90
+ for non-local snapshots, give the reported relative path and line as evidence.
91
+ `--strict` returns code 1 on related errors, extraction gaps and unobserved selections;
92
+ read its JSON as the preflight result instead of rerunning the same inputs without the flag.
93
+ For deleted code use the pre-change snapshot. `scope: bridge` and `complete: false`
94
+ do not establish complete coverage of language-internal or runtime dependencies.
95
+ When current runtime evidence is available, add `--runtime <runtime.json> --revision <revision>`.
96
+ Inspect `runtime.routes`, failures/gaps and `runtime-observation` reasons. These add
97
+ native handler candidates for dynamically named calls, not proof of a particular
98
+ native symbol executing. Static unresolved facts remain unresolved for untested paths.
99
+ If the installed CLI lacks impact, use `query` on bridge names found in the source
100
+ and report that file-based preflight requires the newer implementation.
24
101
  - Audit the boundary: `isthmus check <dart.json> <swift.json> [--strict]`.
25
102
  Read `summary`, each `issues[].code/severity/evidence`, and `limitations`.
26
103
  `-unverified` codes are undecidable findings, not clean results.
@@ -35,15 +112,27 @@ If inputs are missing, identify the required files and proceed with independent
35
112
  Inspect added/removed logical methods, introduced/resolved issues, both sets of limitations,
36
113
  and producer versions/timestamps. Revisions come from the caller's before/after choice.
37
114
  Same-key endpoint changes and rename inference are outside this comparison.
115
+ - Verify recorded runtime calls (development source, after 0.5.0):
116
+ `isthmus verify-runtime --expectations <checks.json> <runtime.json> [more...] --strict --compact`.
117
+ Expectations must be specified independently of the observed log. Check `status`,
118
+ `summary`, unsuccessful `checks`, `failures`, and stale/incomplete `runs`.
119
+ `passed` covers only declared scenarios; missing, stale, dropped or pending evidence
120
+ cannot establish coverage. `evidenceOmitted` is display truncation; `droppedEvents`
121
+ means collection loss. Do not fabricate logs when the runtime recorder is unavailable.
122
+ Expected negative scenarios can declare `allowedOutcomes` explicitly; omitted means
123
+ success only. Preserve expected and unexpected failure counts. Pending calls, stale
124
+ runs, and missing observations cannot be allowed outcomes.
38
125
  - Supply Swift retention evidence, when requested:
39
126
  `isthmus retentions <dart.json> <swift.json> --for cartograph`.
40
127
  Save stdout to a new private temporary file, check successful output, then pass its path to
41
128
  `cartograph dead --external-retentions <path>`. Clean up only artifacts created for this run.
129
+ This command targets Swift; it does not emit Kotlin retention rules.
42
130
 
43
131
  ## Interpret and finish
44
132
 
45
133
  Code 0 means the command ran successfully, not that code is safe to delete.
46
- Code 1 from check/diff strict is a finding to report; query code 64 with
134
+ Code 1 from check/diff/impact/preflight/verify-runtime strict is a finding or evidence gap to report;
135
+ query code 64 with
47
136
  `notFound`/`ambiguous` is a usable answer, while usage errors require corrected arguments.
48
137
  Code 2 indicates unreadable, invalid, or deferred inputs: explain the cause category and next step.
49
138
  Empty results, `resolvedIssues`, and missing callers can reflect dynamic names or incomplete coverage.
@@ -23,7 +23,7 @@ export declare const MAX_INPUT_TEXT_LENGTH: number;
23
23
  /** 한 명령에서 허용하는 전체 UTF-16 입력 문자열 길이다. */
24
24
  export declare const MAX_TOTAL_INPUT_TEXT_LENGTH: number;
25
25
  /** 입력을 순서대로 읽어 파일 수·메모리 상한 안에서 교환 문서로 파싱한다. */
26
- export declare function readBridgeDocuments(inputPaths: readonly string[], readTextFile: ReadTextFile): Promise<BridgeFactsDocument[]>;
26
+ export declare function readBridgeDocuments(inputPaths: readonly string[], readTextFile: ReadTextFile, initialTextLength?: number): Promise<BridgeFactsDocument[]>;
27
27
  /**
28
28
  * 사용자 입력 JSON 파싱 실패인지 확인한다.
29
29
  *
@@ -5,9 +5,9 @@ export const MAX_INPUT_TEXT_LENGTH = 16 * 1024 * 1024;
5
5
  /** 한 명령에서 허용하는 전체 UTF-16 입력 문자열 길이다. */
6
6
  export const MAX_TOTAL_INPUT_TEXT_LENGTH = 64 * 1024 * 1024;
7
7
  /** 입력을 순서대로 읽어 파일 수·메모리 상한 안에서 교환 문서로 파싱한다. */
8
- export async function readBridgeDocuments(inputPaths, readTextFile) {
8
+ export async function readBridgeDocuments(inputPaths, readTextFile, initialTextLength = 0) {
9
9
  const documents = [];
10
- let totalTextLength = 0;
10
+ let totalTextLength = initialTextLength;
11
11
  for (const [index, path] of inputPaths.entries()) {
12
12
  const inputPosition = index + 1;
13
13
  let text;
@@ -1 +1 @@
1
- {"version":3,"file":"command-support.js","sourceRoot":"","sources":["../../src/cli/command-support.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeFactsDocument } from '../exchange/parse.ts';\nimport {\n BridgeFactsValidationError,\n parseBridgeFactsDocument,\n} from '../exchange/parse.ts';\nimport { BridgeJoinValidationError } from '../join/join.ts';\n\n/**\n * 모든 분석 하위 명령이 공유하는 CLI 인프라다.\n *\n * 입력 문서 읽기·예산, 입력 실패의 분류와 메시지, 프로세스 경계 결과 형태를\n * 한 곳에 모아 check-command의 편중을 떼어낸다. baseline 같은 명령 고유\n * 오류는 각 명령이 자신의 실패 매퍼를 덧붙이는 방식으로 확장한다.\n */\n\n/** 파일 경로를 받아 UTF-8 텍스트를 읽는 주입 경계다. */\nexport type ReadTextFile = (path: string) => Promise<string>;\n\n/** 파일 경로에 UTF-8 텍스트를 쓰는 주입 경계다. */\nexport type WriteTextFile = (path: string, text: string) => Promise<void>;\n\n/** 생성 시각을 테스트 가능하게 주입하는 시계다. */\nexport type Clock = () => Date;\n\n/** CLI가 프로세스 경계에 쓸 출력과 종료 코드다. */\nexport interface CommandResult {\n readonly standardOutput: string;\n readonly standardError: string;\n readonly exitCode: 0 | 1 | 2 | 64;\n}\n\n/** 한 입력 파일에서 허용하는 최대 UTF-16 문자열 길이다. */\nexport const MAX_INPUT_TEXT_LENGTH = 16 * 1024 * 1024;\n\n/** 한 명령에서 허용하는 전체 UTF-16 입력 문자열 길이다. */\nexport const MAX_TOTAL_INPUT_TEXT_LENGTH = 64 * 1024 * 1024;\n\n/** 입력을 순서대로 읽어 파일 수·메모리 상한 안에서 교환 문서로 파싱한다. */\nexport async function readBridgeDocuments(\n inputPaths: readonly string[],\n readTextFile: ReadTextFile,\n): Promise<BridgeFactsDocument[]> {\n const documents: BridgeFactsDocument[] = [];\n let totalTextLength = 0;\n for (const [index, path] of inputPaths.entries()) {\n const inputPosition = index + 1;\n let text: string;\n try {\n text = await readTextFile(path);\n } catch {\n throw new BridgeInputReadError(inputPosition);\n }\n totalTextLength += text.length;\n if (\n text.length > MAX_INPUT_TEXT_LENGTH ||\n totalTextLength > MAX_TOTAL_INPUT_TEXT_LENGTH\n ) {\n throw new BridgeInputLimitError(inputPosition);\n }\n // JSON 구문 오류와 문서 검증 실패를 같은 try로 감싸면 검증기의 예외가\n // 구문 오류로 오분류된다. 파싱과 검증을 나눠 분류 경계를 지킨다.\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n if (isJsonParseFailure(error)) {\n throw new BridgeInputJsonError(inputPosition);\n }\n throw error;\n }\n try {\n documents.push(parseBridgeFactsDocument(parsed));\n } catch (error) {\n if (error instanceof BridgeFactsValidationError) {\n throw new BridgeInputContractError(inputPosition, error.message);\n }\n throw error;\n }\n }\n return documents;\n}\n\n/**\n * 사용자 입력 JSON 파싱 실패인지 확인한다.\n *\n * 깊은 중첩은 SyntaxError가 아니라 RangeError(스택 초과)로 실패한다.\n * 둘 다 입력 탓이라 내부 오류 메시지로 오분류하지 않는다.\n */\nexport function isJsonParseFailure(error: unknown): boolean {\n return error instanceof SyntaxError || error instanceof RangeError;\n}\n\n/** 실패한 입력의 1부터 시작하는 순서를 보존하는 입력 오류다. */\nclass BridgeInputError extends Error {\n readonly inputPosition: number;\n\n constructor(name: string, inputPosition: number) {\n super(`${name} at input ${inputPosition}`);\n this.name = name;\n this.inputPosition = inputPosition;\n }\n}\n\n/** 입력 파일을 읽지 못한 경우를 구분한다. */\nclass BridgeInputReadError extends BridgeInputError {\n constructor(inputPosition: number) {\n super('BridgeInputReadError', inputPosition);\n }\n}\n\n/** 입력 텍스트가 JSON이 아닌 경우를 구분한다. */\nclass BridgeInputJsonError extends BridgeInputError {\n constructor(inputPosition: number) {\n super('BridgeInputJsonError', inputPosition);\n }\n}\n\n/** 입력 문서가 교환 계약을 어긴 경우와 그 이유를 구분한다. */\nclass BridgeInputContractError extends BridgeInputError {\n readonly reason: string;\n\n constructor(inputPosition: number, reason: string) {\n super('BridgeInputContractError', inputPosition);\n this.reason = reason;\n }\n}\n\n/** 입력 텍스트의 크기만 계약을 넘은 경우를 구분한다. */\nclass BridgeInputLimitError extends BridgeInputError {\n constructor(inputPosition: number) {\n super('BridgeInputLimitError', inputPosition);\n }\n}\n\n/** 알려진 입력·조인 실패를 원인별 해결 방향을 담은 코드 2 결과로 바꾼다.\n\n명령 고유의 입력(baseline 등)은 각 명령이 자신의 매퍼를 이 뒤에 붙인다. */\nexport function inputFailureResult(error: unknown): CommandResult | undefined {\n if (error instanceof BridgeInputReadError) {\n return inputFailure(\n `Unable to read bridge facts input ${error.inputPosition}; `\n + 'check that the file exists and is readable.\\n',\n );\n }\n if (error instanceof BridgeInputJsonError) {\n return inputFailure(\n `Bridge facts input ${error.inputPosition} is not valid JSON; `\n + 'regenerate it with a bridge-facts producer.\\n',\n );\n }\n if (error instanceof BridgeInputContractError) {\n return inputFailure(\n `Bridge facts input ${error.inputPosition} violates the bridge-facts `\n + `contract: ${error.reason}\\n`,\n );\n }\n if (error instanceof BridgeInputLimitError) {\n return inputFailure(\n `Bridge facts input ${error.inputPosition} exceeds the input size limits; `\n + 'split the extraction into smaller documents.\\n',\n );\n }\n if (error instanceof BridgeJoinValidationError) {\n return inputFailure(`${error.message}\\n`);\n }\n return undefined;\n}\n\n/** 원인 메시지만 stdout 없이 코드 2로 내보낸다. */\nexport function inputFailure(standardError: string): CommandResult {\n return { standardOutput: '', standardError, exitCode: 2 };\n}\n\n/**\n * 전체 조인 보류를 깨끗한 결과와 구분하는 코드 2 결과로 바꾼다.\n *\n * 관찰량을 숫자 보간으로 함께 알린다. 보류된 조인은 문서를 출력하지 않으므로,\n * 이 숫자가 \"몇 개를 봤는데 못 조인했는지\"가 도달하는 유일한 경로다.\n */\nexport function bridgeJoinDeferredError(\n observedFacts: number,\n documentCount: number,\n): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Bridge facts could not be joined; split mixed bridge targets and retry. '\n + `The inputs observed ${observedFacts} facts across ${documentCount} documents.\\n`,\n exitCode: 2,\n };\n}\n\n/** 내부 결함을 입력 탓으로 돌리지 않는 경로 없는 코드 2 결과다. */\nexport function internalError(): CommandResult {\n return {\n standardOutput: '',\n standardError: 'Internal isthmus error; retry with a current version.\\n',\n exitCode: 2,\n };\n}\n"]}
1
+ {"version":3,"file":"command-support.js","sourceRoot":"","sources":["../../src/cli/command-support.ts"],"names":[],"mappings":"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","sourcesContent":["import type { BridgeFactsDocument } from '../exchange/parse.ts';\nimport {\n BridgeFactsValidationError,\n parseBridgeFactsDocument,\n} from '../exchange/parse.ts';\nimport { BridgeJoinValidationError } from '../join/join.ts';\n\n/**\n * 모든 분석 하위 명령이 공유하는 CLI 인프라다.\n *\n * 입력 문서 읽기·예산, 입력 실패의 분류와 메시지, 프로세스 경계 결과 형태를\n * 한 곳에 모아 check-command의 편중을 떼어낸다. baseline 같은 명령 고유\n * 오류는 각 명령이 자신의 실패 매퍼를 덧붙이는 방식으로 확장한다.\n */\n\n/** 파일 경로를 받아 UTF-8 텍스트를 읽는 주입 경계다. */\nexport type ReadTextFile = (path: string) => Promise<string>;\n\n/** 파일 경로에 UTF-8 텍스트를 쓰는 주입 경계다. */\nexport type WriteTextFile = (path: string, text: string) => Promise<void>;\n\n/** 생성 시각을 테스트 가능하게 주입하는 시계다. */\nexport type Clock = () => Date;\n\n/** CLI가 프로세스 경계에 쓸 출력과 종료 코드다. */\nexport interface CommandResult {\n readonly standardOutput: string;\n readonly standardError: string;\n readonly exitCode: 0 | 1 | 2 | 64;\n}\n\n/** 한 입력 파일에서 허용하는 최대 UTF-16 문자열 길이다. */\nexport const MAX_INPUT_TEXT_LENGTH = 16 * 1024 * 1024;\n\n/** 한 명령에서 허용하는 전체 UTF-16 입력 문자열 길이다. */\nexport const MAX_TOTAL_INPUT_TEXT_LENGTH = 64 * 1024 * 1024;\n\n/** 입력을 순서대로 읽어 파일 수·메모리 상한 안에서 교환 문서로 파싱한다. */\nexport async function readBridgeDocuments(\n inputPaths: readonly string[],\n readTextFile: ReadTextFile,\n initialTextLength = 0,\n): Promise<BridgeFactsDocument[]> {\n const documents: BridgeFactsDocument[] = [];\n let totalTextLength = initialTextLength;\n for (const [index, path] of inputPaths.entries()) {\n const inputPosition = index + 1;\n let text: string;\n try {\n text = await readTextFile(path);\n } catch {\n throw new BridgeInputReadError(inputPosition);\n }\n totalTextLength += text.length;\n if (\n text.length > MAX_INPUT_TEXT_LENGTH ||\n totalTextLength > MAX_TOTAL_INPUT_TEXT_LENGTH\n ) {\n throw new BridgeInputLimitError(inputPosition);\n }\n // JSON 구문 오류와 문서 검증 실패를 같은 try로 감싸면 검증기의 예외가\n // 구문 오류로 오분류된다. 파싱과 검증을 나눠 분류 경계를 지킨다.\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n if (isJsonParseFailure(error)) {\n throw new BridgeInputJsonError(inputPosition);\n }\n throw error;\n }\n try {\n documents.push(parseBridgeFactsDocument(parsed));\n } catch (error) {\n if (error instanceof BridgeFactsValidationError) {\n throw new BridgeInputContractError(inputPosition, error.message);\n }\n throw error;\n }\n }\n return documents;\n}\n\n/**\n * 사용자 입력 JSON 파싱 실패인지 확인한다.\n *\n * 깊은 중첩은 SyntaxError가 아니라 RangeError(스택 초과)로 실패한다.\n * 둘 다 입력 탓이라 내부 오류 메시지로 오분류하지 않는다.\n */\nexport function isJsonParseFailure(error: unknown): boolean {\n return error instanceof SyntaxError || error instanceof RangeError;\n}\n\n/** 실패한 입력의 1부터 시작하는 순서를 보존하는 입력 오류다. */\nclass BridgeInputError extends Error {\n readonly inputPosition: number;\n\n constructor(name: string, inputPosition: number) {\n super(`${name} at input ${inputPosition}`);\n this.name = name;\n this.inputPosition = inputPosition;\n }\n}\n\n/** 입력 파일을 읽지 못한 경우를 구분한다. */\nclass BridgeInputReadError extends BridgeInputError {\n constructor(inputPosition: number) {\n super('BridgeInputReadError', inputPosition);\n }\n}\n\n/** 입력 텍스트가 JSON이 아닌 경우를 구분한다. */\nclass BridgeInputJsonError extends BridgeInputError {\n constructor(inputPosition: number) {\n super('BridgeInputJsonError', inputPosition);\n }\n}\n\n/** 입력 문서가 교환 계약을 어긴 경우와 그 이유를 구분한다. */\nclass BridgeInputContractError extends BridgeInputError {\n readonly reason: string;\n\n constructor(inputPosition: number, reason: string) {\n super('BridgeInputContractError', inputPosition);\n this.reason = reason;\n }\n}\n\n/** 입력 텍스트의 크기만 계약을 넘은 경우를 구분한다. */\nclass BridgeInputLimitError extends BridgeInputError {\n constructor(inputPosition: number) {\n super('BridgeInputLimitError', inputPosition);\n }\n}\n\n/** 알려진 입력·조인 실패를 원인별 해결 방향을 담은 코드 2 결과로 바꾼다.\n\n명령 고유의 입력(baseline 등)은 각 명령이 자신의 매퍼를 이 뒤에 붙인다. */\nexport function inputFailureResult(error: unknown): CommandResult | undefined {\n if (error instanceof BridgeInputReadError) {\n return inputFailure(\n `Unable to read bridge facts input ${error.inputPosition}; `\n + 'check that the file exists and is readable.\\n',\n );\n }\n if (error instanceof BridgeInputJsonError) {\n return inputFailure(\n `Bridge facts input ${error.inputPosition} is not valid JSON; `\n + 'regenerate it with a bridge-facts producer.\\n',\n );\n }\n if (error instanceof BridgeInputContractError) {\n return inputFailure(\n `Bridge facts input ${error.inputPosition} violates the bridge-facts `\n + `contract: ${error.reason}\\n`,\n );\n }\n if (error instanceof BridgeInputLimitError) {\n return inputFailure(\n `Bridge facts input ${error.inputPosition} exceeds the input size limits; `\n + 'split the extraction into smaller documents.\\n',\n );\n }\n if (error instanceof BridgeJoinValidationError) {\n return inputFailure(`${error.message}\\n`);\n }\n return undefined;\n}\n\n/** 원인 메시지만 stdout 없이 코드 2로 내보낸다. */\nexport function inputFailure(standardError: string): CommandResult {\n return { standardOutput: '', standardError, exitCode: 2 };\n}\n\n/**\n * 전체 조인 보류를 깨끗한 결과와 구분하는 코드 2 결과로 바꾼다.\n *\n * 관찰량을 숫자 보간으로 함께 알린다. 보류된 조인은 문서를 출력하지 않으므로,\n * 이 숫자가 \"몇 개를 봤는데 못 조인했는지\"가 도달하는 유일한 경로다.\n */\nexport function bridgeJoinDeferredError(\n observedFacts: number,\n documentCount: number,\n): CommandResult {\n return {\n standardOutput: '',\n standardError:\n 'Bridge facts could not be joined; split mixed bridge targets and retry. '\n + `The inputs observed ${observedFacts} facts across ${documentCount} documents.\\n`,\n exitCode: 2,\n };\n}\n\n/** 내부 결함을 입력 탓으로 돌리지 않는 경로 없는 코드 2 결과다. */\nexport function internalError(): CommandResult {\n return {\n standardOutput: '',\n standardError: 'Internal isthmus error; retry with a current version.\\n',\n exitCode: 2,\n };\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import type { CommandResult, ReadTextFile } from './command-support.ts';
2
+ /** 파일·심볼·CI 변경 목록을 브리지 영향 보고서로 바꾸는 CLI 경계다. */
3
+ export declare function runImpactCommand(arguments_: readonly string[], readTextFile: ReadTextFile): Promise<CommandResult>;
4
+ /** impact 명령의 독립적인 선택 방식과 CI 실패 옵션이다. */
5
+ export declare const impactUsage: string;
@@ -0,0 +1,111 @@
1
+ import { ImpactSelectionValidationError, parseImpactSelection } from "../exchange/impact-selection.js";
2
+ import { isSafeNonEmptyString } from "../exchange/parse.js";
3
+ import { parseBridgeRuntime, RuntimeValidationError } from "../exchange/runtime.js";
4
+ import { MAX_DOCUMENTS_PER_JOIN } from "../join/join.js";
5
+ import { createBridgeImpact, encodeBridgeImpact, hasImpactBlockers } from "../report/impact.js";
6
+ import { inputFailure, inputFailureResult, internalError, isJsonParseFailure, MAX_INPUT_TEXT_LENGTH, readBridgeDocuments, } from "./command-support.js";
7
+ import { parseCommandArguments } from "./parse-arguments.js";
8
+ /** 파일·심볼·CI 변경 목록을 브리지 영향 보고서로 바꾸는 CLI 경계다. */
9
+ export async function runImpactCommand(arguments_, readTextFile) {
10
+ const parsed = parseCommandArguments(arguments_.slice(1), ['--file', '--symbol', '--changes', '--runtime', '--revision'], ['--strict', '--compact']);
11
+ if (parsed === undefined || ['--file', '--symbol', '--changes'].filter((flag) => parsed.valueFlags.has(flag)).length !== 1 ||
12
+ parsed.positionals.length < 2 || parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN) {
13
+ return usageError();
14
+ }
15
+ const runtimePath = parsed.valueFlags.get('--runtime');
16
+ const revision = parsed.valueFlags.get('--revision');
17
+ if ((runtimePath === undefined) !== (revision === undefined) ||
18
+ (revision !== undefined && !isSafeNonEmptyString(revision)))
19
+ return usageError();
20
+ try {
21
+ let selection;
22
+ let supplementalTextLength = 0;
23
+ const changesPath = parsed.valueFlags.get('--changes');
24
+ if (changesPath !== undefined) {
25
+ const loaded = await readSupplemental(changesPath, readTextFile, 'changes', parseImpactSelection);
26
+ if ('exitCode' in loaded)
27
+ return loaded;
28
+ selection = loaded.document;
29
+ supplementalTextLength += loaded.textLength;
30
+ }
31
+ else {
32
+ const file = parsed.valueFlags.get('--file');
33
+ const symbol = parsed.valueFlags.get('--symbol');
34
+ try {
35
+ selection = parseImpactSelection({ format: 'isthmus-changes', version: 1,
36
+ files: file === undefined ? [] : [file], symbols: symbol === undefined ? [] : [symbol] });
37
+ }
38
+ catch (error) {
39
+ if (error instanceof ImpactSelectionValidationError)
40
+ return usageError();
41
+ throw error;
42
+ }
43
+ }
44
+ let runtime;
45
+ if (runtimePath !== undefined && revision !== undefined) {
46
+ const loaded = await readSupplemental(runtimePath, readTextFile, 'runtime', parseBridgeRuntime);
47
+ if ('exitCode' in loaded)
48
+ return loaded;
49
+ supplementalTextLength += loaded.textLength;
50
+ runtime = { document: loaded.document, revision };
51
+ }
52
+ const documents = await readBridgeDocuments(parsed.positionals, readTextFile, supplementalTextLength);
53
+ const report = createBridgeImpact(documents, selection, runtime);
54
+ const blocked = parsed.booleanFlags.has('--strict') && hasImpactBlockers(report);
55
+ return {
56
+ standardOutput: encodeBridgeImpact(report, parsed.booleanFlags.has('--compact')),
57
+ standardError: blocked
58
+ ? 'Change preflight requires review: related errors, analysis gaps, or unobserved selectors remain.\n'
59
+ : report.status === 'unobserved'
60
+ ? 'No selected bridge facts were observed; inspect source coverage and use a pre-change snapshot for deleted code.\n'
61
+ : '',
62
+ exitCode: blocked ? 1 : 0,
63
+ };
64
+ }
65
+ catch (error) {
66
+ if (error instanceof RuntimeValidationError)
67
+ return inputFailure(`${error.message}\n`);
68
+ return inputFailureResult(error) ?? internalError();
69
+ }
70
+ }
71
+ /** 변경 파일도 브리지 입력과 같은 읽기·구문·계약 실패 경계를 지킨다. */
72
+ async function readSupplemental(path, readTextFile, kind, parse) {
73
+ const label = kind === 'changes' ? 'Changes' : 'Runtime';
74
+ const format = kind === 'changes' ? 'isthmus-changes' : 'bridge-runtime';
75
+ let text;
76
+ try {
77
+ text = await readTextFile(path);
78
+ }
79
+ catch {
80
+ return inputFailure(`Unable to read ${kind} input; check that the file exists and is readable.\n`);
81
+ }
82
+ if (text.length > MAX_INPUT_TEXT_LENGTH)
83
+ return inputFailure(`${label} input exceeds the input size limit.\n`);
84
+ let value;
85
+ try {
86
+ value = JSON.parse(text);
87
+ }
88
+ catch (error) {
89
+ if (isJsonParseFailure(error))
90
+ return inputFailure(`${label} input is not valid JSON.\n`);
91
+ throw error;
92
+ }
93
+ try {
94
+ return { document: parse(value), textLength: text.length };
95
+ }
96
+ catch (error) {
97
+ if (error instanceof ImpactSelectionValidationError || error instanceof RuntimeValidationError) {
98
+ return inputFailure(`${label} input violates the ${format} contract: ${error.message}\n`);
99
+ }
100
+ throw error;
101
+ }
102
+ }
103
+ /** 잘못된 인수는 입력을 읽기 전에 코드 64로 거부한다. */
104
+ function usageError() {
105
+ return { standardOutput: '', standardError: `${impactUsage}\n`, exitCode: 64 };
106
+ }
107
+ /** impact 명령의 독립적인 선택 방식과 CI 실패 옵션이다. */
108
+ export const impactUsage = 'Usage: isthmus impact (--file <relative-path> | --symbol <name-or-usr> '
109
+ + '| --changes <changes.json>) <bridge-facts.json> <bridge-facts.json> [more...] '
110
+ + '[--runtime <runtime.json> --revision <revision>] [--strict] [--compact]';
111
+ //# sourceMappingURL=impact-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"impact-command.js","sourceRoot":"","sources":["../../src/cli/impact-command.ts"],"names":[],"mappings":"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","sourcesContent":["import { ImpactSelectionValidationError, parseImpactSelection } from '../exchange/impact-selection.ts';\nimport type { ImpactSelection } from '../exchange/impact-selection.ts';\nimport { isSafeNonEmptyString } from '../exchange/parse.ts';\nimport { parseBridgeRuntime, RuntimeValidationError } from '../exchange/runtime.ts';\nimport { MAX_DOCUMENTS_PER_JOIN } from '../join/join.ts';\nimport { createBridgeImpact, encodeBridgeImpact, hasImpactBlockers } from '../report/impact.ts';\nimport type { ImpactRuntimeInput } from '../report/runtime-impact.ts';\nimport {\n inputFailure, inputFailureResult, internalError, isJsonParseFailure,\n MAX_INPUT_TEXT_LENGTH, readBridgeDocuments,\n} from './command-support.ts';\nimport type { CommandResult, ReadTextFile } from './command-support.ts';\nimport { parseCommandArguments } from './parse-arguments.ts';\n\n/** 파일·심볼·CI 변경 목록을 브리지 영향 보고서로 바꾸는 CLI 경계다. */\nexport async function runImpactCommand(\n arguments_: readonly string[], readTextFile: ReadTextFile,\n): Promise<CommandResult> {\n const parsed = parseCommandArguments(arguments_.slice(1),\n ['--file', '--symbol', '--changes', '--runtime', '--revision'], ['--strict', '--compact']);\n if (parsed === undefined || ['--file', '--symbol', '--changes'].filter((flag) => parsed.valueFlags.has(flag)).length !== 1 ||\n parsed.positionals.length < 2 || parsed.positionals.length > MAX_DOCUMENTS_PER_JOIN) {\n return usageError();\n }\n const runtimePath = parsed.valueFlags.get('--runtime');\n const revision = parsed.valueFlags.get('--revision');\n if ((runtimePath === undefined) !== (revision === undefined) ||\n (revision !== undefined && !isSafeNonEmptyString(revision))) return usageError();\n try {\n let selection: ImpactSelection;\n let supplementalTextLength = 0;\n const changesPath = parsed.valueFlags.get('--changes');\n if (changesPath !== undefined) {\n const loaded = await readSupplemental(changesPath, readTextFile, 'changes', parseImpactSelection);\n if ('exitCode' in loaded) return loaded;\n selection = loaded.document;\n supplementalTextLength += loaded.textLength;\n } else {\n const file = parsed.valueFlags.get('--file');\n const symbol = parsed.valueFlags.get('--symbol');\n try {\n selection = parseImpactSelection({ format: 'isthmus-changes', version: 1,\n files: file === undefined ? [] : [file], symbols: symbol === undefined ? [] : [symbol] });\n } catch (error) {\n if (error instanceof ImpactSelectionValidationError) return usageError();\n throw error;\n }\n }\n let runtime: ImpactRuntimeInput | undefined;\n if (runtimePath !== undefined && revision !== undefined) {\n const loaded = await readSupplemental(runtimePath, readTextFile, 'runtime', parseBridgeRuntime);\n if ('exitCode' in loaded) return loaded;\n supplementalTextLength += loaded.textLength;\n runtime = { document: loaded.document, revision };\n }\n const documents = await readBridgeDocuments(parsed.positionals, readTextFile, supplementalTextLength);\n const report = createBridgeImpact(documents, selection, runtime);\n const blocked = parsed.booleanFlags.has('--strict') && hasImpactBlockers(report);\n return {\n standardOutput: encodeBridgeImpact(report, parsed.booleanFlags.has('--compact')),\n standardError: blocked\n ? 'Change preflight requires review: related errors, analysis gaps, or unobserved selectors remain.\\n'\n : report.status === 'unobserved'\n ? 'No selected bridge facts were observed; inspect source coverage and use a pre-change snapshot for deleted code.\\n'\n : '',\n exitCode: blocked ? 1 : 0,\n };\n } catch (error) {\n if (error instanceof RuntimeValidationError) return inputFailure(`${error.message}\\n`);\n return inputFailureResult(error) ?? internalError();\n }\n}\n\n/** 변경 파일도 브리지 입력과 같은 읽기·구문·계약 실패 경계를 지킨다. */\nasync function readSupplemental<T>(\n path: string, readTextFile: ReadTextFile, kind: 'changes' | 'runtime', parse: (value: unknown) => T,\n): Promise<\n { document: T; textLength: number } | CommandResult\n> {\n const label = kind === 'changes' ? 'Changes' : 'Runtime';\n const format = kind === 'changes' ? 'isthmus-changes' : 'bridge-runtime';\n let text: string;\n try { text = await readTextFile(path); }\n catch { return inputFailure(`Unable to read ${kind} input; check that the file exists and is readable.\\n`); }\n if (text.length > MAX_INPUT_TEXT_LENGTH) return inputFailure(`${label} input exceeds the input size limit.\\n`);\n let value: unknown;\n try { value = JSON.parse(text); }\n catch (error) {\n if (isJsonParseFailure(error)) return inputFailure(`${label} input is not valid JSON.\\n`);\n throw error;\n }\n try { return { document: parse(value), textLength: text.length }; }\n catch (error) {\n if (error instanceof ImpactSelectionValidationError || error instanceof RuntimeValidationError) {\n return inputFailure(`${label} input violates the ${format} contract: ${error.message}\\n`);\n }\n throw error;\n }\n}\n\n/** 잘못된 인수는 입력을 읽기 전에 코드 64로 거부한다. */\nfunction usageError(): CommandResult {\n return { standardOutput: '', standardError: `${impactUsage}\\n`, exitCode: 64 };\n}\n\n/** impact 명령의 독립적인 선택 방식과 CI 실패 옵션이다. */\nexport const impactUsage = 'Usage: isthmus impact (--file <relative-path> | --symbol <name-or-usr> '\n + '| --changes <changes.json>) <bridge-facts.json> <bridge-facts.json> [more...] '\n + '[--runtime <runtime.json> --revision <revision>] [--strict] [--compact]';\n"]}