@geonosis/lint-parity 2.3.1 → 2.4.3

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/CHANGELOG.md ADDED
@@ -0,0 +1,408 @@
1
+ # @geonosis/lint-parity
2
+
3
+ ## 2.4.3
4
+
5
+ ## 2.4.2
6
+
7
+ ## 2.4.1
8
+
9
+ ## 2.4.0
10
+
11
+ ## 2.3.1
12
+
13
+ ## 2.3.0
14
+
15
+ ### Minor Changes
16
+
17
+ - 3dac9a7: **A rule a config turns off is out of the corpus's scope (#16).** `rulesNamedBy` took every key and
18
+ ignored severity, so three rules read as "fire nowhere" under a consumer's config where the doctor,
19
+ which reads severity, said one. A rule is in scope when some layer — base block or `overrides` entry
20
+ — leaves it running; a rule every layer sets to `"off"` (or `0`) is excluded, counted in the report
21
+ and named. `rulesTurnedOffBy` is the new door for the excluded list.
22
+ - 3dac9a7: **One `--since`, and a report a machine can read (#97).** `--since` names a plan number, a day and
23
+ a git ref in three commands, and all three took any string: `plan check --since garbage` exempted
24
+ nothing and printed the verdict it would have printed with no flag, `stats --since notadate` put
25
+ every recorded day outside the window and printed `0 turns` as a pass rate. One reader now declares
26
+ which kind each means and refuses an unreadable value by name. `geonosis-ratchet`,
27
+ `geonosis-verify-arch` and `geonosis-lint-parity` take `--json`: the same report, on stdout,
28
+ whatever the verdict — `--json` with an explicit `--format` is refused rather than resolved.
29
+
30
+ ### Patch Changes
31
+
32
+ - 3dac9a7: **A config oxlint would not parse is refused by name, never read as zero (#310).** Measured on
33
+ oxlint 1.80: a config naming a rule the loaded plugin does not export, or passing options to a rule
34
+ whose `schema` is `[]`, prints `Failed to parse oxlint configuration file`, exits 1 and lints
35
+ nothing — and zero findings against zero findings is a parity run that passes. The refusal is now
36
+ its own sentence, quoting what oxlint refused and naming the recipe: config A is the base commit's
37
+ corpus config against the base BUILD, never HEAD's config against an older one.
38
+ - 3dac9a7: **One documented spelling (#90).** Every README of a package the metapackage fronts now names
39
+ `geonosis <tool>` — the door that pins the whole toolchain at one version — beside the bare bin, and
40
+ a `tooling/` test holds it. `#230`'s stamp test lands with it: every flag `docs/orchestration.md`'s
41
+ adoption loop names has to carry the version it first ships in, because the loop once told a
42
+ consumer to run a flag the published version did not have.
43
+
44
+ ## 2.2.0
45
+
46
+ ### Patch Changes
47
+
48
+ - d15a654: **An envelope writer refuses a root it has been told is not its (#241).** A suite ran one over the
49
+ SHARED repo root mid-run: parallel suites made the comparison racy, and the file it left behind is
50
+ one the next run reads as real. Every one of the seven writer copies now refuses to write when
51
+ `GEONOSIS_ENVELOPES_FORBIDDEN_ROOT` names the root it was pointed at, saying so and pointing at a
52
+ scratch root instead. The kit's own vitest config sets it to the repo root, so a test that writes an
53
+ envelope there names itself — which is how the offender that filed this row was found again.
54
+
55
+ ## 2.1.0
56
+
57
+ ## 2.0.0
58
+
59
+ ## 1.4.0
60
+
61
+ ### Minor Changes
62
+
63
+ - c712f3d: **A parity run now says which files both configs actually opened.** `filesLintedBy` asks oxlint itself, through `--debug=files`, and `fileCensus` splits the tree three ways; `geonosis-lint-parity` writes `.geonosis/envelopes/lint-parity.json` where `read` is the files BOTH configs read — the only population a findings diff means anything over — and a file one side ignores is `excused`, named with the side that ignored it. "B reported everything A did" over a tree B half-ignored has been the shape of an adoption proof that proved nothing. The census argv deliberately mirrors the lint argv, because oxlint's ignore resolution is sensitive to it: a config named through a symlinked prefix applies none of its own `ignorePatterns`.
64
+
65
+ ## 1.3.0
66
+
67
+ ### Patch Changes
68
+
69
+ - 0a7673c: **`geonosis-lint-parity --print-config-shape` (#75).** It reads no configuration file: the two
70
+ `.oxlintrc.json` files it compares are named with `--a` and `--b`, and it reads them as oxlint does.
71
+ - 228566f: **The doctor reads the options oxlint RESOLVES, not the base object beside them (#102).** A
72
+ config's `rules` block is only its first layer. An `overrides[]` entry naming a rule again replaces
73
+ its options for the files its globs claim — and dielime declares two of its three Medusa provider
74
+ registries exactly there, because that is where oxlint reads them. The rule resolved it correctly.
75
+ `drift` read `rules` alone and reported two working fulfillment carriers as directories the base
76
+ registry "never names": one config, two answers, from the tool whose whole subject is gates that
77
+ disagree with what they claim to measure.
78
+
79
+ Every reader that consumes a rule's configuration now takes base + matching overrides, resolved the
80
+ way oxlint resolves them — **measured** against oxlint 1.80, not assumed: an override REPLACES the
81
+ base's options for its files rather than merging into them, and the last of several matching
82
+ overrides wins outright.
83
+
84
+ - `drift`'s plugin-directory check asks each directory which layer governs it and checks it against
85
+ THAT layer's registry and manifests, so a repo with several registries gets a line per registry.
86
+ - `exercised` counts a rule an overrides entry enables — it read "no rule of this plugin is enabled
87
+ here" before, a SKIP from the check whose thesis is that an invisible check reads as a passing one
88
+ — and builds a probe from every configuration the repo gives a rule, not only the first.
89
+ - `@geonosis/lint-parity`'s `rulesNamedBy` reads overrides too: a rule configured only there fired
90
+ in the reach run and had no row in the report about which rules reach anything.
91
+
92
+ A layer's options are only half of what it resolves; the other half is which FILES it governs. A
93
+ probe built from an override's options and written where the RULE looks is a probe for a
94
+ configuration that will not run on it — during.day scopes `no-raw-html-atoms` to its compound and
95
+ cell folders, and the probe lands nowhere near them. When no layer enabling a rule claims where its
96
+ probe lands, the question was not ASKED: UNJUDGED, naming the globs, never a FAIL against a repo
97
+ enforcing the rule on every file it means to.
98
+
99
+ Measured read-only. dielime: `2 directory(ies) it never names` becomes five OK lines, one per
100
+ registry, and nothing else in the run moves. during.day: the rules it enables only in `overrides[]`
101
+ were invisible to this check and are now counted — two configs move from silence to UNJUDGED, which
102
+ is what "we could not tell" has always meant here, and neither passes.
103
+
104
+ ## 1.2.0
105
+
106
+ ## 1.1.0
107
+
108
+ ## 1.0.1
109
+
110
+ ### Patch Changes
111
+
112
+ - 1c2ee0b: **Every package ships its declarations and says where they are.** `@geonosis/visual-diff` and
113
+ `@geonosis/verify` emitted no `.d.ts` at all (TS7016 in a consumer: the comparator every visual test
114
+ calls became `any` at the one seam that decides whether a screenshot regressed), and no package
115
+ declared `types` — the nineteen that worked did so through the sibling-file fallback, which ends the
116
+ day `exports` gains a condition. Every export now carries a `types` condition beside `default`, every
117
+ manifest a `types` field, and `tooling/types.test.ts` holds all twenty-one to it after the build.
118
+
119
+ `geonosis-verify-arch` prints a check once when its label is its id ("direction, orphan", not
120
+ "direction direction").
121
+
122
+ ## 1.0.0
123
+
124
+ ### Major Changes
125
+
126
+ - c575f44: Five counters, `tolerance`, and a baseline rewrite that only ever goes down.
127
+
128
+ - `disabledCiJobs` — lines of `if: false` across the workflow files (`dir`). A job switched off to
129
+ get a release through is debt exactly like a lint error: invisible, because the pipeline goes
130
+ green. A condition that merely mentions `false` is not one, and a repo with no workflows
131
+ directory reads 0 rather than refusing; a workflow that is there and cannot be read refuses.
132
+ - `bundleBytes` — one integer out of whatever the repo's sizing command printed (`command`,
133
+ `match`), separators and all. With `match` it takes the group that pattern names and refuses when
134
+ it matches nothing, rather than falling back to any integer and reporting the wrong number as
135
+ confidently as the right one.
136
+ - `fastTierMs` — `finishedAt − startedAt` from the gate report `geonosis-verify` wrote (`report`,
137
+ `tier`). It refuses a report of another tier: the full tier takes minutes, and timing it against
138
+ the fast tier's baseline would report a regression of the wrong thing.
139
+ - `testsWithoutRunner` — workspaces holding tests with no `test` script. Unmeasured and passing are
140
+ indistinguishable from outside, so the count of unmeasured things has to be a number of its own.
141
+ - `packagesWithoutTypecheck` — workspaces with no `typecheck` script (`script`).
142
+
143
+ `tolerance` is new on the verdict: the fraction of the baseline a number may drift UP before the
144
+ ratchet calls it growth. Bytes and milliseconds move on a dependency patch nobody chose, and a gate
145
+ that fails on +40 bytes is a gate that gets switched off. It forgives noise upward only — a shrink
146
+ of any size still lowers the baseline — and a `tolerance` that is not a non-negative number stops
147
+ the run naming the entry, because one that silently became `NaN` reads exactly like a counter that
148
+ can never grow.
149
+
150
+ **Fix:** a baseline rewrite now lowers only the numbers that SHRANK. Writing every measured key back
151
+ was a harmless no-op while `held` implied `now === baseline`. Tolerance breaks that implication, and
152
+ the blanket rewrite would have laundered growth-within-tolerance into the new floor on the first
153
+ unrelated win — a baseline going up on a pass.
154
+
155
+ `examples/dielime.ratchet.json` is dielime's Phase 3 config: 36 keys, one `testFailures` entry per
156
+ workspace. It keeps `--format=unix` and `expectFormat` on the two oxlint counters, which dielime's
157
+ own config drops — the summary cross-check can tell a misparse from a clean run only when the run
158
+ printed a summary, and a warnings-only run in an unreadable shape prints one that says nothing
159
+ about errors. Pinning the format is what closes that window.
160
+ - c418f91: The `geonosis` Claude Code plugin — the method as enforcement rather than advice.
161
+
162
+ `plugin/` ships five hooks, Node ≥ 22, zero dependencies, every tool resolved from the CONSUMER's
163
+ `node_modules/.bin` so a repo that has not adopted a tool is never blocked on a tool it lacks:
164
+
165
+ - **SessionStart** runs `geonosis-doctor` if it is installed and prints a one-line digest of
166
+ `.oxlintrc.json` — which plugins are loaded, how many architecture rules are at `error`. It never
167
+ fails a session.
168
+ - **PreToolUse / Bash** refuses `prettier` (law 4) and `--no-verify` (law 7), and hands a
169
+ `git commit` to `geonosis-ledger` — the message its `-m` flags compose, and whether the staged
170
+ diff delivers anything but comments. The detectors are the ledger's, never reimplemented here; a
171
+ repo without it gets a note and no opinion.
172
+ - **PreToolUse / Edit|Write|MultiEdit** refuses a write that ADDS `eslint-disable`,
173
+ `oxlint-disable`, `@ts-ignore` or `@ts-expect-error` (law 3), any write to `gate-baseline.json` or
174
+ under `.geonosis/` (D-007 — the scored agent never writes the scoreboard), and a lint config that
175
+ sets a rule to `"warn"`, `"off"`, `0` or `1` (law 2). It reads what the call would ADD, never what
176
+ it removes, so the edit that DELETES a suppression is not refused by the guard against them.
177
+ - **PostToolUse / Edit|Write** runs oxfmt on the file just written. It never blocks.
178
+ - **Stop** runs `geonosis-verify fast` and will not let the turn end on a red one, capped at five
179
+ blocks per session — an agent retrying the same broken thing five times has a problem it will not
180
+ solve on the sixth, so the sixth prints the report and lets go.
181
+
182
+ `.claude-plugin/marketplace.json` offers it from `./plugin`. `pnpm generate:skill` now writes the
183
+ plugin's copy of the mechanical skill, the engineer agent with the machine-specific path taken out,
184
+ and `plugin.json` carrying the kit's own version; staleness tests fail when any of the four drift.
185
+ - 5f09ddc: Every hook is driven end to end, as Claude Code drives it.
186
+
187
+ Each script is SPAWNED with real stdin JSON over a throwaway repo carrying a `geonosis.json`, a
188
+ `gate-baseline.json`, an `.oxlintrc.json` and a `node_modules/.bin` linked to the BUILT
189
+ `geonosis-verify` and `geonosis-ratchet`. A hook tested by calling its policy function proves the
190
+ policy; only a spawn proves the exit code, the JSON on stdout and the tool resolution — which is
191
+ the whole of what Claude Code actually reads.
192
+
193
+ The Stop hook is exercised against a real fast tier that really goes red: a law file one line over
194
+ its baseline, so the ratchet grows and the hook blocks with exit 2, the failing step and its tail on
195
+ stderr. Then the sixth block prints the report and exits 0; a green run clears the session's count;
196
+ `stop_hook_active` short-circuits; and a repo with a BACKLOG that did not regress is not blocked at
197
+ all — which is the whole adoption argument, tested rather than asserted.
198
+
199
+ The suite was falsified before it was trusted: making Stop exit 0 instead of 2, dropping the
200
+ delivery check, and raising the cap to 50 each turned exactly the tests that should have gone red,
201
+ red.
202
+ - 89c6769: The ratchet's own layers are walled, and the walls have a door that is proved open.
203
+
204
+ The three ratchet layers named FILE paths only, so `packages/ratchet/src/index.ts` — the published
205
+ entry — sat in no layer and could import anything, the CLI included, with lint green. It is
206
+ `ratchet-api` now, and the formatter it shares with the CLI has moved into a `ratchet-report` layer
207
+ of its own rather than being counted as part of the CLI it is not.
208
+
209
+ The dogfood suite gains a second kind of plant. A wall that refused everything would be as useless
210
+ as one that refused nothing, so each layer's permitted import — `verify` reaching the lock through
211
+ `@geonosis/ratchet` — is planted and asserted to stay CLEAN. Without that, the next engineer widens
212
+ `mayImport` to get their work through and the wall goes with it.
213
+ - b2d915f: **Fix:** a counter's command no longer loses what it wrote to stderr.
214
+
215
+ `<command> 2>&1` binds the redirect to the LAST command of the string. `echo boom >&2; echo fine`
216
+ therefore merged nothing, and on a ZERO exit `execSync` hands back stdout alone — so a tool that
217
+ prints its findings to stderr and exits 0 read as a tool that found nothing. That is the exact shape
218
+ of a gate that has quietly stopped gating, and it is the one direction in which this kind of bug is
219
+ dangerous: the number comes back lower than the truth and the baseline banks it.
220
+
221
+ On a NON-ZERO exit the line survived, because the failure path concatenates both captured buffers —
222
+ but concatenation put every stderr line after every stdout line, moving each diagnostic away from
223
+ the line it was about, which is what the evidence tail on a regression is for.
224
+
225
+ The command now runs in a subshell, so the shell itself merges the streams in the order they were
226
+ written. Measured before and after: `echo boom >&2` on a zero exit read `""` and now reads `boom`;
227
+ `echo one; echo two >&2; echo three; exit 4` read `one three two` and now reads `one two three`.
228
+ - 48bce0c: **Fix:** only a counter that measures a quantity accepts a `tolerance`.
229
+
230
+ Any counter took one. `{"counter":"lawLineCount","tolerance":0.5}` over a two-line law with a
231
+ baseline of 2 printed `ratchet PASS` at three lines — a 50 % growth, waved through. The same edit is
232
+ `"tolerance": 100` on `oxlintErrors`: the gate switched off from the config file, with the run still
233
+ reporting green. That is law 2 — never downgrade a rule — wearing a friendlier name, and it was the
234
+ easiest edit in the repo to get past a reviewer.
235
+
236
+ `Counter` gains `tolerates?: true`, declared only by `bundleBytes` and `fastTierMs`. Whether a
237
+ number is a measured quantity or a count of findings is the counter's to know, not the config's.
238
+ Anything else now stops the run, naming the entry:
239
+
240
+ ```
241
+ geonosis-ratchet: "oxlintErrors" does not accept a tolerance — only a counter that measures a
242
+ quantity declares one
243
+ ```
244
+
245
+ The plugin's write guard gains its first **ask**. A write to `geonosis.ratchet.json` that introduces
246
+ or raises a `"tolerance"` is neither refused nor allowed: it goes in front of the human as
247
+ `permissionDecision: "ask"`. Widening a band is legitimate and is not the agent's call — a band
248
+ somebody chose and a band somebody's agent chose are not the same thing. A tolerance carried through
249
+ an edit unchanged is not a change and says nothing.
250
+ - 461233c: Docs for the two new things: `docs/plugin.md` and `packages/verify/README.md`.
251
+
252
+ `docs/plugin.md` covers installing the plugin from a local path and from the remote once it is
253
+ pushed, what each of the five hooks does and the reason it does it that way, the five-block cap,
254
+ which files the runner owns and why the agent may not write them, what to put in `geonosis.json`,
255
+ and — the section every README skips — what the plugin CANNOT do. A `fast` tier of `["true"]` passes
256
+ for ever; only `geonosis-ratchet --prove` shows a counter can go red.
257
+
258
+ `packages/verify/README.md` is the runner's own: `--prove` first, the config with no default tier,
259
+ the three exit codes and why a report that could not be written is exit 2, the report shape with the
260
+ `<tier>/<n>` id rule, and `--exclusive`.
261
+
262
+ `docs/README.md` indexes both. The ratchet README gains rows for the five new counters, a section on
263
+ `tolerance` and the shrank-only rewrite it forces, and the landmine that cost an afternoon: a
264
+ loader or shim named RELATIVELY in a counter's command resolves to nothing under `--prove`, whose
265
+ probes run in a scratch directory. Write it `$PWD/…` and it works in both.
266
+ - e48501b: `@geonosis/verify` — one gate runner, with a report a hook can read.
267
+
268
+ `geonosis-verify <tier>` runs the shell steps a repo declared under `verify` in `geonosis.json`,
269
+ serially, stopping at the first red, and writes `.geonosis/gate-report.json`:
270
+ `{ tier, startedAt, finishedAt, ok, steps: [{ id, command, ok, exitCode, ms, tail, skipped? }] }`.
271
+ A step whose text is another tier's name is that tier, inlined in place; the inlined steps keep the
272
+ tier that declared them, so `fast/2` is the same command whether the operator ran `fast` or ran the
273
+ `full` tier that inlines it.
274
+
275
+ There is no default tier and there never will be one: no config, no `verify` object, or an unknown
276
+ tier is refused by name with exit 2, and so is a green run whose report could not be written — a
277
+ gate that cannot record has not passed. A failed step is exit 1.
278
+
279
+ `--exclusive` takes the ratchet's machine-wide lock through `@geonosis/ratchet`'s published entry
280
+ (`--exclusive-timeout <seconds>` bounds the wait), so a verify and a ratchet on one laptop serialise
281
+ against each other rather than each against itself. `--prove` plants a tier with one passing step
282
+ and one that exits 3, runs the real binary over it in a temp directory, and checks the failure
283
+ reached the report — a runner that reports green is worth nothing until it has been watched going
284
+ red on purpose.
285
+
286
+ The kit walls the new package with its own `layer-walls` rule: `verify` may import `ratchet-api`
287
+ and nothing behind it, planted against in the dogfood suite like the other walls.
288
+
289
+ ## 0.6.0
290
+
291
+ ### Minor Changes
292
+
293
+ - e446760: **New package `@geonosis/doctor` (bin `geonosis-doctor`).** Four questions a version bump is not
294
+ finished until something has asked. Each one is a way enforcement has reported green while measuring
295
+ nothing, in a real repo, for weeks.
296
+
297
+ - **`loaded` — declared ≠ loaded.** oxlint resolves a `jsPlugins` specifier from the CONFIG FILE's
298
+ directory, so a nested `apps/<ws>/node_modules/@scope/plugin` left behind by a per-workspace
299
+ install runs while every manifest and the lockfile say a different version. One consumer measured
300
+ a whole bug report against 0.3.0 on a repo pinned to 0.4.0. For every config in the tree naming a
301
+ `@geonosis/*` plugin, this resolves it from that config's own directory, imports it, reads
302
+ `meta.version`, and compares it to the version the nearest manifest declares — then lists every
303
+ copy **resolution** reaches, one question per workspace. Never a `find`: on pnpm the store keeps
304
+ every version ever installed, so counting directories answers 7 on a correct tree. A declared
305
+ range it cannot read is a FAIL, not a pass.
306
+ - **`exercised` — enabled ≠ exercised.** Every rule a config enables, run against the corpus the
307
+ loaded plugin ships, under that config's own options — because the options are usually where the
308
+ reach went. Reuses `@geonosis/lint-parity`'s `corpusOf`; a plugin shipping no corpus is a SKIP.
309
+ - **`baseline` — a number that may only shrink, against another ref.** A ratchet lowers its own
310
+ baseline when a number shrinks, so nothing inside one checkout can see a branch raise one back up.
311
+ `--baseline-against [ref]` compares `gate-baseline.json` at HEAD with the same file at a ref. It
312
+ is OFF unless asked for by name: it is the only check that runs git, and this tool gets pointed at
313
+ repos other people are working in.
314
+ - **`runner` — an exit code is not a verdict.** Workspaces whose `test` script runs vitest or
315
+ `bun test` without asking for a JSON report, and which no `testFailures` counter in report mode
316
+ covers. `@cloudflare/vitest-pool-workers` exited 0 over suites it had just reported as failing.
317
+
318
+ `--strict` promotes every WARN to a FAIL; `--json` prints the whole report for CI. Exits 1 on any
319
+ FAIL, 2 when the run could not be made at all.
320
+
321
+ The package depends on `@geonosis/lint-parity` and on **nothing from the rule plugin** — it asks
322
+ which plugin a config loads, and a copy in its own dependency tree would be one more copy for the
323
+ question to be wrong about. A `layer-walls` layer named `doctor` in the kit's own config holds that
324
+ wall, planted against in `dogfood.test.ts`.
325
+
326
+ **`@geonosis/lint-parity` exports `resolveOxlint(from)`** — the consumer's own oxlint, walking up
327
+ from a directory. It was already the answer the CLI needed; it is now the answer every tool that
328
+ spawns oxlint shares, rather than each keeping its own.
329
+
330
+ **Measured, and worth knowing before writing anything that reads oxlint's output:** on oxlint 1.80 a
331
+ JS plugin rule that throws from `create()` is reported ONCE PER FILE, and `--format=unix` renders
332
+ each of those as `:0:0: [Warning]` — no path, no rule id, no sentence. The reason survives only in
333
+ the default formatter. `geonosis-doctor` therefore asks a second time, over one trivial file, in the
334
+ format that answers. No rule changed in this release.
335
+
336
+ ## 0.5.0
337
+
338
+ ### Minor Changes
339
+
340
+ - **New rule `cell-no-cross-feature-organelles` (preset `frontend`).** `organelle-dependency` is scoped
341
+ to `organelles/` and `no-cross-feature-stores` only judges stores, so a CELL importing another
342
+ feature's ORGANELLE passed both while both stayed green. Option `sharedFeatures`, default
343
+ `['shared']` — the same option and the same default as `organelle-dependency`'s, because the two
344
+ rules draw one boundary from two sides. Measured on a consumer: three findings in two files.
345
+
346
+ **The corpus carries both spellings of every direction rule.** A direction rule judges where an
347
+ import lands, so `@/features/billing/x` and `../../billing/x` are one breach; the corpus previously
348
+ carried whichever spelling each rule's author happened to write. Fifteen fixtures added. No rule
349
+ changed.
350
+
351
+ **`geonosis-lint-parity --corpus --expect-changed <rule,…>`.** A changelog line that names a rule is
352
+ a claim; this makes it data the corpus judges, and exits 1 on a claim the corpus is silent about or a
353
+ change nobody claimed. Mandatory for any release whose notes name a rule.
354
+
355
+ **`testFailures` reads the runner's JSON report** with `report: "vitest-json"`, refusing when the
356
+ report is absent, unparsable, missing `numFailedTests`, or says `success: false` while naming none.
357
+ `Counter.probe` takes a list, so `--prove` proves each reading mode by name.
358
+
359
+ **`geonosis-ratchet --prove` proves the lock**, by running two children of the CLI over a slow
360
+ `--hold` and requiring the second to start after the first finished.
361
+
362
+ `@geonosis/ratchet`'s source is layered — `core/`, `counters/`, `report.ts`, `cli.ts` — with no
363
+ change to what `@geonosis/ratchet` exports or to a single line the CLI prints.
364
+
365
+ ## 0.4.0
366
+
367
+ Version only — the three `@geonosis/*` packages move as one fixed group, and nothing in this package
368
+ changed in 0.4.0. The release is the plugin's rule core and `@geonosis/ratchet`'s heavy lock.
369
+
370
+ ## 0.3.0
371
+
372
+ ## 0.2.1
373
+
374
+ Version only — the three `@geonosis/*` packages move as one fixed group, and nothing in this
375
+ package changed in 0.2.1. The release is `@geonosis/ratchet`'s: `--prove`, `--exclusive`, and
376
+ `typecheckErrors` refusing on an unbuilt workspace.
377
+
378
+ ## 0.2.0
379
+
380
+ ### Minor Changes
381
+
382
+ - Loose coupling, first slice — and a posture change that will break a config on purpose.
383
+
384
+ **`@geonosis/lint-parity` is a new package.** Findings parity, rule reach, the corpus-manifest
385
+ contract and the `geonosis-lint-parity` bin now live on their own. The tool knows no rule name and
386
+ no plugin: two oxlint configs and a tree are the whole input, and a corpus arrives as a directory
387
+ whose own `manifest.json` says which rules it speaks for. A parity tool welded to one rule plugin
388
+ can only ever check that plugin.
389
+
390
+ **BREAKING (plugin): the `geonosis-lint-parity` bin and the `./parity` export are gone.** Install
391
+ `@geonosis/lint-parity` and pass `--corpus node_modules/@geonosis/oxlint-plugin-biological-architecture/corpus`
392
+ — the plugin still ships the corpus, which is the part that is genuinely its own. `--corpus` now
393
+ requires its directory.
394
+
395
+ **BREAKING (plugin): a rule enabled without the options it needs refuses the run.** `no-brand-names`,
396
+ `no-raw-html-atoms`, `documents-share-one-table`, `document-sagas-are-generic`,
397
+ `step-opens-its-own-cell` and `layer-walls` throw from `create()` naming the missing option, instead
398
+ of returning an empty visitor. A consumer that enabled one bare will fail lint until it configures
399
+ the rule or turns it off — which is the point: one consumer ran `no-raw-html-atoms` at "error" for
400
+ months with no element→atom map, over 136 raw elements, and the gate was green. An enabled rule that
401
+ cannot fire is indistinguishable from compliance. A rule nobody enabled still does nothing.
402
+
403
+ **New rule `layer-walls`, new preset `tooling`.** A repo declares its own layers as data —
404
+ `{ name, paths, mayImport }` — and the rule enforces the direction between them: relative sources
405
+ resolved against the importing file first, `import()`, `require()` and both re-export forms visited,
406
+ externals not walls unless asked. The kit enables it on itself.
407
+
408
+ The plugin also ships type declarations now, for all three entries.
@@ -17,11 +17,25 @@ const newest = (dir) =>
17
17
  }, 0)
18
18
  : 0
19
19
  const src = join(here, '..', 'src')
20
- if (existsSync(src) && newest(src) > newest(join(here, '..', 'dist'))) {
21
- process.stderr.write(
22
- 'geonosis-lint-parity: dist is older than srcrun pnpm build before trusting this bin.\n',
23
- )
24
- process.exit(2)
20
+ const dist = join(here, '..', 'dist')
21
+ // `newest` answers 0 for a directory that is NOT THERE and for one that is merely old, so the
22
+ // two conditions were one comparison and one sentence and the staleness sentence was printed
23
+ // over trees that had never been built at all, blocking a turn in a fresh git worktree, which
24
+ // has no dist because dist is not committed (#357). Only the second is evidence of a mistake.
25
+ if (existsSync(src)) {
26
+ const built = newest(dist)
27
+ if (built === 0) {
28
+ process.stderr.write(
29
+ 'geonosis-lint-parity: this tree has never been built — run pnpm build. No dist here at all, which is what a fresh clone or a linked git worktree starts with; nothing is stale.\n',
30
+ )
31
+ process.exit(2)
32
+ }
33
+ if (newest(src) > built) {
34
+ process.stderr.write(
35
+ 'geonosis-lint-parity: dist is older than src — run pnpm build before trusting this bin.\n',
36
+ )
37
+ process.exit(2)
38
+ }
25
39
  }
26
40
 
27
41
  await import('../dist/parity-cli.js')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geonosis/lint-parity",
3
- "version": "2.3.1",
3
+ "version": "2.4.3",
4
4
  "types": "./dist/index.d.ts",
5
5
  "description": "Findings parity and rule reach over any two oxlint configs — the diff a fork is deleted on.",
6
6
  "keywords": [
@@ -31,6 +31,7 @@
31
31
  }
32
32
  },
33
33
  "files": [
34
+ "CHANGELOG.md",
34
35
  "bin",
35
36
  "dist"
36
37
  ],
@@ -44,7 +45,7 @@
44
45
  "access": "public"
45
46
  },
46
47
  "devDependencies": {
47
- "@geonosis/ratchet": "2.3.1"
48
+ "@geonosis/ratchet": "2.4.3"
48
49
  },
49
50
  "scripts": {
50
51
  "build": "tsup",