@applesnort/crosscheck 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joel Mangin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/PROVENANCE.md ADDED
@@ -0,0 +1,57 @@
1
+ # Provenance
2
+
3
+ This file records where the content in this repository came from, so that anyone
4
+ reusing it can see what it derives from.
5
+
6
+ ## The lens definitions
7
+
8
+ `lenses/architect.md`, `lenses/check.md`, `lenses/security-check.md`, and
9
+ `lenses/ux.md` were written from scratch on 2026-08-05.
10
+
11
+ They were composed from the stated purpose of each lens and from the named public
12
+ standards cited inside each file — OWASP Top 10 (2021) and ASVS for
13
+ `security-check`, Nielsen's ten usability heuristics for `ux`, and explicit
14
+ first-party criteria for `architect` and `check`. Citing a standard by identifier
15
+ is not reuse of its text; no text from any standard is reproduced here.
16
+
17
+ Earlier, personal versions of these four lenses existed in the author's local
18
+ tooling. Their origin could not be established from available records, so they were
19
+ deliberately **not** consulted while writing the versions in this repository, and
20
+ no text from them carries forward. That is the reason these files exist as a fresh
21
+ composition rather than as a cleanup of the originals.
22
+
23
+ ## The foreman and the library
24
+
25
+ `foreman.md` describes the dispatch, verification, merge, and reporting method. It
26
+ originates with the author, developed through practical use across 2026. It is not
27
+ derived from a third-party source.
28
+
29
+ `lib/`, `bin/`, `test/`, and `fixtures/` were written from scratch on 2026-08-05
30
+ and have no third-party origin. They carry no dependencies, so nothing is
31
+ vendored, and no code was adapted from another project.
32
+
33
+ `lib/sarif.mjs` targets SARIF 2.1.0 as specified by OASIS. It implements the
34
+ format against the published specification; no text or code from the
35
+ specification is reproduced here. The two panel-specific additions —
36
+ `properties.consensusScore` and the `crosscheck/*` notification descriptors — are
37
+ this project's own, placed in the extension points the format provides for that
38
+ purpose.
39
+
40
+ The general idea of using multiple critic personas to review code is **not**
41
+ original to this project and is not claimed as such. Comparable prior work
42
+ includes Claude Code's built-in parallel code review, community multi-agent review
43
+ panels, and the long-standing static-analysis practice of aggregating several
44
+ tools and treating agreement between them as a confidence signal. What this
45
+ repository offers is a specific, documented method — not a novel concept.
46
+
47
+ ## Deliberately not included
48
+
49
+ - **An accessibility lens.** The author's personal version derives from the
50
+ GOV.UK accessibility personas (Ashleigh, Claudia, Ron), which are published by
51
+ the Government Digital Service under the Open Government Licence v3.0. Reuse is
52
+ permitted with attribution; rather than carry that obligation here, the lens is
53
+ omitted. Anyone adding one should use the GOV.UK personas directly and attribute
54
+ them under OGL v3.
55
+ - **Domain-specific lenses.** Lenses encoding a particular product's domain, or
56
+ a particular named colleague's review preferences, are project-local by nature
57
+ and are not published.
package/README.md ADDED
@@ -0,0 +1,244 @@
1
+ # crosscheck
2
+
3
+ Run several independent review lenses over the same change in parallel, merge their
4
+ findings into one deduped report, and emit it as **SARIF** — so LLM review findings
5
+ land in the same places static-analysis findings already do.
6
+
7
+ ```bash
8
+ # run a panel: crosscheck builds the prompts and merges the results,
9
+ # your --exec command supplies the model
10
+ npx @applesnort/crosscheck run lib/ --exec 'claude -p' --sarif panel.sarif
11
+
12
+ # or merge output a panel already produced
13
+ npx @applesnort/crosscheck report --in run.json
14
+ ```
15
+
16
+ **crosscheck never talks to a model itself.** `--exec` names any command that takes
17
+ one lens prompt on stdin and returns findings on stdout — `claude -p`, `llm -m ...`,
18
+ or your own wrapper. crosscheck owns prompt construction, routing, fan-out, dedupe,
19
+ and output; you own the model. `--dry-run` prints the roster and prompts without
20
+ spawning anything.
21
+
22
+ No dependencies, no install step, 153 tests.
23
+
24
+ > **v0.x — the API is unstable.** The CLI commands and the `lib/` exports may
25
+ > change shape before 1.0. Pin an exact version if you depend on it.
26
+ >
27
+ > Published as `@applesnort/crosscheck`; npm rejects the unscoped name as too
28
+ > similar to the (abandoned) `cross-check`. Installed, the command is
29
+ > `crosscheck`.
30
+
31
+ ## SARIF output
32
+
33
+ Findings are emitted as [SARIF 2.1.0](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html),
34
+ the OASIS interchange format static analyzers already speak. Every other
35
+ multi-persona review panel emits prose for a human to read once. This one produces
36
+ a document GitHub code scanning, editor problem panels, and security dashboards
37
+ already know how to ingest — none of them needing to know a model wrote it.
38
+
39
+ - One **rule per lens**, carrying the standards that lens cites, so consumers can
40
+ filter and configure by lens.
41
+ - `partialFingerprints` for stable identity across runs, independent of severity,
42
+ fix text, and ordering.
43
+ - Lens attribution and consensus score ride in `properties`, since SARIF has no
44
+ native concept for either.
45
+ - A lens that died sets `executionSuccessful: false` and emits a
46
+ `toolExecutionNotification`. The gap is machine-readable, not a line of prose a
47
+ dashboard will drop.
48
+
49
+ That last point is the design rule throughout: **every omission is disclosed.** A
50
+ lens skipped for irrelevance, a lens whose agent died, and a finding refuted during
51
+ verification are three different things and must read differently. A partial panel
52
+ presented as a complete one is worse than no panel.
53
+
54
+ ## Why lenses instead of one review pass
55
+
56
+ A single review pass optimizes for one kind of defect at a time. Ask for "problems"
57
+ and you get whichever category the model reaches for first. Ask several narrow
58
+ specialists — each told explicitly what it does *not* own — and the union covers
59
+ more ground, because none of them is trading correctness findings against usability
60
+ findings inside one context.
61
+
62
+ The panel is a foreman, not a reviewer. It resolves the target, decides which lenses
63
+ are relevant, dispatches them, and synthesizes what comes back. The lenses do the
64
+ looking; the deterministic half — parse, dedupe, score, emit — is code with tests
65
+ rather than prompt instructions. See [`foreman.md`](foreman.md).
66
+
67
+ ## Measuring whether your lenses are redundant
68
+
69
+ Most implementations stop at "N agents agreed." That over-credits lenses whose
70
+ remits overlap: two lenses looking at the same things agreeing tells you less than
71
+ two that do not. So overlap is **measured** from a real run rather than assumed:
72
+
73
+ ```bash
74
+ crosscheck overlap --in run.json --out overlap.json # measure it
75
+ crosscheck report --in run.json --overlap overlap.json
76
+ ```
77
+
78
+ This has produced discriminating results in both directions on real data. The same
79
+ two lenses — written with deliberately opposed methods, a CWE taxonomy walk and
80
+ sink-first flow tracing — measured **0.45** overlap against 66 OWASP Benchmark
81
+ cases and **1.0** against a 20-case corpus where they returned byte-for-byte
82
+ identical detections.
83
+
84
+ At 1.0, agreement is scored as **one** effective confirmation rather than two,
85
+ because that is what it is worth. That is a result you can act on: drop the
86
+ redundant lens, or replace it with one that fails differently.
87
+
88
+ Findings are ranked by those effective confirmations — 1 for a single lens, and for
89
+ a set, 1 plus the summed independence of each distinct pair. **Whether that ranking
90
+ predicts correctness is unproven**; six calibration rounds could not test it,
91
+ because the lenses almost never erred. It is kept as a documented hypothesis rather
92
+ than a validated feature — [the full record is here](fixtures/calibration/PREREGISTERED.md),
93
+ including every failed prediction.
94
+
95
+ ### Matching is fuzzy, because real lens output is
96
+
97
+ Two lenses never phrase a defect identically, and they anchor it on different lines.
98
+ In one measured run, three lenses each found the same swallowed error and cited it
99
+ at lines **54, 56, and 57**. An exact-match merge reports that as three findings and
100
+ zero agreement.
101
+
102
+ So findings cluster on line proximity (±3) **plus** issue similarity — a Jaccard
103
+ index over content words, thresholded at `0.12`. Both defaults come from
104
+ measurement: same-defect pairs scored `0.161`–`0.538`, while different defects
105
+ sharing a line scored `0.038`–`0.050`. The threshold sits in that gap.
106
+ `test/merge-realdata.test.mjs` pins both bands using verbatim lens output, so they
107
+ cannot drift unnoticed.
108
+
109
+ ## Baselines
110
+
111
+ On an existing codebase the first run returns everything already wrong, and the
112
+ report gets closed unread. Record it, then report only what changed:
113
+
114
+ ```bash
115
+ crosscheck baseline --in first-run.json --out .crosscheck-baseline.json
116
+ crosscheck report --in run.json --baseline .crosscheck-baseline.json
117
+ ```
118
+
119
+ Suppressed counts are always reported, and baseline entries that stopped appearing
120
+ are flagged — either they were fixed, or a lens quietly stopped running. A baseline
121
+ that hides its own size is just a way to declare a codebase's problems normal.
122
+
123
+ ## Calibration
124
+
125
+ A review panel is otherwise unfalsifiable: you cannot tell whether it works, whether
126
+ a new lens helped, or whether a prompt edit made it worse.
127
+
128
+ ```bash
129
+ crosscheck calibrate --in run.json --expected fixtures/calibration/expected.json
130
+ ```
131
+
132
+ reports recall, precision, per-lens recall against only the defects that lens owns,
133
+ severity agreement, and consensus precision beside single-lens precision. It exits
134
+ non-zero when a planted defect was missed, so it works as a CI gate on the panel
135
+ itself. `lib/corpus.mjs` scores externally authored corpora at case level, and
136
+ `scripts/fetch-corpus.sh` pulls one without vendoring it.
137
+
138
+ Six rounds have been run and recorded — two self-authored fixtures, an external
139
+ corpus of 2,740 labeled cases, two model tiers, a purpose-built deception corpus,
140
+ and a controlled prompt ablation. The results, the criteria fixed in advance of each
141
+ round, every failed prediction, and one methodological error that voided a round are
142
+ all in [`fixtures/calibration/PREREGISTERED.md`](fixtures/calibration/PREREGISTERED.md).
143
+
144
+ The short version: the lenses were nearly always right, which is why the consensus
145
+ claim above remains untested. The one false positive across all six rounds was
146
+ produced by **both** lenses at once — both had to resolve a single opaque helper and
147
+ both inferred its behaviour from its name. Independence of *method* does not give
148
+ independence of *failure*.
149
+
150
+ ## What's here
151
+
152
+ ```
153
+ foreman.md the dispatch / verify / merge / report method
154
+ lenses/
155
+ architect.md structure, data shape, reversibility
156
+ check.md correctness — boundaries, absent values, error paths
157
+ security-check.md OWASP-framed application security
158
+ taint.md sink-first data flow to a dangerous operation
159
+ ux.md usability under interruption and extreme states
160
+ lib/
161
+ parse.mjs lens text -> findings
162
+ merge.mjs normalize, dedupe, consensus scoring
163
+ sarif.mjs SARIF 2.1.0 writer
164
+ baseline.mjs baseline record / filter / staleness
165
+ lenses.mjs frontmatter, glob routing, roster validation
166
+ calibrate.mjs score a run against planted defects
167
+ corpus.mjs external corpora, case-level scoring
168
+ lib/
169
+ prompt.mjs lens prompt construction
170
+ run.mjs roster planning and bounded fan-out
171
+ bin/crosscheck.mjs CLI: run | report | sarif | baseline | overlap | calibrate
172
+ fixtures/calibration/ planted defects, ground truth, and the calibration record
173
+ fixtures/deception/ 20 modules that look safe and are not, or the reverse
174
+ PROVENANCE.md where all of this came from
175
+ ```
176
+
177
+ Every lens shares one output contract, so the merge needs no per-lens parsing:
178
+
179
+ ```
180
+ file:line — SEVERITY — issue — fix
181
+ ```
182
+
183
+ `SEVERITY` is `BLOCK`, `FIX`, or `CONSIDER`; other vocabularies normalize onto it. A
184
+ lens with nothing to say returns exactly `NO FINDINGS` — deliberately distinct from
185
+ a lens that failed to run. Lines that do not match the contract are reported as
186
+ unparsed rather than discarded, because a lens that starts narrating instead of
187
+ reporting should not look like a clean one.
188
+
189
+ Routing is declared in each lens's frontmatter (`when`, `owns`, `not-owns`), so a
190
+ roster validates before any agent is dispatched. `not-owns` is required: a lens that
191
+ never declines dilutes the signal everything else depends on.
192
+
193
+ > **Note:** `fixtures/deception/src/` contains deliberately exploitable code and
194
+ > helpers that behave differently from what their names promise. It exists to test
195
+ > reviewers. Do not copy any of it into real software.
196
+
197
+ ## Using it
198
+
199
+ The lenses are plain markdown prompts — nothing is tied to a particular agent
200
+ framework. Any harness that can run N prompts concurrently and collect their text
201
+ can drive this; feed the results in as `[{"lens": "check", "output": "..."}]`, with
202
+ `null` for a lens that died.
203
+
204
+ `crosscheck run` does this for you, bounded by `--concurrency`, and writes the raw
205
+ lens text with `--out` so a run can be rescored later without paying the model
206
+ again. Routing comes from each lens's `when` globs; `--only` and `--skip` override
207
+ it, and every skip is reported with its reason.
208
+
209
+ One property worth preserving if you build your own dispatcher: **dispatch out of
210
+ band.** A parallel fan-out that renders inline floods the session you are working
211
+ in and has to be killed to recover it.
212
+
213
+ ```bash
214
+ npm test # 153 tests, no dependencies
215
+ ```
216
+
217
+ ## Adding a lens
218
+
219
+ A lens earns its place by finding what the others miss. Give it a remit narrow
220
+ enough that it declines most changes, state what it does *not* own, and make it name
221
+ the concrete trigger for every finding. Then measure it: add defects it should catch
222
+ to the calibration fixture and check whether recall actually moved, and check the
223
+ overlap figure to see whether it is telling you anything the existing lenses were
224
+ not.
225
+
226
+ If a lens encodes your product's domain, your storage conventions, or a particular
227
+ reviewer's standards, keep it in your own project. The ones here are deliberately
228
+ generic; the useful ones usually aren't.
229
+
230
+ ## Prior art
231
+
232
+ Multiple critic personas reviewing code is not a new idea and is not claimed as one.
233
+ Claude Code ships a parallel multi-agent code review; community multi-agent review
234
+ panels exist; aggregating several analyzers and weighting their agreement is
235
+ long-standing practice, formalized in SARIF.
236
+
237
+ What is offered here is narrower: LLM review lenses as a **SARIF producer**, with
238
+ lens redundancy measured rather than assumed, and a calibration harness that reports
239
+ what it cannot establish as readily as what it can. See
240
+ [`PROVENANCE.md`](PROVENANCE.md).
241
+
242
+ ## License
243
+
244
+ MIT — see [`LICENSE`](LICENSE).