isthmus-cli 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +367 -0
- package/README.md +304 -176
- package/Skills/isthmus/SKILL.md +7 -0
- package/dist/cli/atomic-write.d.ts +11 -0
- package/dist/cli/atomic-write.js +32 -0
- package/dist/cli/atomic-write.js.map +1 -0
- package/dist/cli/check-command.d.ts +7 -1
- package/dist/cli/check-command.js +203 -11
- package/dist/cli/check-command.js.map +1 -1
- package/dist/cli/main.js +4 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/retentions-command.d.ts +2 -3
- package/dist/cli/retentions-command.js.map +1 -1
- package/dist/exchange/parse.d.ts +8 -0
- package/dist/exchange/parse.js +15 -9
- package/dist/exchange/parse.js.map +1 -1
- package/dist/join/join.d.ts +2 -0
- package/dist/join/join.js +8 -4
- package/dist/join/join.js.map +1 -1
- package/dist/report/baseline.d.ts +61 -0
- package/dist/report/baseline.js +170 -0
- package/dist/report/baseline.js.map +1 -0
- package/dist/report/check-report.d.ts +19 -1
- package/dist/report/check-report.js +16 -0
- package/dist/report/check-report.js.map +1 -1
- package/dist/report/diff.js +3 -5
- package/dist/report/diff.js.map +1 -1
- package/dist/report/retentions.d.ts +10 -0
- package/dist/report/retentions.js +21 -7
- package/dist/report/retentions.js.map +1 -1
- package/dist/report/sarif.d.ts +74 -0
- package/dist/report/sarif.js +105 -0
- package/dist/report/sarif.js.map +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,98 +1,167 @@
|
|
|
1
1
|
# isthmus
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[cartograph](https://github.com/ictechgy/cartograph)(Swift) · kartograph(Kotlin,
|
|
5
|
-
[dartograph](https://github.com/ictechgy/dartograph)(Dart)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
3
|
+
**Cross-language bridge calls in cross-platform apps, joined into one graph.**
|
|
4
|
+
[cartograph](https://github.com/ictechgy/cartograph) (Swift) · kartograph (Kotlin, planned) ·
|
|
5
|
+
[dartograph](https://github.com/ictechgy/dartograph) (Dart) each draw their own map; isthmus
|
|
6
|
+
joins them into one.
|
|
7
|
+
|
|
8
|
+
[한국어 문서](README.ko.md)
|
|
9
|
+
|
|
10
|
+
The name refers to an isthmus — the narrow strip of land that connects two landmasses. On a
|
|
11
|
+
map, it is the bridge.
|
|
12
|
+
|
|
13
|
+
## What it does, and why
|
|
14
|
+
|
|
15
|
+
Native code in a React Native or Flutter app is called from JS/Dart **by string name**:
|
|
16
|
+
`MethodChannel('com.example/camera')`, `NativeModules.CameraModule`. Compiler indexes cannot
|
|
17
|
+
see these strings. As a result:
|
|
18
|
+
|
|
19
|
+
- cartograph reports a Swift handler that Flutter calls as **unused** — a false positive
|
|
20
|
+
- per-language analysis alone struggles to catch "Dart calls `invokeMethod('takePhoto')` but
|
|
21
|
+
no Swift handler exists" **before the build** — a runtime crash
|
|
22
|
+
- for the same reason, "this channel exists in Swift but nothing in Dart calls it" is a
|
|
23
|
+
cross-boundary fact that is hard to judge from any single per-language tool
|
|
24
|
+
|
|
25
|
+
isthmus joins the **bridge facts** each language tool exports (channel names, method names,
|
|
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
|
|
28
|
+
retention evidence** — "keep Swift `CameraHandler.takePhoto`, because `lib/camera.dart:42`
|
|
29
|
+
calls it over channel `com.example/camera`".
|
|
30
|
+
|
|
31
|
+
## Status
|
|
32
|
+
|
|
33
|
+
**0.4.0.** The 0.3.0 contract and commands — the bridge-facts version 1 parser, `check`,
|
|
34
|
+
`query`, `graph`, `diff`, the external retention evidence round trip for cartograph, and
|
|
35
|
+
check baselines that suppress accepted findings by logical issue identity while preserving
|
|
36
|
+
their evidence — plus this cycle's additions: a SARIF 2.1.0 rendering of check results for
|
|
37
|
+
GitHub code scanning (`check --format sarif`, additive, with logic-key fingerprints that
|
|
38
|
+
survive source line moves), observation volume in the check summary (`observedFacts`,
|
|
39
|
+
`observedLimitations`) so a project without bridges and a run that observed nothing are no
|
|
40
|
+
longer indistinguishable, multi-caller retention evidence (`evidence.callers` with a
|
|
41
|
+
per-retention cap and an explicit `callersOmitted` count, byte-identical for single
|
|
42
|
+
callers), and usr-less `qualifiedName` identity for Objective-C handlers built without an
|
|
43
|
+
index. isthmus keeps fail-closed behavior for external input, mixed targets, graph size,
|
|
44
|
+
and the Dart/Swift Phase 0 extraction boundary. Facts that could not be joined are
|
|
45
|
+
re-counted on the consumer side, and retention subjects whose evidence cannot be built are
|
|
46
|
+
refused loudly, so neither disappears silently. Coverage gaps a receiver reports about
|
|
47
|
+
itself come back as undecidable, not as mismatches, and gap mitigation never leaks across
|
|
48
|
+
targets. Next: dogfooding it on a real Flutter app, and React Native support.
|
|
49
|
+
|
|
50
|
+
The supported producers are cartograph 0.5.3+ and dartograph 0.1.1+. Both were verified on
|
|
51
|
+
their real output, and on a Swift USR ↔ Dart invocation evidence round trip over the public
|
|
52
|
+
battery plugin.
|
|
53
|
+
|
|
54
|
+
| Document | Contents |
|
|
37
55
|
|---|---|
|
|
38
|
-
| [`docs/PRD.md`](docs/PRD.md) |
|
|
39
|
-
| [`docs/PLAN.md`](docs/PLAN.md) |
|
|
40
|
-
| [`docs/GRAPH-EXCHANGE.md`](docs/GRAPH-EXCHANGE.md) |
|
|
41
|
-
| [`docs/RESEARCH.md`](docs/RESEARCH.md) |
|
|
42
|
-
| [`experiments/phase-0/`](experiments/phase-0/) | Dart
|
|
56
|
+
| [`docs/PRD.md`](docs/PRD.md) | What, for whom, how far |
|
|
57
|
+
| [`docs/PLAN.md`](docs/PLAN.md) | Step-by-step plan. **cartograph and dartograph have prerequisite work** |
|
|
58
|
+
| [`docs/GRAPH-EXCHANGE.md`](docs/GRAPH-EXCHANGE.md) | The bridge-facts format the sister tools export — the contract shared across the sister repositories |
|
|
59
|
+
| [`docs/RESEARCH.md`](docs/RESEARCH.md) | Confirmed facts vs. unconfirmed claims |
|
|
60
|
+
| [`experiments/phase-0/`](experiments/phase-0/) | Temporary Dart/Swift extractors, pinned JSON, hand-join verification |
|
|
43
61
|
|
|
44
|
-
|
|
62
|
+
Internal documents are maintained in Korean, the maintainer's working language.
|
|
63
|
+
|
|
64
|
+
## Dependency picture
|
|
45
65
|
|
|
46
66
|
```
|
|
47
|
-
cartograph
|
|
48
|
-
kartograph
|
|
49
|
-
dartograph
|
|
50
|
-
JS/TS
|
|
67
|
+
cartograph ──bridges──┐
|
|
68
|
+
kartograph ──bridges──┼──▶ isthmus ──▶ boundary edges · mismatch reports · retention evidence
|
|
69
|
+
dartograph ──bridges──┤
|
|
70
|
+
JS/TS extractor ─bridges─┘
|
|
51
71
|
```
|
|
52
72
|
|
|
53
|
-
isthmus
|
|
73
|
+
isthmus itself is small. The heavy lifting — interpreting each language — falls to the
|
|
74
|
+
sister tools.
|
|
54
75
|
|
|
55
|
-
##
|
|
76
|
+
## Install
|
|
56
77
|
|
|
57
|
-
Node.js 22.18.0
|
|
78
|
+
Requires Node.js 22.18.0 or later.
|
|
58
79
|
|
|
59
|
-
|
|
80
|
+
Install globally and run it under the CLI name `isthmus`:
|
|
60
81
|
|
|
61
82
|
```bash
|
|
62
83
|
npm install --global isthmus-cli
|
|
63
84
|
isthmus --help
|
|
64
85
|
```
|
|
65
86
|
|
|
66
|
-
|
|
87
|
+
For a one-off run without installing, name the package explicitly:
|
|
67
88
|
|
|
68
89
|
```bash
|
|
69
90
|
npx isthmus-cli --help
|
|
70
91
|
```
|
|
71
92
|
|
|
72
|
-
`npx isthmus
|
|
93
|
+
Do not use `npx isthmus` — that installs a different package with the same name.
|
|
73
94
|
|
|
74
|
-
##
|
|
95
|
+
## Usage
|
|
75
96
|
|
|
76
|
-
isthmus
|
|
97
|
+
isthmus never runs the sister tools itself. You hand it the JSON files they produced:
|
|
77
98
|
|
|
78
99
|
```bash
|
|
79
100
|
isthmus check dart-bridges.json swift-bridges.json
|
|
80
101
|
```
|
|
81
102
|
|
|
82
|
-
|
|
103
|
+
List all commands and the installed package version:
|
|
83
104
|
|
|
84
105
|
```bash
|
|
85
106
|
isthmus --help
|
|
86
107
|
isthmus --version
|
|
87
108
|
```
|
|
88
109
|
|
|
89
|
-
CI
|
|
110
|
+
To make CI fail when bridge errors exist, add `--strict`:
|
|
90
111
|
|
|
91
112
|
```bash
|
|
92
113
|
isthmus check dart-bridges.json swift-bridges.json --strict
|
|
93
114
|
```
|
|
94
115
|
|
|
95
|
-
|
|
116
|
+
### SARIF output
|
|
117
|
+
|
|
118
|
+
To upload check results to GitHub code scanning (or any SARIF 2.1.0 consumer), ask for SARIF
|
|
119
|
+
instead of the isthmus-check JSON:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
isthmus check dart-bridges.json swift-bridges.json --format sarif > isthmus.sarif
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The default is `--format json`, which keeps the versioned isthmus-check document. SARIF is an
|
|
126
|
+
additive, isthmus-owned rendering of the same join: every issue becomes a result with its
|
|
127
|
+
`check` issue code as the rule id, the first evidence endpoint as the primary location
|
|
128
|
+
(project-relative paths become percent-encoded, repository-relative URIs), remaining
|
|
129
|
+
endpoints as related locations, and baseline-suppressed issues carry an `external`
|
|
130
|
+
suppression. Results include a
|
|
131
|
+
`partialFingerprints` hash of the logical issue identity (code, target, channel, method), so
|
|
132
|
+
deduplication survives source line moves exactly like baseline suppression. `--strict`,
|
|
133
|
+
`--baseline`, and `--update-baseline` combine with either format and keep their documented
|
|
134
|
+
exit-code behavior.
|
|
135
|
+
|
|
136
|
+
### Baselines
|
|
137
|
+
|
|
138
|
+
To accept the current findings as a baseline, write them to a file once and apply that file
|
|
139
|
+
from the next run onward:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
isthmus check dart-bridges.json swift-bridges.json --update-baseline isthmus-baseline.json
|
|
143
|
+
isthmus check dart-bridges.json swift-bridges.json --strict --baseline isthmus-baseline.json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`--update-baseline` does not suppress the run that writes it; it rewrites the whole file — an
|
|
147
|
+
isthmus-owned `isthmus-baseline` version 1 document — from the current issues, so resolved
|
|
148
|
+
items drop out automatically. `--baseline` suppresses only the issues whose logical identity
|
|
149
|
+
(code, target, channel, method) matches an entry, so moving source lines never breaks
|
|
150
|
+
suppression and a new channel or method mismatch is never suppressed. Suppressed issues are
|
|
151
|
+
not deleted: they keep their facts, evidence, and severity, gain a `suppressed` marker, and
|
|
152
|
+
are excluded only from the summary error/warning counts and the `--strict` decision. Entries
|
|
153
|
+
that match no issue are counted in `staleBaselineEntries`, so a baseline hiding a future
|
|
154
|
+
regression stays visible in the report. An unreadable, non-JSON, or contract-violating
|
|
155
|
+
baseline file fails with exit code 2. The two flags cannot be combined in one run, and a value
|
|
156
|
+
starting with `-` is rejected (including legitimate file names that start with `-`). `--update-baseline` combined
|
|
157
|
+
with `--strict` still writes the file, and the exit code follows that run's unsuppressed
|
|
158
|
+
errors. If more than 10,000 entries would be recorded, the command fails with exit code 2
|
|
159
|
+
instead of leaving an artifact it cannot consume. Writes go through a temporary file in the
|
|
160
|
+
same directory (atomic rename), so an interrupted run cannot corrupt an existing baseline.
|
|
161
|
+
|
|
162
|
+
### Retention evidence
|
|
163
|
+
|
|
164
|
+
To return matched Swift handlers to cartograph as retention evidence:
|
|
96
165
|
|
|
97
166
|
```bash
|
|
98
167
|
isthmus retentions \
|
|
@@ -102,18 +171,34 @@ isthmus retentions \
|
|
|
102
171
|
cartograph dead --external-retentions external-retentions.json
|
|
103
172
|
```
|
|
104
173
|
|
|
105
|
-
`retentions
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
174
|
+
`retentions` prefers each handler's USR and falls back to its `qualifiedName`. When a method is
|
|
175
|
+
invoked from several caller locations, the evidence carries all of them in `callers` (the
|
|
176
|
+
representative first `caller` stays for older consumers) and counts any entries beyond the
|
|
177
|
+
100-per-retention cap in `callersOmitted` instead of dropping them silently. A
|
|
178
|
+
`mixed-targets` document cannot have per-fact targets restored in v1, so every consuming
|
|
179
|
+
command defers the join with exit code 2; split such a document per target at production time
|
|
180
|
+
first.
|
|
181
|
+
|
|
182
|
+
cartograph retains Swift symbols only, so `--for cartograph` requires at least one
|
|
183
|
+
receiver-side Swift document and refuses with exit code 2 instead of emitting an empty
|
|
184
|
+
retention document. If a matched Swift handler has callers but no `symbol`, and therefore
|
|
185
|
+
cannot become evidence, the command fails with the same code rather than producing a partial
|
|
186
|
+
document: a retention file with missing evidence makes live handlers look unused to the
|
|
187
|
+
consumer.
|
|
188
|
+
|
|
189
|
+
Every consuming command requires at least one caller-side (dart) and one receiver-side (swift)
|
|
190
|
+
platform document. Given only one side, it refuses with exit code 2 rather than misreading a
|
|
191
|
+
one-sided observation as a boundary mismatch. Input failure messages state the cause (read
|
|
192
|
+
failure, JSON error, exchange contract violation, project mismatch, missing platform
|
|
193
|
+
composition, size limit), the input position, and how to resolve it, and never expose input
|
|
194
|
+
bodies or paths.
|
|
195
|
+
|
|
196
|
+
To verify the whole path from production to consumption on a real public Flutter plugin, run
|
|
197
|
+
this from the repository root. The script does a sparse checkout of a pinned `plus_plugins`
|
|
198
|
+
commit, confirms the retention evidence for three methods in the battery plugin's original
|
|
199
|
+
Dart and Swift sources, and removes the temporary checkout. It needs network access, Git
|
|
200
|
+
2.26+, Swift 6, cartograph 0.5.3+, and dartograph 0.1.1+. isthmus is rebuilt from the current
|
|
201
|
+
sources automatically; a third argument can point at a separate isthmus JavaScript artifact.
|
|
117
202
|
|
|
118
203
|
```bash
|
|
119
204
|
npm run build
|
|
@@ -122,19 +207,22 @@ node scripts/verify-public-flutter-plugin.mjs \
|
|
|
122
207
|
/path/to/dartograph
|
|
123
208
|
```
|
|
124
209
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
210
|
+
The public plugin verification checks the Swift USRs from the original `addMethodCallDelegate`
|
|
211
|
+
implementation and the three original Dart call sites, and verifies that cartograph
|
|
212
|
+
`--explain` reads the representative evidence for those symbols. It does not force a
|
|
213
|
+
dead-state transition on an already-public plugin handler; that transition and the
|
|
214
|
+
`setMethodCallHandler` path are covered separately by the synthetic corpus in
|
|
215
|
+
`verify-cartograph-roundtrip.mjs`.
|
|
130
216
|
|
|
131
|
-
|
|
217
|
+
### Query and graph
|
|
218
|
+
|
|
219
|
+
To see which locations on the other side of the boundary a channel or method connects to:
|
|
132
220
|
|
|
133
221
|
```bash
|
|
134
222
|
isthmus query takePhoto dart-bridges.json swift-bridges.json
|
|
135
223
|
```
|
|
136
224
|
|
|
137
|
-
|
|
225
|
+
To emit only the boundary edges as JSON, Graphviz DOT, or Mermaid:
|
|
138
226
|
|
|
139
227
|
```bash
|
|
140
228
|
isthmus graph dart-bridges.json swift-bridges.json
|
|
@@ -142,82 +230,103 @@ isthmus graph dart-bridges.json swift-bridges.json --format dot
|
|
|
142
230
|
isthmus graph dart-bridges.json swift-bridges.json --format mermaid
|
|
143
231
|
```
|
|
144
232
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
`graph
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
- `
|
|
156
|
-
- `
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
233
|
+
When the same method exists on several channels, `query` returns candidates instead of picking
|
|
234
|
+
one; feed a returned `qualifiedName` back into the same subject position to disambiguate.
|
|
235
|
+
`graph` emits matched edges only and preserves the input `limitations` as a JSON field or as
|
|
236
|
+
DOT/Mermaid comments. If the Cartesian product of evidence would exceed 100,000 edges, the
|
|
237
|
+
command fails with exit code 2 to prevent a memory blowup.
|
|
238
|
+
|
|
239
|
+
### What check reports
|
|
240
|
+
|
|
241
|
+
The output is `isthmus-check` version 1 JSON, reporting these facts:
|
|
242
|
+
|
|
243
|
+
- `unhandled-invocation` (error): an invocation exists but no native handler does
|
|
244
|
+
- `unregistered-channel-creation` (error): a caller-side channel creation exists but no
|
|
245
|
+
native registration does
|
|
246
|
+
- `registration-without-creation` (warning): a native channel registration exists but no
|
|
247
|
+
caller-side creation does
|
|
248
|
+
- `handler-without-invocation` (warning): a native handler exists but no caller-side use does
|
|
249
|
+
- `unhandled-invocation-unverified` (warning): the same fact as the first item, but the
|
|
250
|
+
receiver reported that it may have missed handlers, so "absent" and "not seen" cannot be
|
|
251
|
+
distinguished
|
|
252
|
+
- `unregistered-channel-creation-unverified` (warning): registration undecidable for the same
|
|
253
|
+
reason
|
|
254
|
+
|
|
255
|
+
The `summary` carries the issue counts plus observation volume: `observedFacts` is the total
|
|
256
|
+
number of facts across all input documents and `observedLimitations` counts the reported
|
|
257
|
+
analysis limitations. This keeps a project with no bridges and a run that observed nothing
|
|
258
|
+
from producing indistinguishable reports — an `observedFacts` of 0 means the producers saw
|
|
259
|
+
nothing to describe.
|
|
260
|
+
|
|
261
|
+
The `-unverified` kinds come from limitations in receiver-side documents. For example, in a
|
|
262
|
+
plugin whose Flutter handler is written in Objective-C, cartograph reports
|
|
263
|
+
`objective-c-sources:` and may fail to enumerate the handler facts completely. Asserting
|
|
264
|
+
"unhandled invocation" as an error there would recreate the very false positive this tool
|
|
265
|
+
exists to remove. The facts and evidence are still reported, but `--strict` does not fail.
|
|
266
|
+
Gap kinds are distinguished: a channel registration with a non-literal name downgrades channel
|
|
267
|
+
diagnostics only, never method diagnostics. The mitigation unit is the diagnostic's target —
|
|
268
|
+
facts join per target only, so a gap reported by another target's receiver document never
|
|
269
|
+
downgrades the current target's diagnostics, and a gap from a receiver document with no facts
|
|
270
|
+
applies to all targets because nothing can be attributed. Caller-side limitations never hide
|
|
271
|
+
native code, so they do not affect severity, and unknown limitation wording is never
|
|
272
|
+
interpreted as a gap.
|
|
273
|
+
|
|
274
|
+
Since 0.2.0, isthmus reads the optional v1 `limitationScopes`. `{ limitationIndex, channels }`
|
|
275
|
+
is a conservative channel upper bound for that limitation as a whole — never merely a list of
|
|
276
|
+
literals that happened to be found. Without scopes, or when a scoped entry coexists with
|
|
277
|
+
another unscoped gap, the existing whole-target mitigation stands. An empty channel set or an
|
|
278
|
+
invalid index is an input error. Scopes are preserved as `channels` through
|
|
279
|
+
check/query/graph/diff. A producer's tool name alone never drives mitigation: `unjoined-*`
|
|
280
|
+
counts mitigate only when they carry the consumer-attached `origin: "consumer"`.
|
|
281
|
+
|
|
282
|
+
The optional fact field `sourceLanguage: "objective-c"` distinguishes ObjC implementations in
|
|
283
|
+
`.m`/`.mm` files from the Swift graph. These facts carry no symbol. Their matches stay in
|
|
284
|
+
check/query/graph but are excluded from the Swift retention list, and
|
|
285
|
+
`omittedObjectiveCHandlers` reports how many were excluded. A matched Swift handler that has
|
|
286
|
+
callers but no `symbol` and no `sourceLanguage` marker still fails with exit code 2.
|
|
287
|
+
Consumers supporting this extension must ship before producers: old consumers drop scopes
|
|
288
|
+
(mitigating broadly) and fail to produce ObjC retentions.
|
|
289
|
+
|
|
290
|
+
Every issue carries its observed locations as `evidence`. Dynamic names, unresolved receivers
|
|
291
|
+
or handler bodies, missing USRs, input generation-time differences, and mixed targets stay in
|
|
292
|
+
`limitations` with their provenance. This tool never decides whether code is safe to delete.
|
|
293
|
+
|
|
294
|
+
isthmus output documents treat added fields and new issue codes within version 1 as compatible
|
|
295
|
+
changes; the document version is raised only when an existing field's meaning changes or the
|
|
296
|
+
field is removed.
|
|
297
|
+
|
|
298
|
+
`limitations` holds both producer-reported and isthmus-counted limitations. Each entry states
|
|
299
|
+
its provenance and attribution with `platform`, `target`, and `tool`; entries with
|
|
300
|
+
`origin: "consumer"` were observed at the join stage. Facts that could not be joined are
|
|
301
|
+
re-counted per platform and target, regardless of what producers reported or how many they
|
|
302
|
+
reported:
|
|
303
|
+
|
|
304
|
+
- `unjoined-dynamic-channels`: channel create/register facts with a non-literal name
|
|
305
|
+
- `unjoined-dynamic-methods`: invoke/handle facts with a non-literal name
|
|
306
|
+
- `unjoined-unattributed-handlers`: handler facts that belong to no known channel
|
|
307
|
+
|
|
308
|
+
Duplicate facts at the same location are counted once. A handler that is both dynamic and
|
|
309
|
+
unattributed is counted as dynamic only.
|
|
310
|
+
|
|
311
|
+
| Exit code | Meaning |
|
|
204
312
|
|---|---|
|
|
205
|
-
| `0` |
|
|
206
|
-
| `1` | `--strict
|
|
207
|
-
| `2` |
|
|
208
|
-
| `64` |
|
|
313
|
+
| `0` | Success. In default mode, issues are reported but do not fail the run |
|
|
314
|
+
| `1` | `--strict` found error issues (for `diff`: newly observed errors only). `-unverified` warnings and baseline-suppressed errors do not fail |
|
|
315
|
+
| `2` | Tool failure: file read, JSON, exchange contract, project mismatch, missing platform composition, deferred join, size limits (input text, graph edges, baseline entries), baseline file or write errors, retention evidence that cannot be built. stderr distinguishes the cause |
|
|
316
|
+
| `64` | Bad command, option, or input count; or `query` `notFound`/`ambiguous` |
|
|
209
317
|
|
|
210
|
-
|
|
211
|
-
|
|
318
|
+
For development from a checkout, run `npm ci` first. Development verification runs the type
|
|
319
|
+
check and a clean build, enforces 90% product-code coverage, and exercises the real CLI and
|
|
320
|
+
package contract checks together:
|
|
212
321
|
|
|
213
322
|
```bash
|
|
214
323
|
npm run verify
|
|
215
324
|
```
|
|
216
325
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
326
|
+
To verify the external retention round trip with the two real producers, pass cartograph
|
|
327
|
+
0.5.3+, the dartograph binary, and a fixture root both tools can analyze. This needs producer
|
|
328
|
+
binaries and a compiler index, so it is not part of `npm run verify` or public CI; run it
|
|
329
|
+
manually before a release.
|
|
221
330
|
|
|
222
331
|
```bash
|
|
223
332
|
node scripts/verify-cartograph-roundtrip.mjs \
|
|
@@ -226,9 +335,24 @@ node scripts/verify-cartograph-roundtrip.mjs \
|
|
|
226
335
|
/path/to/FalsePositiveCorpus
|
|
227
336
|
```
|
|
228
337
|
|
|
229
|
-
|
|
338
|
+
To verify that producer-emitted `limitationScopes` reach the consumer and relax diagnostics at
|
|
339
|
+
channel granularity, run the self-contained scope dogfood. It synthesizes a minimal Swift
|
|
340
|
+
package with a delegated handler registration — a literal channel whose handler body the
|
|
341
|
+
scanner cannot inspect, the one shape whose channel upper bound is provable — plus a Dart
|
|
342
|
+
caller, and checks that only the scoped channel's unhandled invocation becomes an unverified
|
|
343
|
+
warning while an adjacent unhandled invocation and an unregistered channel creation stay
|
|
344
|
+
errors. It needs cartograph 0.9.0+, dartograph 0.1.1+, and Swift 6, and performs no network
|
|
345
|
+
access.
|
|
230
346
|
|
|
231
|
-
|
|
347
|
+
```bash
|
|
348
|
+
node scripts/verify-limitation-scopes.mjs \
|
|
349
|
+
/path/to/cartograph \
|
|
350
|
+
/path/to/dartograph
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Comparing before and after a change (0.1.4+)
|
|
354
|
+
|
|
355
|
+
To compare the Dart and Swift exchange files of one project before and after a change:
|
|
232
356
|
|
|
233
357
|
```bash
|
|
234
358
|
isthmus diff \
|
|
@@ -236,34 +360,38 @@ isthmus diff \
|
|
|
236
360
|
--after after-dart.json after-swift.json --strict
|
|
237
361
|
```
|
|
238
362
|
|
|
239
|
-
`isthmus-diff` v1
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
`
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
[
|
|
363
|
+
`isthmus-diff` v1 outputs, as JSON: added and removed logical method connections, newly
|
|
364
|
+
observed and no-longer-observed issues, the analysis limitations at both snapshots and their
|
|
365
|
+
difference, and producer versions and generation times. Connections include caller and handler
|
|
366
|
+
locations. Line moves do not count as connection changes, and renames are never inferred.
|
|
367
|
+
Caller/handler replacement under the same logical key, and per-call-site additions or
|
|
368
|
+
removals, are outside this comparison.
|
|
369
|
+
|
|
370
|
+
`--strict` exits 1 only when a newly observed error exists. With only pre-existing errors,
|
|
371
|
+
warnings, or limitations, it exits 0, so a success code never means "safe to delete" or "fully
|
|
372
|
+
analyzed". `resolvedIssues` likewise means a previous mismatch is no longer observed — check
|
|
373
|
+
the limitations to see whether a dynamic transition or an extractor change caused it.
|
|
374
|
+
`--strict` is recognized at any argument position and cannot be given more than once.
|
|
375
|
+
|
|
376
|
+
`diff` currently accepts only Flutter Dart/Swift documents. Both platforms are required at
|
|
377
|
+
each point in time, and the two snapshots must agree on `project` and on the per-platform,
|
|
378
|
+
per-tool document counts. Build each revision from the same checkout path and keep the JSON.
|
|
379
|
+
Do not compare a partial extraction against a full one; use the same analysis settings. Input
|
|
380
|
+
files are capped at 256 total, and the text size limits match the rest of the CLI. Mixed
|
|
381
|
+
targets or incomparable inputs are refused with exit code 2. `generatedAt` is the fact
|
|
382
|
+
extraction time, not an indicator of revision order — the comparison direction comes from the
|
|
383
|
+
`--before` and `--after` arguments, so you must point them at the right revisions.
|
|
384
|
+
|
|
385
|
+
## Coding-agent skill
|
|
386
|
+
|
|
387
|
+
[`Skills/isthmus/SKILL.md`](Skills/isthmus/SKILL.md) provides a skill that teaches agents to
|
|
388
|
+
check other languages' callers with `query` before deleting or renaming a native bridge
|
|
389
|
+
handler. Copy it into your agent's project skill directory.
|
|
390
|
+
|
|
391
|
+
Codex discovers the same text through the `.agents/skills/isthmus` link in this checkout.
|
|
392
|
+
Edit only `Skills/isthmus/SKILL.md`; the npm package includes it. Skill content verification
|
|
393
|
+
and per-model tuning rationale are in the [agent audit record](docs/AGENT-AUDIT.md).
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
[MIT](LICENSE). Free forever, including commercial use.
|
package/Skills/isthmus/SKILL.md
CHANGED
|
@@ -21,6 +21,13 @@ If inputs are missing, identify the required files and proceed with independent
|
|
|
21
21
|
|
|
22
22
|
## Choose the requested operation
|
|
23
23
|
|
|
24
|
+
- Audit the boundary: `isthmus check <dart.json> <swift.json> [--strict]`.
|
|
25
|
+
Read `summary`, each `issues[].code/severity/evidence`, and `limitations`.
|
|
26
|
+
`-unverified` codes are undecidable findings, not clean results.
|
|
27
|
+
To accept current findings, run once with `--update-baseline <file>`; apply it
|
|
28
|
+
later with `--baseline <file>`. Suppressed issues keep their evidence and only
|
|
29
|
+
leave the summary counts and `--strict` failures; report `staleBaselineEntries`
|
|
30
|
+
as resolved items to prune on the next update.
|
|
24
31
|
- Trace callers: `isthmus query <channel-or-method> <dart.json> <swift.json>`.
|
|
25
32
|
Read `usedBy`, `dependsOn`, symbol evidence, and `limitations`.
|
|
26
33
|
`ambiguous` returns qualified-name candidates; disambiguate from context or ask when necessary.
|