@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 +21 -0
- package/PROVENANCE.md +57 -0
- package/README.md +244 -0
- package/bin/crosscheck.mjs +426 -0
- package/fixtures/calibration/PREREGISTERED.md +522 -0
- package/fixtures/calibration/expected.json +144 -0
- package/fixtures/calibration/src/session.js +123 -0
- package/foreman.md +140 -0
- package/lenses/architect.md +92 -0
- package/lenses/check.md +87 -0
- package/lenses/security-check.md +105 -0
- package/lenses/taint.md +102 -0
- package/lenses/ux.md +105 -0
- package/lib/baseline.mjs +78 -0
- package/lib/calibrate.mjs +169 -0
- package/lib/corpus.mjs +340 -0
- package/lib/lenses.mjs +200 -0
- package/lib/merge.mjs +310 -0
- package/lib/parse.mjs +96 -0
- package/lib/prompt.mjs +85 -0
- package/lib/run.mjs +129 -0
- package/lib/sarif.mjs +176 -0
- package/package.json +53 -0
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).
|