@geonosis/oxlint-plugin-biological-architecture 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +321 -0
  2. package/README.md +22 -20
  3. package/RULES.md +152 -121
  4. package/corpus/.oxlintrc.json +37 -7
  5. package/corpus/app/arrow-route/page.tsx +1 -0
  6. package/corpus/app/barrel-delegate/page.tsx +5 -0
  7. package/corpus/app/good-delegate/page.tsx +2 -0
  8. package/corpus/atoms/index.ts +1 -0
  9. package/corpus/cells/alias-of-cell.tsx +3 -0
  10. package/corpus/cells/index.ts +1 -0
  11. package/corpus/cells/laundered-compound.tsx +3 -0
  12. package/corpus/cells/laundered-organelle.tsx +3 -0
  13. package/corpus/compounds/bad-aliased-barrel-organelle-import.tsx +6 -0
  14. package/corpus/compounds/bad-barrel-organelle-import.tsx +7 -0
  15. package/corpus/compounds/bad-member-hook.tsx +8 -0
  16. package/corpus/compounds/good-atom-through-barrel.tsx +6 -0
  17. package/corpus/dates/bad-method-order.ts +7 -0
  18. package/corpus/dates/good-time-order.ts +2 -0
  19. package/corpus/disguise/bad-namespace-effect.tsx +7 -0
  20. package/corpus/disguise/good-rendering-effect.tsx +7 -0
  21. package/corpus/emails/welcome.ts +4 -0
  22. package/corpus/failure/logged-by-name.ts +2 -0
  23. package/corpus/hidden/bad-inert-hidden.tsx +3 -0
  24. package/corpus/hidden/good-hidden-input.tsx +3 -0
  25. package/corpus/inline-data/cells/bad-inline-options.tsx +15 -0
  26. package/corpus/inline-data/cells/good-imported-options.tsx +11 -0
  27. package/corpus/integrations/providers/email-gmail.ts +2 -2
  28. package/corpus/medusa/queries/bad-unordered-page.ts +1 -1
  29. package/corpus/medusa/queries/good-ordered-page.ts +1 -1
  30. package/corpus/medusa/subscribers/bad-satisfied-config.ts +5 -0
  31. package/corpus/molecules/alias-of-molecule.tsx +2 -0
  32. package/corpus/molecules/good-molecule.types.ts +1 -0
  33. package/corpus/molecules/laundered-atom.tsx +3 -0
  34. package/corpus/organelles/bad-logic.ts +5 -0
  35. package/corpus/organelles/custom-carousel.tsx +11 -0
  36. package/corpus/organelles/index.ts +2 -0
  37. package/corpus/organelles/reveal-on-scroll-list.tsx +9 -0
  38. package/corpus/organelles/use-cart-effect.ts +11 -0
  39. package/corpus/packages/db/src/cell/good-cell-transaction.ts +7 -0
  40. package/corpus/ports/good-engine-callback.ts +4 -0
  41. package/corpus/subpaths/bad-root-reexport.ts +3 -0
  42. package/corpus/time/bad-reexported-door.ts +5 -0
  43. package/corpus/tissues/alias-of-tissue.tsx +2 -0
  44. package/corpus/tissues/bad-handler-prop.tsx +7 -0
  45. package/corpus/tissues/bad-laundered-organelle.tsx +6 -0
  46. package/corpus/tissues/bad-laundered-through-cells-barrel.tsx +6 -0
  47. package/corpus/tissues/bad-member-hook.tsx +11 -0
  48. package/corpus/tissues/bad-namespace-laundered.tsx +7 -0
  49. package/corpus/tissues/bad-passthrough.tsx +4 -2
  50. package/corpus/tissues/bad-star-laundered.tsx +2 -0
  51. package/corpus/tissues/bad-untyped-prop.tsx +7 -0
  52. package/corpus/tissues/good-cell-through-barrel.tsx +6 -0
  53. package/corpus/tissues/good-data-props.tsx +13 -0
  54. package/corpus/tissues/good-namespace-real-cell.tsx +7 -0
  55. package/corpus/tissues/laundered-compound.tsx +3 -0
  56. package/corpus/todos/bad-xxx-marker.ts +2 -0
  57. package/corpus/todos/good-marker-inside-a-number.ts +2 -0
  58. package/corpus/tokens/theme/palette.tsx +1 -1
  59. package/corpus/tsconfig.json +3 -0
  60. package/corpus/workers/bad-satisfied-worker.ts +7 -0
  61. package/dist/chunk-H7UQNDFE.js +109 -0
  62. package/dist/{chunk-24K5232K.js → chunk-ZY3XIRRE.js} +1 -1
  63. package/dist/discover-atoms.js +4 -3
  64. package/dist/index.d.ts +3 -4
  65. package/dist/index.js +1469 -914
  66. package/dist/presets.d.ts +5 -5
  67. package/dist/presets.js +1 -1
  68. package/package.json +5 -4
  69. package/corpus/tissues/bad-data-props.tsx +0 -5
package/RULES.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Read from the two source trees on 2026-08-29:
4
4
 
5
- - `dielime/packages/eslint-plugin-biological-architecture` — **36 rules**
6
- - `during.day/packages/eslint-plugin-biological-architecture` — **52 rules** (51 when the union was
5
+ - a Medusa storefront's `packages/eslint-plugin-biological-architecture` — **36 rules**
6
+ - a Workers + D1 app's `packages/eslint-plugin-biological-architecture` — **52 rules** (51 when the union was
7
7
  cut; `step-opens-its-own-cell` landed there hours later and was folded in afterwards)
8
8
 
9
9
  One plugin name, two rule sets, no shared code. **33 rules had the same filename in both trees; only
@@ -15,8 +15,8 @@ plus twenty-eight the kit wrote for itself: `layer-walls`, `emit-declares-attemp
15
15
  `cell-no-cross-feature-organelles`, the `backend-medusa` ten, `no-unregistered-plugin-dir` +
16
16
  `router-schema-parity`, the `themekit` two, `no-mock-db-in-integration`, `todo-names-its-plan`,
17
17
  `boundary-time-is-primitive`, `durable-body-reads-through-steps`, `subpath-over-barrel` and the six
18
- of the W67 batch — **80**. The two retired ids still resolve for one release, as no-ops that say
19
- they are retired, so a config naming them parses; they go at the next major.
18
+ of the W67 batch — **80**. The two retired ids still resolve until the next major, as no-ops that
19
+ say they are retired, so a config naming them parses.
20
20
  Every one of the twenty-eight is marked **written here** in the inventory, and the table below is
21
21
  held against that column rather than kept by hand.
22
22
 
@@ -24,8 +24,8 @@ held against that column rather than kept by hand.
24
24
  |---|---|
25
25
  | in both trees, byte-identical | 3 |
26
26
  | in both trees, **diverged** | 30 |
27
- | dielime only | 3 |
28
- | during.day only | 16 |
27
+ | A Medusa storefront only | 3 |
28
+ | A Workers + D1 app only | 16 |
29
29
  | **union** | **52** |
30
30
  | written here | 28 |
31
31
  | **total** | **80** |
@@ -34,12 +34,12 @@ held against that column rather than kept by hand.
34
34
 
35
35
  Two shapes of divergence, and one resolution rule each.
36
36
 
37
- **dielime broadened the path patterns.** Every dielime rule scoped its file test as
37
+ **A Medusa storefront broadened the path patterns.** Every Medusa storefront rule scoped its file test as
38
38
  `(?:features/[^/]+/)?<tier>/`, so it fires on a FLAT kit (`packages/ui/src/tissues/…`) as well as a
39
- feature-sliced app. during.day's kept `features/<name>/<tier>/`. **The broader pattern wins** — a
39
+ feature-sliced app. A Workers + D1 app's kept `features/<name>/<tier>/`. **The broader pattern wins** — a
40
40
  rule that reaches more files is the stricter rule.
41
41
 
42
- **during.day broadened the node coverage.** Its August 2026 sweep added
42
+ **A Workers + D1 app broadened the node coverage.** Its August 2026 sweep added
43
43
  `ExportAllDeclaration` / `ExportNamedDeclaration` handlers to every import-direction rule (a
44
44
  re-export names a module exactly as an import does), and turned `tissue-no-hooks` from an import
45
45
  check into a hook-CALL check — the hole three review rounds missed was a tissue reaching a store
@@ -50,19 +50,19 @@ Where a divergence was a false-positive fix rather than a reach change (own-sibl
50
50
  `Kind`-vocabulary props), it was kept: both repos' fixtures depend on those, and a rule that fires on
51
51
  an accepted convention is a rule people learn to ignore.
52
52
 
53
- **Both repos' test suites are ported and green** — during.day's 300-odd hand-built-AST tests, its
54
- `real-source-tests` cases through the real binary, and dielime's 45-case oxlint fixture lock.
53
+ **Both repos' test suites are ported and green** — a Workers + D1 app's 300-odd hand-built-AST tests, its
54
+ `real-source-tests` cases through the real binary, and a Medusa storefront's 45-case oxlint fixture lock.
55
55
 
56
56
  ## Named decisions
57
57
 
58
- ### `cell-must-be-stateful` (dielime) vs `cell-handles-are-scoped` (during.day) — **both kept, one renamed**
58
+ ### `cell-must-be-stateful` (a Medusa storefront) vs `cell-handles-are-scoped` (a Workers + D1 app) — **both kept, one renamed**
59
59
 
60
60
  These share only the word "cell", and they mean different things by it.
61
61
 
62
- - dielime's `cell-must-be-stateful` is the **biological tier**: a file in `cells/` whose whole family
62
+ - A Medusa storefront's `cell-must-be-stateful` is the **biological tier**: a file in `cells/` whose whole family
63
63
  is props-in / JSX-out is a compound in the wrong folder. It is the inverse of
64
64
  `compound-must-be-stateless`. → kept as-is, `frontend`.
65
- - during.day's `cell-handles-are-scoped` is a **Cloudflare D1 database cell**: a worker that opens a
65
+ - A Workers + D1 app's `cell-handles-are-scoped` is a **Cloudflare D1 database cell**: a worker that opens a
66
66
  cell handle must wrap every invocation in `withCells` or it leaks a pool per call. Nothing to do
67
67
  with a tier. → kept, **renamed `worker-handles-are-scoped`**, `backend-d1`.
68
68
 
@@ -75,7 +75,7 @@ vocabulary at every diagnostic, so the backend one gave up the word.
75
75
  compiled in — a rule exactly one repo could enable, and the thing pinning that repo's last
76
76
  generic-utility package in place, because moving the door would have silenced the rule guarding it.
77
77
  The door is now the `door` option and the directories are `exempt`, both regex sources over the
78
- normalised filename, and the rule refuses the run when `door` is missing. The LIBRARY it polices was
78
+ normalised filename, and the rule reports the missing `door` once a run. The LIBRARY it polices was
79
79
  compiled in for one release longer — `dayjs`, so a repo on date-fns kept the raw-`Date` half and
80
80
  lost the half that catches a second configured formatter, which is the defect that half exists for.
81
81
  It is the `libraries` list now, defaulting to `['dayjs']`, and every diagnostic names the library
@@ -98,14 +98,45 @@ work. What changed is that they are counted: each of those rules declares `meta.
98
98
  pattern exempts in THIS tree beside the number the enumeration names — so a grant nobody decided
99
99
  stops being a grant nobody can see.
100
100
 
101
+ ### An enabled rule that cannot fire **reports that, once a run** (2.11.0 — amends the 0.2.0 refusal)
102
+
103
+ The requirement below stands unchanged: a rule enabled without the option it cannot run without is
104
+ a config error, and it has to be visible. What changed on 2026-09-18 is HOW. From 0.2.0 the rule
105
+ threw from `create()`, which read as "the run is refused". Measured, it was destructive and
106
+ unreadable at once: oxlint reports a throwing plugin rule against the file it was being constructed
107
+ for and drops every other finding in that file — one file carrying a real `todo-names-its-plan`
108
+ finding, linted beside a bare `no-brand-names`, reported nothing but the throw — and under
109
+ `--format=unix`, the format every counter and the parity tool read, the throw rendered as
110
+ `:0:0: [Warning]`: no path, no rule id, no sentence.
111
+
112
+ So the rule now reports the refusal as a **finding**, on the first file a run constructs it for,
113
+ and is inert for the files after it (#202: one finding, one problem in the summary, however many
114
+ files the run has — measured over 300 files and 8 threads). The finding has a place and a rule id,
115
+ the ratchet counts it, the parity tool attributes it, its neighbours stay where they were, and the
116
+ run still exits non-zero. The wording is the same header as before —
117
+
118
+ ```
119
+ biological-architecture/<rule> requires option `<option>`. It is enabled but its `<option>` option
120
+ is nothing, so it judges no file — this finding stands in for every file it did not read, once a
121
+ run. Configure `<option>`, or turn the rule off: an enabled rule that judges nothing would
122
+ otherwise read exactly like a rule that found nothing. It accepts { … }.
123
+ ```
124
+
125
+ — so the doctor and `optionsNeededBy` read the rule and the option out of the same fixed position
126
+ (#199), and both hear a refusal a plugin throws (older builds) as well as one it reports. Nothing
127
+ exported from the plugin throws for a missing option any more; `requireOption` inside a rule module
128
+ raises the requirement, and the registry (`src/index.ts`) turns it into the finding. Everywhere
129
+ the prose below says "refuses the run", read "reports the missing option once a run and judges
130
+ nothing".
131
+
101
132
  ### An enabled rule that cannot fire **refuses the run** (0.2.0)
102
133
 
103
134
  This replaces the "does nothing until configured" posture below, and it is the one breaking change
104
- in 0.2.0 that a consumer will feel.
135
+ in 0.2.0 that a consumer will feel. (The mechanism was amended in 2.11.0 — see above.)
105
136
 
106
137
  Making every repo-specific list an option (law 6) has an obvious implementation and a wrong one:
107
138
  return an empty visitor when the option is missing. The result is a rule that is enabled, graded
108
- `"error"`, and **indistinguishable from compliance**. dielime ran `no-raw-html-atoms` at `"error"`
139
+ `"error"`, and **indistinguishable from compliance**. A Medusa storefront ran `no-raw-html-atoms` at `"error"`
109
140
  for months with no element→atom map. 136 raw elements in the tree. Zero findings. A green gate. It
110
141
  was found by hand, not by a gate, because there was no gate — only a rule with nothing to compare
111
142
  against, which is the silent-fallback class one level up from a counter reading zero under a format
@@ -117,7 +148,7 @@ So the posture splits in two:
117
148
  - a rule that **is** enabled without the options it needs is a **config error**, and `create()`
118
149
  throws, naming the rule and the missing option. oxlint refuses and exits non-zero.
119
150
 
120
- **Nineteen** rules take this, and the list is not kept by hand: `src/rules/option-gated.ts` names
151
+ The rules that take this are not listed by hand: `src/rules/option-gated.ts` names
121
152
  each with the option its refusal must say and a bag that makes it run clean, and
122
153
  `src/option-gated.test.ts` asks every rule the plugin exports whether it refuses a bare `create()`
123
154
  and fails when one of them is not on the list — or is on it and no longer refuses. It found seven
@@ -143,7 +174,7 @@ non-zero, and the summary counts a problem no line accounts for — which is exa
143
174
  never re-enter kit source (the ones the fork had compiled into its regex) — proved reaching by
144
175
  adding one to a source file and watching it fire.
145
176
 
146
- ### `no-brand-names` (dielime only) — brand list is now an **option**
177
+ ### `no-brand-names` (a Medusa storefront only) — brand list is now an **option**
147
178
 
148
179
  The rule shipped with a two-brand regex and a matching CDN-host allowance compiled into it. It could
149
180
  only ever serve the repo that wrote it. Now:
@@ -155,7 +186,7 @@ only ever serve the repo that wrote it. Now:
155
186
  ]
156
187
  ```
157
188
 
158
- Enabled with no `brands`, it refuses the run — see the decision above.
189
+ Enabled with no `brands`, it reports that once a run and judges nothing — see the decision above.
159
190
 
160
191
  ### `step-opens-its-own-cell` — one law, two spellings, every fact an **option**
161
192
 
@@ -197,7 +228,7 @@ finding there is a saga importing the engine's `step` around that wrapper — re
197
228
  ]
198
229
  ```
199
230
 
200
- Enabled with no `within`, it refuses the run. Type-only imports never fire: they bring no handle, so
231
+ Enabled with no `within`, it reports that once a run. Type-only imports never fire: they bring no handle, so
201
232
  they can share none. The diagnostic quotes the option values rather than any repo's paths, and the
202
233
  preset is its own — the invariant is true of a DURABLE run that hibernates between steps and false
203
234
  of an inline saga sharing the request's connection, so `backend-workflows` would have been a repo
@@ -270,7 +301,7 @@ run, like every other option a rule cannot fire without.
270
301
 
271
302
  Preset `backend-medusa`, which is twelve rules now.
272
303
 
273
- **One correction to the brief that asked for it**: dielime does not guard 50 emit sites this way
304
+ **One correction to the brief that asked for it**: a Medusa storefront does not guard 50 emit sites this way
274
305
  today. It has **four** `emitEventStep` calls (`create-sample-order`, `ingest-shipment-milestone`,
275
306
  `convert-sample-to-order` ×2), and **all four would fire** — three carry
276
307
  `options: { priority: EventPriority.CRITICAL }` with no attempts, one carries no options at all.
@@ -290,12 +321,12 @@ registry is read as SOURCE — a rule that had to import it would need the repo'
290
321
  up inside a linter, and every string in it is a name the registry knows whether it is spelled as an
291
322
  id or as an import path.
292
323
 
293
- dielime is the fixture that matters just as much: measured, **zero** of its provider directories are
324
+ A Medusa storefront is the fixture that matters just as much: measured, **zero** of its provider directories are
294
325
  unregistered. A rule that fired on the clean tree would be off within a day. And its third seam has
295
326
  no directories at all — `email-gmail.ts` and `whatsapp-360dialog.ts` sit flat beside their registry —
296
327
  so it is out of scope because nothing matches `<root>/<dir>/<manifest>`, not by a special case.
297
328
 
298
- *`registry` takes a list, because a registry can be split.* dielime's `medusa-config.ts` imports
329
+ *`registry` takes a list, because a registry can be split.* A Medusa storefront's `medusa-config.ts` imports
299
330
  `medusa-config.fulfillment.ts`, and the carrier directories are registered in the half that is
300
331
  imported. Reading only the file the option named made two REGISTERED directories a standing FAIL
301
332
  there — a false positive on a working tree, which is how a rule gets turned off. The halves are
@@ -313,16 +344,16 @@ its fixture exists to catch. The hand-built nodes could not find this: a hand-bu
313
344
  prose in it. Comments are stripped first now, and the direction is the point — a name that appears
314
345
  only in a comment is usually the note explaining why the directory was abandoned.
315
346
 
316
- **`router-schema-parity`.** during.day holds the mirror by hand at 21 of 22 routers and 21 of 23
347
+ **`router-schema-parity`.** A Workers + D1 app holds the mirror by hand at 21 of 22 routers and 21 of 23
317
348
  contracts; Midday holds its own at 33 of 37. A router with no schema is a wire contract written
318
349
  wherever the handler happens to say it; a schema with no router is a contract nothing serves.
319
350
 
320
351
  Two facts were measured before the rule was written, and both changed its shape:
321
352
 
322
- 1. during.day's two halves are in **different packages** — `apps/api/src/trpc/routers` against
353
+ 1. A Workers + D1 app's two halves are in **different packages** — `apps/api/src/trpc/routers` against
323
354
  `packages/api-contract/src` — so the options are repo-root-relative. A package-relative option
324
355
  could not name them both.
325
- 2. during.day's three apparent gaps are ONE deliberate arrangement: `routers/documents-mounted.ts`
356
+ 2. A Workers + D1 app's three apparent gaps are ONE deliberate arrangement: `routers/documents-mounted.ts`
326
357
  mounts the invoice and purchase-order entities and imports both contracts. A strict filename
327
358
  mirror reports all three and is wrong all three times, on the very tree the rule was built from.
328
359
  So `mounts` (`{ "documents-mounted": ["invoice", "purchase-order"] }`) is config, and the corpus
@@ -334,20 +365,20 @@ Neither of these came from review. Both came from running the rule read-only ove
334
365
  then running a CONTROL that should have made the same files fire.
335
366
 
336
367
  **`no-unregistered-plugin-dir.manifests` is required, with no default.** The rule first shipped a
337
- default entry-file list — `index.ts`, `index.tsx`, `config.ts`. Over dielime's two provider roots it
368
+ default entry-file list — `index.ts`, `index.tsx`, `config.ts`. Over a Medusa storefront's two provider roots it
338
369
  reported zero findings, and every directory there IS registered, so the run read as a pass. It was
339
- not one: dielime's plugin directories contain `service.ts` and no index at all, so the rule matched
370
+ not one: a Medusa storefront's plugin directories contain `service.ts` and no index at all, so the rule matched
340
371
  no file and answered a question it had never asked. The control — the same files, with a registry
341
372
  naming none of them — also reported zero, which is what exposed it. With `manifests: ["service.ts"]`
342
373
  the control fires on all three directories and the real config on none.
343
374
 
344
375
  **`emit-declares-attempts.budgetHelpers`, empty by default.** The claim this rule carried was that it
345
- covers dielime's emit sites, so that repo's 132-line source walker could be deleted. Measured: all
376
+ covers a Medusa storefront's emit sites, so that repo's 132-line source walker could be deleted. Measured: all
346
377
  **54** `emitEventStep` sites spell the bag `options: retryableEvent(…)` — zero object literals, zero
347
378
  missing bags — and a call is precisely the shape the rule declines to judge. It reported zero, and
348
379
  that zero proved only that no bag was absent. Named as a budget helper, a call to `retryableEvent`
349
380
  passes and a call to anything else is reported as a budget nobody can check; the control naming a
350
- helper dielime does not use reports all 54. Empty by default, so no existing consumer moves.
381
+ helper a Medusa storefront does not use reports all 54. Empty by default, so no existing consumer moves.
351
382
 
352
383
  Both are the same failure as a counter that reads zero under a format it cannot parse, and both were
353
384
  invisible to the rules' own fixtures — a fixture is written to match the rule, which is exactly what
@@ -359,8 +390,8 @@ The only rule in the plugin whose landmine is in **neither** source repo. Measur
359
390
 
360
391
  | tree | `mockDb` | `vi.mock('…db…')` | `vi.mock(` in integration suites |
361
392
  |---|---:|---:|---:|
362
- | dielime | 0 | 0 | **0** of 106 integration files |
363
- | during.day | 0 | 0 | 0 (`vi.mock` appears nowhere in the tree) |
393
+ | A Medusa storefront | 0 | 0 | **0** of 106 integration files |
394
+ | A Workers + D1 app | 0 | 0 | 0 (`vi.mock` appears nowhere in the tree) |
364
395
  | Midday | 32 references in 4 files | — | the gate |
365
396
 
366
397
  Midday's shape: `apps/api/src/__tests__/setup.ts:80` exports `mockDb = createMockDb()`;
@@ -391,22 +422,22 @@ Three matching decisions, each with a case in the node layer:
391
422
 
392
423
  Every rule below exists because the framework **infers** something instead of making you say it.
393
424
  None of them throws at runtime; all of them are found in production, far from the cause. Six were
394
- scans in dielime's `packages/verify-arch` first and are now also per-file rules; two come from
425
+ scans in a Medusa storefront's `packages/verify-arch` first and are now also per-file rules; two come from
395
426
  Medusa's own source; one from an estimate a route already labels by hand. Three more — the per-file
396
427
  halves of R4.3, R3.8 and R5.1 — landed after adoption showed what a text scan misses.
397
428
 
398
429
  | rule | the silence | source |
399
430
  |---|---|---|
400
- | `mutating-step-declares-retry` | `maxRetries` defaults to **0** (`orchestration/transaction/types.ts:48`), so a transient failure reverts real state a retry would have absorbed | dielime scan R5.2 |
401
- | `step-declares-compensation-or-none` | `stepConfig.noCompensation = !compensateFn` (`create-step.ts:184`) — a deliberate non-compensation and a forgotten one are the same source | analysis §1.1, "highest-value lint gap"; dielime scan R5.6 |
402
- | `named-when-block` | an unnamed `when()` derives a `when-then-{ulid}` step name (`composer/when.ts:120-126`), so a resumed run cannot find its checkpoint | dielime scan R5.5 |
403
- | `subscriber-declares-id` | the loader infers a subscriber id from a function or file name; two that infer alike dedupe each other | dielime scan R5.4 |
404
- | `no-loader-side-effect-in-index-file` | `ResourceLoader.discoverResources` filters files named `index` (`resource-loader.js:51`) | dielime scan #15442 |
405
- | `service-name-not-reserved` | a module key matching the `Modules` enum silently REPLACES the built-in (`medusa-app.ts:175-198`); `registerModule` guards only aliases and mains | analysis §1.3 — dielime's `catalog_search` scar |
406
- | `no-index-count-as-exact` | the index engine's count is an EXPLAIN plan-row estimate and reads as an exact total | analysis §3a; dielime's b2b route labels it by hand |
407
- | `unique-query-step-name` | the step-handler map is a plain assignment and the orchestrator appends a duplicate action with no dedupe, so two un-named query steps in one body last-win: one read never happens | dielime scan R4.3 |
408
- | `plugin-route-namespaced` | the routes-loader assigns last-write-wins, so a plugin route with no namespace segment is claimable by any other package with no error | dielime scan R3.8 |
409
- | `route-no-inline-mutation` | a route that writes directly succeeds, returns 200, and skips the compensation and retry class that live behind the workflow door | dielime scan R5.1 |
431
+ | `mutating-step-declares-retry` | `maxRetries` defaults to **0** (`orchestration/transaction/types.ts:48`), so a transient failure reverts real state a retry would have absorbed | a Medusa storefront's scan R5.2 |
432
+ | `step-declares-compensation-or-none` | `stepConfig.noCompensation = !compensateFn` (`create-step.ts:184`) — a deliberate non-compensation and a forgotten one are the same source | analysis §1.1, "highest-value lint gap"; a Medusa storefront's scan R5.6 |
433
+ | `named-when-block` | an unnamed `when()` derives a `when-then-{ulid}` step name (`composer/when.ts:120-126`), so a resumed run cannot find its checkpoint | a Medusa storefront's scan R5.5 |
434
+ | `subscriber-declares-id` | the loader infers a subscriber id from a function or file name; two that infer alike dedupe each other | a Medusa storefront's scan R5.4 |
435
+ | `no-loader-side-effect-in-index-file` | `ResourceLoader.discoverResources` filters files named `index` (`resource-loader.js:51`) | a Medusa storefront's scan #15442 |
436
+ | `service-name-not-reserved` | a module key matching the `Modules` enum silently REPLACES the built-in (`medusa-app.ts:175-198`); `registerModule` guards only aliases and mains | analysis §1.3 — a Medusa storefront's `catalog_search` scar |
437
+ | `no-index-count-as-exact` | the index engine's count is an EXPLAIN plan-row estimate and reads as an exact total | analysis §3a; a Medusa storefront's b2b route labels it by hand |
438
+ | `unique-query-step-name` | the step-handler map is a plain assignment and the orchestrator appends a duplicate action with no dedupe, so two un-named query steps in one body last-win: one read never happens | a Medusa storefront's scan R4.3 |
439
+ | `plugin-route-namespaced` | the routes-loader assigns last-write-wins, so a plugin route with no namespace segment is claimable by any other package with no error | a Medusa storefront's scan R3.8 |
440
+ | `route-no-inline-mutation` | a route that writes directly succeeds, returns 200, and skips the compensation and retry class that live behind the workflow door | a Medusa storefront's scan R5.1 |
410
441
 
411
442
  Four postures they share, all of them the one `emit-declares-attempts` set:
412
443
 
@@ -435,7 +466,7 @@ parameter and its doc comment, and nothing else). So a `workflows/index.ts` **is
435
466
  2.19 and a `subscribers/index.ts` is not. A rule firing on a file the framework reads is a false
436
467
  positive, and false positives are how a rule gets ignored — so the default is what was measured, and
437
468
  a repo below 2.19 adds `workflows/` back. The `medusa` scanner pack in `@geonosis/verify-arch`
438
- defaults the same way, and `examples/dielime.verify-arch.json` adds the third kind, which is what
469
+ defaults the same way, and `examples/medusa-storefront.verify-arch.json` adds the third kind, which is what
439
470
  keeps that repo's numbers identical to today's.
440
471
 
441
472
  **The class an AST rule wins over a text scan.** These three are the per-file halves of scans a
@@ -463,48 +494,48 @@ claiming one route, a `serviceName` that must be unique across every module. The
463
494
  decision rule, not a ranking: whole-graph facts are scans, per-file facts are rules, and the six
464
495
  that are both are defence in depth on purpose.
465
496
 
466
- **`step-declares-compensation-or-none` judges every step, not only mutating ones.** dielime's R5.6
497
+ **`step-declares-compensation-or-none` judges every step, not only mutating ones.** A Medusa storefront's R5.6
467
498
  scan restricts itself to steps whose text contains a write; the rule does not, because
468
499
  `noCompensation = !compensateFn` is true of every step and a read-only step declares
469
500
  `noCompensation: true` once and is done. The two are therefore not redundant: the scan is the
470
501
  repo-wide backstop, the rule is the finer instrument. `noCompensation: false` is the inferred state
471
502
  written out and is still reported.
472
503
 
473
- ### `store-route-scopes-tenant-data` (dielime only) — Medusa-specific
504
+ ### `store-route-scopes-tenant-data` (a Medusa storefront only) — Medusa-specific
474
505
 
475
506
  `/api/store/**/route.ts` is a Medusa convention. It is in `backend-medusa`, and also in
476
507
  `tenant-scoping` because that is what it enforces.
477
508
 
478
- ### `no-logic-in-component-files` — dielime's narrowed version
509
+ ### `no-logic-in-component-files` — a Medusa storefront's narrowed version
479
510
 
480
- during.day's version also reported type re-exports and custom hooks in component files. Both are
511
+ A Workers + D1 app's version also reported type re-exports and custom hooks in component files. Both are
481
512
  covered by dedicated rules in the union (`no-type-definitions-in-components`,
482
- `no-hook-in-component-disguise`), and during.day's own suite does not test this rule. dielime's
513
+ `no-hook-in-component-disguise`), and a Workers + D1 app's own suite does not test this rule. A Medusa storefront's
483
514
  narrower "one public component per file" version is the one under test, and the one kept.
484
515
 
485
- ### `tissue-must-compose` — dielime's, minus its backstop
516
+ ### `tissue-must-compose` — a Medusa storefront's, minus its backstop
486
517
 
487
- dielime added a "renders ANY PascalCase tag" backstop. during.day has a test proving that backstop
518
+ A Medusa storefront added a "renders ANY PascalCase tag" backstop. A Workers + D1 app has a test proving that backstop
488
519
  lets a tissue satisfy the gate by rendering a component it imported as a **type** — precisely the
489
- lint-theater the rule exists for. The backstop is dropped; dielime's broader `COMPOSITION_PATTERNS`
520
+ lint-theater the rule exists for. The backstop is dropped; a Medusa storefront's broader `COMPOSITION_PATTERNS`
490
521
  (feature barrels, `.parts`/`.sections` siblings, react-email primitives) already recognise real
491
- arrangements, and dielime's fixtures stay green without it.
522
+ arrangements, and a Medusa storefront's fixtures stay green without it.
492
523
 
493
524
  ### `cell-must-not-compose-cell` — the union's two halves collided on the barrel
494
525
 
495
- The kit's version is dielime's scope (`(?:^|/)cells/`, so the rule reaches a `packages/ui` tree with
496
- no `features/<name>/` segment) plus during.day's visitors (`ExportAllDeclaration` and
526
+ The kit's version is a Medusa storefront's scope (`(?:^|/)cells/`, so the rule reaches a `packages/ui` tree with
527
+ no `features/<name>/` segment) plus a Workers + D1 app's visitors (`ExportAllDeclaration` and
497
528
  `ExportNamedDeclaration`, so a cell cannot launder an import through `export … from`). Each half is
498
529
  right. Together, unguarded, they reported every `cells/index.ts` barrel — because a barrel is
499
- nothing but `export … from` lines naming sibling cells. dielime's first adoption produced 32 such
530
+ nothing but `export … from` lines naming sibling cells. A Medusa storefront's first adoption produced 32 such
500
531
  errors, and every one of them was a file obeying `cells-folder-index-is-barrel`, which is also an
501
532
  error and demands exactly that shape.
502
533
 
503
534
  Neither fork could see it:
504
535
 
505
- - **dielime** had the broad scope but visited `ImportDeclaration` only, so the re-exports in its
536
+ - **A Medusa storefront** had the broad scope but visited `ImportDeclaration` only, so the re-exports in its
506
537
  barrels were invisible to the rule;
507
- - **during.day** visited all three, but scoped to `features/[^/]+/cells/`, and it has no
538
+ - **A Workers + D1 app** visited all three, but scoped to `features/[^/]+/cells/`, and it has no
508
539
  `cells/index.ts` or `cells/dynamic.ts*` anywhere in `apps/` or `packages/` — nothing to collide
509
540
  with.
510
541
 
@@ -519,7 +550,7 @@ holds `cells/`.
519
550
  ### `no-raw-html-atoms` — the tier it was always documented for (0.3.0)
520
551
 
521
552
  Its own description said levels 2-6 and it fired at every level, `atoms/` included, where a raw
522
- `<button>` is not a violation but the whole point of the file. dielime armed it by switching the rule
553
+ `<button>` is not a violation but the whole point of the file. A Medusa storefront armed it by switching the rule
523
554
  off over `**/atoms/**` in its own config: a repo carrying the kit's defect as configuration.
524
555
 
525
556
  It is now silent inside the atom tier — `(?:^|/)(?:features/[^/]+/)?atoms/`, the merged pattern from
@@ -547,17 +578,17 @@ rule the option refusal exists to stop, one option along.
547
578
 
548
579
  | Rule | Option | Unconfigured |
549
580
  |---|---|---|
550
- | `organelle-dependency` | `sharedFeatures` | `['shared']` — during.day's `charts`/`document` are config, not law |
581
+ | `organelle-dependency` | `sharedFeatures` | `['shared']` — a Workers + D1 app's `charts`/`document` are config, not law |
551
582
  | `mutating-step-declares-retry` | `retryKey` | `maxRetries` — Medusa's key. `@geonosis/workflows` spells the same budget `retry: { limit, backoff }` (`create-step.ts`), and with the key compiled in a correctly budgeted step on that engine was a finding for ever |
552
583
  | `mutating-step-declares-retry` | `retryLimitKey` | `limit` — the key that holds the NUMBER when the budget is an object. #191: reading only the bare form let `retry: { limit: 0 }` pass, which is the same silence as `maxRetries: 0` |
553
584
  | `worker-handles-are-scoped` | `within` + `wrapper` | **refuses the run** — `withCells` and `/apps/api/src/` were compiled in, so the rule served one repo's layout and one repo's vocabulary and a scope opener called anything else read as unscoped for ever |
554
- | `subpath-over-barrel` | `packages` (`{ name, subpaths: { <subpath>: string[] } }[]`) | **refuses the run** — which of a repo's own packages expose subpaths, and which names live under each, is a map only that package's owner has |
585
+ | `subpath-over-barrel` | `packages` (`{ name, subpaths?: { <subpath>: string[] } }[]`) | optional — the map is the package's own `package.json` `exports`, read for the repo's own packages (linked into `node_modules`, or private) with the names behind each door read off its entry file. `{ name, subpaths }` replaces the map for one package; `{ name }` alone reads a third-party package too |
555
586
  | `durable-body-reads-through-steps` | `ports` | **refuses the run** — what a body reaches the outside world through is the repo's vocabulary; `durableKey` / `durableValue` (default `durable: true`) say how a workflow declares itself durable |
556
- | `unique-query-step-name` | `queryStepCalls` | **refuses the run** — the two names it shipped with are Medusa framework calls with no counterpart, so on the org engine the rule had zero reach while reading as enforced (026-W2, measured through the binary). There is no second spelling to inherit: `@geonosis/workflows` has no query-step primitive, and a repeated step name there is DEFINED rather than lost — `reserve`, `reserve#2` in call order (`engine.ts:124`) |
587
+ | `unique-query-step-name` | `queryStepCalls` | **refuses the run** — the two names it shipped with are Medusa framework calls with no counterpart, so on `@geonosis/workflows` the rule had zero reach while reading as enforced (026-W2, measured through the binary). There is no second spelling to inherit: `@geonosis/workflows` has no query-step primitive, and a repeated step name there is DEFINED rather than lost — `reserve`, `reserve#2` in call order (the engine's `reserveName`) |
557
588
  | `no-raw-html-atoms` | (positional) element→atom map | **refuses the run** |
558
589
  | `no-raw-html-atoms` | `scope` (second positional, `{ scope: ["cells", …] }`) | every file except the atom tier |
559
590
 
560
- Package specifiers in diagnostics (`@dielime/ui`, `@during/utils/time`) were made generic, and
591
+ Package specifiers in diagnostics (`@shop/ui`, `@app/utils/time`) were made generic, and
561
592
  `CLAUDE.md §…` citations were removed: they pointed at a law file this package does not ship, and
562
593
  the remedy now lives in each rule's `fixShape`.
563
594
 
@@ -577,7 +608,7 @@ fix to how a path is read had to be found rule by rule.
577
608
  | `lib/imports.ts` | `sourceOf` (import · `export … from` · `export * from` · `import()` · `require()`), `isTypeOnly`, `resolveRelative` |
578
609
  | `lib/visitors.ts` | `onImportSource` — one visitor set over all five shapes — and `mergeVisitors` |
579
610
  | `lib/direction-rule.ts` | `directionRule()`: a direction rule as a declaration (scope, forbidden, messages) |
580
- | `lib/options.ts` | `requireOption` — an enabled rule that cannot fire refuses the run |
611
+ | `lib/options.ts` | `requireOption` — an enabled rule that cannot fire says so; `reportingOncePerRun` — the registry reports it as one finding a run |
581
612
  | `lib/cells-barrel.ts` | the barrel list two rules must agree on |
582
613
  | `lib/literal-call.ts` | the string a `saga('x', …)`-shaped call names |
583
614
 
@@ -588,7 +619,7 @@ declaration and holds no mechanism at all.
588
619
  `onImportSource` visits a type-only import only when the rule counts one. Four do:
589
620
  `no-orm-outside-db` and `dialect-through-the-seam` say why in their docs (a type borrowed from the
590
621
  ORM is a line a replacement would still have to rewrite), and `atom-no-deps` and
591
- `molecule-atoms-only` inherited it from dielime — an atom that borrows a domain's type is an atom
622
+ `molecule-atoms-only` inherited it from a Medusa storefront — an atom that borrows a domain's type is an atom
592
623
  that knows a domain. The other six direction rules skip them, as they always did.
593
624
 
594
625
  ### Direction rules resolve relative imports (0.4.0)
@@ -597,7 +628,7 @@ that knows a domain. The other six direction rules skip them, as they always did
597
628
  importing file before judging it. The other eight direction rules read the string as written, so
598
629
  `../../billing/organelles/totals` — which names no feature and no tier until it is resolved — was
599
630
  invisible to them. Whether a relative climb across a feature boundary was a finding depended on
600
- which rule happened to look at it. during.day's A2b probe found this; it was one resolver in two
631
+ which rule happened to look at it. A Workers + D1 app's A2b probe found this; it was one resolver in two
601
632
  rules and no resolver in eight.
602
633
 
603
634
  **Now:** the classification every direction rule judges by goes through `resolveRelative` once, in
@@ -624,9 +655,9 @@ argued — `proofs/018-T3-relative-imports/`:
624
655
  | tree | A | B | lost | gained |
625
656
  |---|---:|---:|---:|---:|
626
657
  | the kit's corpus | 99 | 101 | 0 | 2 — `no-cross-feature-stores` and `organelle-dependency`, on the two fixtures written RED for this |
627
- | dielime, its own config, `apps packages` | 135 | 135 | 0 | 0 |
658
+ | A Medusa storefront, its own config, `apps packages` | 135 | 135 | 0 | 0 |
628
659
 
629
- dielime's zero is not luck: that tree has seven relative cross-feature imports of a store or an
660
+ A Medusa storefront's zero is not luck: that tree has seven relative cross-feature imports of a store or an
630
661
  organelle, five of them into `shared/organelles/*` (which `organelle-dependency`'s `sharedFeatures`
631
662
  option permits) and two of them in **cells** reaching another feature's organelle — which no rule
632
663
  here polices, because `organelle-dependency` is scoped to `organelles/` and `no-cross-feature-stores`
@@ -641,7 +672,7 @@ and says nothing about who imports it. `no-cross-feature-stores` is scoped to ce
641
672
  but only judges `features/<x>/stores/`. So a **cell** importing another feature's **organelle** — a
642
673
  hook holding that feature's state, pulled across the boundary — passed both.
643
674
 
644
- It is not hypothetical. dielime, on its own config, with the rule enabled through a temporary config
675
+ It is not hypothetical. A Medusa storefront, on its own config, with the rule enabled through a temporary config
645
676
  and nothing else changed:
646
677
 
647
678
  | site | import |
@@ -672,42 +703,42 @@ theme whether or not it is organised biologically. `backend-d1` exists because `
672
703
  them would be a repo fact wearing a preset's name. `backend-workflows-cf` exists for the same reason:
673
704
  `step-opens-its-own-cell` is true of a durable run on Cloudflare Workflows and false of an inline
674
705
  one. It was called `backend-sagaflow-cf` until 1.5.0, after the engine it was written for; the old
675
- name resolves to the same rules for ONE release and says so once, then goes. `package-subpaths` exists because `subpath-over-barrel` is inert until a repo writes out which
676
- of ITS packages expose subpaths and which names live under each — a map only that package's owner
677
- has, and one a kit that guessed at it would keep stale on somebody else's behalf.
706
+ name resolves to the same rules for ONE release and says so once, then goes. `package-subpaths` exists because `subpath-over-barrel` reads the repo's own packages off disk —
707
+ their `exports` and the entry file behind each door — which is a fact about that repo's layout, not
708
+ every repo's.
678
709
 
679
- | Rule | Preset(s) | Requires | Core | In dielime | In during.day | Same code |
710
+ | Rule | Preset(s) | Requires | Core | In a Medusa storefront | In a Workers + D1 app | Same code |
680
711
  |---|---|---|---|---|---|---|
681
712
  | `atom-no-deps` | frontend | — | `directionRule` | yes | yes | diverged |
682
713
  | `boundary-time-is-primitive` | backend-workflows | `types` | `nodes` + `paths` + `options` | — | — | **written here** (D-050, #145) |
683
- | `cell-must-be-stateful` | frontend | — | `paths` | yes | — | dielime only |
714
+ | `cell-must-be-stateful` | frontend | — | `paths` | yes | — | a Medusa storefront only |
684
715
  | `cell-must-not-compose-cell` | frontend | — | `directionRule` + `cells-barrel` | yes | yes | diverged |
685
716
  | `cell-no-cross-feature-organelles` | frontend | — | `directionRule` | — | — | **written here** |
686
717
  | `cell-no-tissues` | frontend | — | `directionRule` | yes | yes | diverged |
687
718
  | `cells-folder-index-is-barrel` | frontend | — | `paths` + `cells-barrel` | yes | yes | diverged |
688
719
  | `compound-must-be-stateless` | frontend | — | `paths` | yes | yes | diverged |
689
720
  | `compound-no-stores` | frontend | — | `directionRule` | yes | yes | diverged |
690
- | `constants-in-constants-file` | backend-workflows | — | `paths` | — | yes | during only |
691
- | `dialect-through-the-seam` | backend-workflows | — | `onImportSource` + `mergeVisitors` + `paths` | — | yes | during only |
721
+ | `constants-in-constants-file` | backend-workflows | — | `paths` | — | yes | a Workers + D1 app only |
722
+ | `dialect-through-the-seam` | backend-workflows | `seam` | `onImportSource` + `mergeVisitors` + `paths` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
692
723
  | `durable-body-reads-through-steps` | backend-workflows | `ports` | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (#167, design-026 §8) |
693
724
  | `effect-hook-naming` | frontend | — | `paths` | yes | yes | diverged |
694
725
  | `emit-declares-attempts` | backend-medusa | — | `options` | — | — | **written here** |
695
726
  | `font-roles-only` | themekit | `roles` | `paths` + `options` + `styling` | — | — | **written here** (U2) |
696
727
  | `layer-walls` | tooling | `layers` | `onImportSource` + `imports` + `paths` + `options` | — | — | **written here** |
697
728
  | `list-take-without-order` | backend-medusa | — | `nodes` + `options` | — | — | **written here** (#21) |
698
- | `max-comment-density` | comment-ceiling | — | `paths` | — | yes | during only |
729
+ | `max-comment-density` | comment-ceiling | — | `paths` | — | yes | a Workers + D1 app only |
699
730
  | `molecule-atoms-only` | frontend | — | `directionRule` | yes | yes | diverged |
700
731
  | `molecule-must-compose` | frontend | — | — | yes | yes | diverged |
701
- | `mutating-step-declares-retry` | backend-medusa + backend-workflows | — | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (dielime scan R5.2) |
702
- | `named-when-block` | backend-medusa | — | `nodes` + `options` | — | — | **written here** (dielime scan R5.5) |
732
+ | `mutating-step-declares-retry` | backend-medusa + backend-workflows | — | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (a Medusa storefront's scan R5.2) |
733
+ | `named-when-block` | backend-medusa | — | `nodes` + `options` | — | — | **written here** (a Medusa storefront's scan R5.5) |
703
734
  | `next-route-segment-is-thin-delegate` | frontend | — | `paths` | yes | yes | diverged |
704
735
  | `no-unregistered-plugin-dir` | integrations | `roots` | `options` + `paths` | — | — | **written here** (Midday's 8 dead dirs; flat seam #36) |
705
736
  | `no-mock-db-in-integration` | testbed | `integrationDirs` | `options` + `paths` | — | — | **written here** (Midday's 35 mocked router suites) |
706
- | `router-schema-parity` | backend-trpc | `routers` | `options` + `paths` | — | — | **written here** (during.day's mirror) |
707
- | `no-brand-names` | frontend | `brands` | `paths` + `options` | yes | — | dielime only |
737
+ | `router-schema-parity` | backend-trpc | `routers` | `options` + `paths` | — | — | **written here** (a Workers + D1 app's mirror) |
738
+ | `no-brand-names` | frontend | `brands` | `paths` + `options` | yes | — | a Medusa storefront only |
708
739
  | `no-card-shaped-div` | frontend | — | — | yes | yes | diverged |
709
740
  | `no-cross-feature-stores` | frontend | — | `directionRule` | yes | yes | diverged |
710
- | `no-d1-transaction` | backend-d1 | — | `paths` | — | yes | during only |
741
+ | `no-d1-transaction` | backend-d1 | — | `paths` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
711
742
  | `no-date-string-compare` | backend-workflows | — | `nodes` + `options` | — | — | **written here** (#20) |
712
743
  | `no-duplicate-jsx-patterns` | frontend | — | `paths` | yes | yes | diverged |
713
744
  | `no-generic-utility-package` | tooling | — | `paths` + `options` | — | — | **written here** (#171) |
@@ -716,47 +747,47 @@ has, and one a kit that guessed at it would keep stale on somebody else's behalf
716
747
  | `no-inert-hidden-jsx` | frontend | — | `paths` | yes | yes | diverged |
717
748
  | `no-inline-data-in-jsx` | frontend | — | `paths` | yes | yes | diverged |
718
749
  | `no-invalid-feature-folders` | frontend | — | `paths` | yes | yes | diverged |
719
- | `no-loader-side-effect-in-index-file` | backend-medusa | — | `nodes` + `paths` + `options` | — | — | **written here** (dielime scan #15442) |
750
+ | `no-loader-side-effect-in-index-file` | backend-medusa | — | `nodes` + `paths` + `options` | — | — | **written here** (a Medusa storefront's scan #15442) |
720
751
  | `no-logic-in-component-files` | frontend | — | `paths` | yes | yes | diverged |
721
- | `plugin-route-namespaced` | backend-medusa | `packages` | `paths` + `options` | — | — | **written here** (dielime scan R3.8) |
722
- | `no-orm-outside-db` | backend-workflows | — | `directionRule` | — | yes | during only |
752
+ | `plugin-route-namespaced` | backend-medusa | `packages` | `paths` + `options` | — | — | **written here** (a Medusa storefront's scan R3.8) |
753
+ | `no-orm-outside-db` | backend-workflows | — | `directionRule` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
723
754
  | `no-raw-html-atoms` | frontend | `element→atom map` | `tiers` + `options` | yes | yes | diverged |
724
- | `no-raw-sql-outside-allowed` | backend-workflows | — | `paths` | — | yes | during only |
755
+ | `no-raw-sql-outside-allowed` | backend-workflows | — | `paths` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
725
756
  | `no-react-namespace` | frontend | — | — | yes | yes | **identical** |
726
757
  | `no-renamed-html-props` | frontend | — | — | yes | yes | **identical** |
727
758
  | `no-render-prop-reader` | frontend | — | `paths` | yes | yes | diverged |
728
- | `no-swallowed-catch` | failure-paths | — | `nodes` + `paths` + `options` | — | — | **written here** (#3) |
729
- | `no-runtime-dead-affordance` | frontend | — | `options` | — | — | **written here** (#5) |
759
+ | `no-swallowed-catch` | failure-paths | `paths` | `nodes` + `paths` + `options` | — | — | **written here** (#3) |
760
+ | `no-runtime-dead-affordance` | frontend | `element→atom map` | `options` | — | — | **written here** (#5) |
730
761
  | `no-trivial-wrapper-component` | frontend | — | `paths` | yes | yes | diverged |
731
762
  | `no-ts-in-bio-folders` | frontend | — | `paths` | yes | yes | diverged |
732
763
  | `no-type-definitions-in-components` | frontend | — | `paths` | yes | yes | diverged |
733
- | `no-void-port` | backend-workflows | — | — | — | yes | during only |
764
+ | `no-void-port` | backend-workflows | — | — | — | yes | a Workers + D1 app only |
734
765
  | `organelle-dependency` | frontend | — | `directionRule` | yes | yes | diverged |
735
766
  | `organelle-single-source` | frontend | — | `onImportSource` + `mergeVisitors` + `paths` | yes | yes | diverged |
736
- | `queries-require-org-scope` | tenant-scoping | — | `paths` | — | yes | during only |
737
- | `queue-loop-is-the-library` | backend-workflows | — | — | — | yes | during only |
738
- | `route-no-inline-mutation` | backend-medusa | — | `nodes` + `paths` + `comments` + `options` | — | — | **written here** (dielime scan R5.1) |
767
+ | `queries-require-org-scope` | tenant-scoping | `queries` | `paths` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
768
+ | `queue-loop-is-the-library` | backend-workflows | — | — | — | yes | a Workers + D1 app only |
769
+ | `route-no-inline-mutation` | backend-medusa | — | `nodes` + `paths` + `comments` + `options` | — | — | **written here** (a Medusa storefront's scan R5.1) |
739
770
  | `service-name-not-reserved` | backend-medusa | — | `nodes` + `objects` + `options` | — | — | **written here** (Medusa source) |
740
- | `ssot-no-inline-facts` | backend-workflows | — | `paths` | — | yes | during only |
741
- | `ssot-no-process-env` | backend-workflows | — | `paths` | — | yes | during only |
771
+ | `ssot-no-inline-facts` | backend-workflows | — | `paths` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
772
+ | `ssot-no-process-env` | backend-workflows | — | `paths` | — | yes | a Workers + D1 app only |
742
773
  | `step-declares-compensation-or-none` | backend-medusa + backend-workflows | — | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (Medusa source) |
743
- | `step-opens-its-own-cell` | backend-workflows-cf | `within` | `nodes` + `objects` + `paths` + `options` | — | yes | during only, re-targeted |
744
- | `store-route-scopes-tenant-data` | backend-medusa + tenant-scoping | — | `nodes` + `imports` + `paths` + `options` | yes | — | dielime only, de-hardcoded |
745
- | `subpath-over-barrel` | package-subpaths | `packages` | `nodes` + `options` | — | — | **written here** (#180, during.day asked) |
746
- | `subscriber-declares-id` | backend-medusa | — | `objects` + `paths` + `options` | — | — | **written here** (dielime scan R5.4) |
747
- | `tables-declare-their-plane` | backend-workflows + tenant-scoping | — | `paths` + `literal-call` | — | yes | during only |
748
- | `tenant-tables-carry-org-id` | tenant-scoping | — | `paths` + `literal-call` | — | yes | during only |
774
+ | `step-opens-its-own-cell` | backend-workflows-cf | `within` | `nodes` + `objects` + `paths` + `options` | — | yes | a Workers + D1 app only, re-targeted |
775
+ | `store-route-scopes-tenant-data` | backend-medusa + tenant-scoping | — | `nodes` + `imports` + `paths` + `options` | yes | — | a Medusa storefront only, de-hardcoded |
776
+ | `subpath-over-barrel` | package-subpaths | — | `nodes` + `options` | — | — | **written here** (#180, a Workers + D1 app asked) |
777
+ | `subscriber-declares-id` | backend-medusa | — | `objects` + `paths` + `options` | — | — | **written here** (a Medusa storefront's scan R5.4) |
778
+ | `tables-declare-their-plane` | backend-workflows + tenant-scoping | `schema` | `paths` + `literal-call` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
779
+ | `tenant-tables-carry-org-id` | tenant-scoping | `tenant` | `paths` + `literal-call` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
749
780
  | `test-no-clock-polling-for-promise-flips` | testbed | — | `nodes` + `paths` + `options` | — | — | **written here** (#4) |
750
781
  | `tokens-only-colors` | themekit | `tokenPrefix` | `paths` + `options` + `styling` + `colors` | — | — | **written here** (U2) |
751
- | `time-through-the-door` | backend-workflows | `door` | `paths` + `options` | — | yes | during only, de-hardcoded |
782
+ | `time-through-the-door` | backend-workflows | `door` | `paths` + `options` | — | yes | a Workers + D1 app only, de-hardcoded |
752
783
  | `tissue-must-compose` | frontend | — | `paths` | yes | yes | diverged |
753
784
  | `tissue-no-data-props` | frontend | — | `paths` | yes | yes | diverged |
754
785
  | `tissue-no-hooks` | frontend | — | `paths` | yes | yes | diverged |
755
786
  | `tissue-no-organelles` | frontend | — | `directionRule` | yes | yes | diverged |
756
787
  | `tissue-no-stores` | frontend | — | `directionRule` | yes | yes | diverged |
757
788
  | `todo-names-its-plan` | tooling | `markers` | `comments` + `options` | — | — | **written here** (W28) |
758
- | `unique-query-step-name` | backend-medusa | `queryStepCalls` | `nodes` + `objects` + `options` | — | — | **written here** (dielime scan R4.3) |
759
- | `worker-handles-are-scoped` | backend-d1 | `within` | `paths` + `options` | — | yes (as `cell-handles-are-scoped`) | during only, de-hardcoded |
789
+ | `unique-query-step-name` | backend-medusa | `queryStepCalls` | `nodes` + `objects` + `options` | — | — | **written here** (a Medusa storefront's scan R4.3) |
790
+ | `worker-handles-are-scoped` | backend-d1 | `within` | `paths` + `options` | — | yes (as `cell-handles-are-scoped`) | a Workers + D1 app only, de-hardcoded |
760
791
  | `zustand-v5-best-practices` | frontend | — | — | yes | yes | **identical** |
761
792
 
762
793
  Preset sizes: frontend 36 · backend-workflows 16 · backend-medusa 12 · tenant-scoping 4 ·
@@ -770,13 +801,13 @@ the retry rule only since its key became an option — so both presets carry the
770
801
  of `backend-medusa` would take a live rule away from the repo it was written for, which is a
771
802
  downgrade wearing a preset change. `unique-query-step-name` stays Medusa's alone: the collision it
772
803
  reports is that framework's step-handler map last-winning, and a repeated step name on the org
773
- engine is DEFINED rather than lost (`reserve`, `reserve#2`, `engine.ts:124`).
804
+ engine is DEFINED rather than lost (`reserve`, `reserve#2`, the engine's `reserveName`).
774
805
 
775
806
  ## The build
776
807
 
777
- Two builds existed. during.day's `package.json` resolves `.` through a `bun` condition to
808
+ Two builds existed. A Workers + D1 app's `package.json` resolves `.` through a `bun` condition to
778
809
  `src/index.ts` and only falls back to `dist/index.js`; without bun, oxlint gets the fallback anyway.
779
- dielime's is the plain one — `tsup … --format esm --no-dts --platform node --out-dir dist` with
810
+ A Medusa storefront's is the plain one — `tsup … --format esm --no-dts --platform node --out-dir dist` with
780
811
  `"exports": { ".": "./dist/index.js" }` — and that is what this package uses.
781
812
 
782
813
  It is **source-proven**, not assumed: `src/rules/fixtures.test.ts` runs the real `oxlint` binary with
@@ -787,7 +818,7 @@ fire" case would pass silently without that assertion.
787
818
  ## `fixShape`
788
819
 
789
820
  Every rule exports `{ meta, create, fixShape }`. `fixShape` is a short Markdown string in the voice
790
- of during.day's `during-code` skill: what the gate rejects and what to write instead.
821
+ of a Workers + D1 app's own code skill: what the gate rejects and what to write instead.
791
822
  `pnpm generate:skill` assembles them into `skills/geonosis-code/SKILL.md`, grouped by preset. A
792
823
  staleness test fails when the committed skill is not what the rules generate, so the skill and the
793
824
  rules cannot drift.
@@ -807,7 +838,7 @@ enforce them on every file — which is what both source repos reported against
807
838
  2026-08-30. `@geonosis/doctor`'s `exercised` check reads `probe` off the plugin a config loads,
808
839
  writes it into a copy of the corpus, and runs again.
809
840
 
810
- A probe is a CLAIM, so `src/probes.test.ts` tries to break every one of them through the real
841
+ A probe is a CLAIM, so `src/probes.bin.test.ts` tries to break every one of them through the real
811
842
  binary, under consumer-shaped options, and requires exactly one finding. Two rules deserve the
812
843
  detail:
813
844
 
@@ -816,7 +847,7 @@ detail:
816
847
  registry, so its probe declares BOTH — the directory under `roots`, and a registry carrying no
817
848
  string literal at all, so nothing in it can name the directory beside it. It was a single file
818
849
  until 2026-08-30 and fired nothing, which was the truth about the probe and a lie about every
819
- consumer: dielime enforces this rule at error over four registries and read as a permanent FAIL
850
+ consumer: a Medusa storefront enforces this rule at error over four registries and read as a permanent FAIL
820
851
  for it.
821
852
  - `layer-walls` and the path-regex options are inverted by `lib/probe.ts`'s `pathMatching`, which
822
853
  builds a path back out of the pattern and then TESTS it against that pattern, refusing with a
@@ -843,7 +874,7 @@ option not named here is one no rule reads.
843
874
  | `compound-must-be-stateless` | — |
844
875
  | `compound-no-stores` | — |
845
876
  | `constants-in-constants-file` | — |
846
- | `dialect-through-the-seam` | — |
877
+ | `dialect-through-the-seam` | `exempt`, `seam` |
847
878
  | `durable-body-reads-through-steps` | `durableKey`, `durableValue`, `ports`, `stepFactories`, `stepProxies`, `workflowFactories` |
848
879
  | `effect-hook-naming` | — |
849
880
  | `emit-declares-attempts` | `budgetHelpers`, `emitters` |
@@ -859,7 +890,7 @@ option not named here is one no rule reads.
859
890
  | `no-brand-names` | `allowedSubstrings`, `brands` |
860
891
  | `no-card-shaped-div` | — |
861
892
  | `no-cross-feature-stores` | — |
862
- | `no-d1-transaction` | — |
893
+ | `no-d1-transaction` | `except`, `transacts` |
863
894
  | `no-date-string-compare` | `fieldPattern` |
864
895
  | `no-duplicate-jsx-patterns` | — |
865
896
  | `no-generic-utility-package` | `entries`, `names` |
@@ -871,36 +902,36 @@ option not named here is one no rule reads.
871
902
  | `no-loader-side-effect-in-index-file` | `directories`, `resourceFactories` |
872
903
  | `no-logic-in-component-files` | — |
873
904
  | `no-mock-db-in-integration` | `dbModules`, `integrationDirs`, `mockNames` |
874
- | `no-orm-outside-db` | — |
905
+ | `no-orm-outside-db` | `dataLayer` |
875
906
  | `no-raw-html-atoms` | (a positional list), `scope` |
876
- | `no-raw-sql-outside-allowed` | — |
907
+ | `no-raw-sql-outside-allowed` | `allow` |
877
908
  | `no-react-namespace` | — |
878
909
  | `no-renamed-html-props` | — |
879
910
  | `no-render-prop-reader` | — |
880
911
  | `no-runtime-dead-affordance` | (a positional list) |
881
912
  | `no-swallowed-catch` | `paths` |
882
913
  | `no-trivial-wrapper-component` | — |
883
- | `no-ts-in-bio-folders` | — |
914
+ | `no-ts-in-bio-folders` | `extraFolders` |
884
915
  | `no-type-definitions-in-components` | — |
885
916
  | `no-unregistered-plugin-dir` | `manifests`, `registry`, `roots` |
886
- | `no-void-port` | — |
917
+ | `no-void-port` | `engineCallbacks` |
887
918
  | `organelle-dependency` | `sharedFeatures` |
888
919
  | `organelle-single-source` | — |
889
920
  | `plugin-route-namespaced` | `packages`, `scopes` |
890
- | `queries-require-org-scope` | — |
921
+ | `queries-require-org-scope` | `queries`, `tenantKey` |
891
922
  | `queue-loop-is-the-library` | — |
892
923
  | `route-no-inline-mutation` | `inlineMutationOk`, `mutationPrefixes`, `paths` |
893
924
  | `router-schema-parity` | `ignore`, `mounts`, `routers`, `schemas`, `suffix` |
894
925
  | `service-name-not-reserved` | `moduleFactories`, `reserved` |
895
- | `ssot-no-inline-facts` | `declarationCallees` |
896
- | `ssot-no-process-env` | — |
926
+ | `ssot-no-inline-facts` | `declarationCallees`, `owners` |
927
+ | `ssot-no-process-env` | `doors` |
897
928
  | `step-declares-compensation-or-none` | `stepFactories` |
898
929
  | `step-opens-its-own-cell` | `engine`, `hosts`, `scopeKey`, `within`, `wrapper` |
899
930
  | `store-route-scopes-tenant-data` | `entities`, `evidenceCalls`, `routes`, `scopingCalls` |
900
931
  | `subpath-over-barrel` | `packages` |
901
932
  | `subscriber-declares-id` | `paths` |
902
- | `tables-declare-their-plane` | — |
903
- | `tenant-tables-carry-org-id` | — |
933
+ | `tables-declare-their-plane` | `schema` |
934
+ | `tenant-tables-carry-org-id` | `tenant`, `tenantKey` |
904
935
  | `test-no-clock-polling-for-promise-flips` | `pollers`, `testGlobs` |
905
936
  | `time-through-the-door` | `door`, `exempt`, `exemptModules`, `libraries` |
906
937
  | `tissue-must-compose` | — |