@geonosis/oxlint-plugin-biological-architecture 2.1.0 → 2.3.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 (34) hide show
  1. package/RULES.md +51 -16
  2. package/corpus/.oxlintrc.json +43 -9
  3. package/corpus/affordances/bad-dead-control.tsx +6 -0
  4. package/corpus/affordances/good-live-control.tsx +12 -0
  5. package/corpus/api/store/history/find-orders.ts +8 -0
  6. package/corpus/api/store/history/route.ts +7 -0
  7. package/corpus/api/store/track/find-tracked-order.ts +12 -0
  8. package/corpus/api/store/track/route.ts +10 -0
  9. package/corpus/dates/bad-string-order.ts +7 -0
  10. package/corpus/dates/good-time-order.ts +7 -0
  11. package/corpus/failure/good-refresh.ts +17 -0
  12. package/corpus/failure/logged.ts +6 -0
  13. package/corpus/failure/refresh.ts +4 -0
  14. package/corpus/failure/reload.ts +6 -0
  15. package/corpus/manifest.json +6 -0
  16. package/corpus/medusa/queries/bad-unordered-page.ts +20 -0
  17. package/corpus/medusa/queries/good-ordered-page.ts +23 -0
  18. package/corpus/messaging/providers/email-gmail.ts +2 -0
  19. package/corpus/messaging/providers/sms-twilio.ts +2 -0
  20. package/corpus/messaging/providers/whatsapp-360dialog.ts +2 -0
  21. package/corpus/messaging/registry.ts +5 -0
  22. package/corpus/packages/pricing/package.json +5 -0
  23. package/corpus/packages/pricing/src/index.ts +2 -0
  24. package/corpus/packages/utils/package.json +5 -0
  25. package/corpus/packages/utils/src/index.ts +4 -0
  26. package/corpus/suites/__tests__/awaits.ts +17 -0
  27. package/corpus/suites/__tests__/settles.ts +14 -0
  28. package/dist/{chunk-LTDP2733.js → chunk-KV7JRNAP.js} +7 -2
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.js +787 -52
  31. package/dist/presets.d.ts +3 -0
  32. package/dist/presets.js +1 -1
  33. package/package.json +2 -2
  34. /package/corpus/api/store/orders/{scoped.route.ts → scoped/route.ts} +0 -0
package/RULES.md CHANGED
@@ -9,11 +9,13 @@ Read from the two source trees on 2026-08-29:
9
9
  One plugin name, two rule sets, no shared code. **33 rules had the same filename in both trees; only
10
10
  3 of those 33 were byte-identical.** A fix in one never reached the other.
11
11
 
12
- The union here is **54 rules**, plus twenty-two the kit wrote for itself — `layer-walls`,
13
- `emit-declares-attempts`, `cell-no-cross-feature-organelles`, the `backend-medusa` ten of 1.0.0,
12
+ The union here is **54 rules**, plus twenty-eight the kit wrote for itself — `layer-walls`,
13
+ `emit-declares-attempts`, `cell-no-cross-feature-organelles`, the `backend-medusa` ten,
14
14
  `no-unregistered-plugin-dir` + `router-schema-parity`, the `themekit` two,
15
15
  `no-mock-db-in-integration`, `todo-names-its-plan`, `boundary-time-is-primitive`,
16
- `durable-body-reads-through-steps` and `subpath-over-barrel` — **76**.
16
+ `durable-body-reads-through-steps`, `subpath-over-barrel` and the six of the W67 batch — **82**.
17
+ Every one of the twenty-eight is marked **written here** in the inventory, and the table below is
18
+ held against that column rather than kept by hand.
17
19
 
18
20
  | | count |
19
21
  |---|---|
@@ -22,13 +24,8 @@ The union here is **54 rules**, plus twenty-two the kit wrote for itself — `la
22
24
  | dielime only | 3 |
23
25
  | during.day only | 18 |
24
26
  | **union** | **54** |
25
- | written here (`layer-walls`, `emit-declares-attempts`, `cell-no-cross-feature-organelles`) | 3 |
26
- | written here, `backend-medusa` (1.0.0) | 7 |
27
- | written here, the integration seam (1.0.0) | 2 |
28
- | **total** | **66** |
29
- | written here, `backend-medusa` (1.0.0) | 10 |
30
- | **total** | **67** |
31
- | written here, `themekit` (1.0.0) | 2 |
27
+ | written here | 28 |
28
+ | **total** | **82** |
32
29
 
33
30
  ## How divergence was resolved
34
31
 
@@ -88,6 +85,16 @@ decided the boundary or counts what accumulated on the far side — a consumer's
88
85
  took on seven unchartered exports, one of them a write, lint-free, in eleven commits. A list of
89
86
  module names is a number a reader can hold: a repo has one door, or it has three and knows it.
90
87
 
88
+ Every other rule that grants by path now takes the same enumeration beside its pattern (#139).
89
+ `time-through-the-door`'s `exempt`, `font-roles-only`'s `allow` and `tokens-only-colors`' `allow` —
90
+ measured from the option table, and the only three path GRANTS the plugin has; `within`, `paths`,
91
+ `roots` and `routers` are scopes — each accept `exemptModules`, a list of modules named in full,
92
+ matched by path or by the tail a repo writes them as and never as a pattern. The patterns still
93
+ work. What changed is that they are counted: each of those rules declares `meta.grants`
94
+ (`{ paths, modules }`), and `geonosis-doctor`'s `exercised` line WARNs with the number of files the
95
+ pattern exempts in THIS tree beside the number the enumeration names — so a grant nobody decided
96
+ stops being a grant nobody can see.
97
+
91
98
  ### An enabled rule that cannot fire **refuses the run** (0.2.0)
92
99
 
93
100
  This replaces the "does nothing until configured" posture below, and it is the one breaking change
@@ -710,20 +717,23 @@ has, and one a kit that guessed at it would keep stale on somebody else's behalf
710
717
  | `emit-declares-attempts` | backend-medusa | — | `options` | — | — | **written here** |
711
718
  | `font-roles-only` | themekit | `roles` | `paths` + `options` + `styling` | — | — | **written here** (U2) |
712
719
  | `layer-walls` | tooling | `layers` | `onImportSource` + `imports` + `paths` + `options` | — | — | **written here** |
720
+ | `list-take-without-order` | backend-medusa | — | `nodes` + `options` | — | — | **written here** (#21) |
713
721
  | `max-comment-density` | comment-ceiling | — | `paths` | — | yes | during only |
714
722
  | `molecule-atoms-only` | frontend | — | `directionRule` | yes | yes | diverged |
715
723
  | `molecule-must-compose` | frontend | — | — | yes | yes | diverged |
716
724
  | `mutating-step-declares-retry` | backend-medusa + backend-workflows | — | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (dielime scan R5.2) |
717
725
  | `named-when-block` | backend-medusa | — | `nodes` + `options` | — | — | **written here** (dielime scan R5.5) |
718
726
  | `next-route-segment-is-thin-delegate` | frontend | — | `paths` | yes | yes | diverged |
719
- | `no-unregistered-plugin-dir` | integrations | `roots` | `options` + `paths` | — | — | **written here** (Midday's 8 dead dirs) |
727
+ | `no-unregistered-plugin-dir` | integrations | `roots` | `options` + `paths` | — | — | **written here** (Midday's 8 dead dirs; flat seam #36) |
720
728
  | `no-mock-db-in-integration` | testbed | `integrationDirs` | `options` + `paths` | — | — | **written here** (Midday's 35 mocked router suites) |
721
729
  | `router-schema-parity` | backend-trpc | `routers` | `options` + `paths` | — | — | **written here** (during.day's mirror) |
722
730
  | `no-brand-names` | frontend | `brands` | `paths` + `options` | yes | — | dielime only |
723
731
  | `no-card-shaped-div` | frontend | — | — | yes | yes | diverged |
724
732
  | `no-cross-feature-stores` | frontend | — | `directionRule` | yes | yes | diverged |
725
733
  | `no-d1-transaction` | backend-d1 | — | `paths` | — | yes | during only |
734
+ | `no-date-string-compare` | backend-workflows | — | `nodes` + `options` | — | — | **written here** (#20) |
726
735
  | `no-duplicate-jsx-patterns` | frontend | — | `paths` | yes | yes | diverged |
736
+ | `no-generic-utility-package` | tooling | — | `paths` + `options` | — | — | **written here** (#171) |
727
737
  | `no-hook-in-component-disguise` | frontend | — | `paths` | yes | yes | diverged |
728
738
  | `no-index-count-as-exact` | backend-medusa | — | `nodes` + `objects` + `options` | — | — | **written here** (Medusa source) |
729
739
  | `no-inert-hidden-jsx` | frontend | — | `paths` | yes | yes | diverged |
@@ -738,6 +748,8 @@ has, and one a kit that guessed at it would keep stale on somebody else's behalf
738
748
  | `no-react-namespace` | frontend | — | — | yes | yes | **identical** |
739
749
  | `no-renamed-html-props` | frontend | — | — | yes | yes | **identical** |
740
750
  | `no-render-prop-reader` | frontend | — | `paths` | yes | yes | diverged |
751
+ | `no-swallowed-catch` | failure-paths | — | `nodes` + `paths` + `options` | — | — | **written here** (#3) |
752
+ | `no-runtime-dead-affordance` | frontend | — | `options` | — | — | **written here** (#5) |
741
753
  | `no-trivial-wrapper-component` | frontend | — | `paths` | yes | yes | diverged |
742
754
  | `no-ts-in-bio-folders` | frontend | — | `paths` | yes | yes | diverged |
743
755
  | `no-type-definitions-in-components` | frontend | — | `paths` | yes | yes | diverged |
@@ -752,11 +764,12 @@ has, and one a kit that guessed at it would keep stale on somebody else's behalf
752
764
  | `ssot-no-process-env` | backend-workflows | — | `paths` | — | yes | during only |
753
765
  | `step-declares-compensation-or-none` | backend-medusa + backend-workflows | — | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (Medusa source) |
754
766
  | `step-opens-its-own-cell` | backend-workflows-cf | `within` | `nodes` + `objects` + `paths` + `options` | — | yes | during only, re-targeted |
755
- | `store-route-scopes-tenant-data` | backend-medusa + tenant-scoping | — | `paths` | yes | — | dielime only |
767
+ | `store-route-scopes-tenant-data` | backend-medusa + tenant-scoping | — | `nodes` + `imports` + `paths` + `options` | yes | — | dielime only, de-hardcoded |
756
768
  | `subpath-over-barrel` | package-subpaths | `packages` | `nodes` + `options` | — | — | **written here** (#180, during.day asked) |
757
769
  | `subscriber-declares-id` | backend-medusa | — | `objects` + `paths` + `options` | — | — | **written here** (dielime scan R5.4) |
758
770
  | `tables-declare-their-plane` | backend-workflows + tenant-scoping | — | `paths` + `literal-call` | — | yes | during only |
759
771
  | `tenant-tables-carry-org-id` | tenant-scoping | — | `paths` + `literal-call` | — | yes | during only |
772
+ | `test-no-clock-polling-for-promise-flips` | testbed | — | `nodes` + `paths` + `options` | — | — | **written here** (#4) |
760
773
  | `tokens-only-colors` | themekit | `tokenPrefix` | `paths` + `options` + `styling` + `colors` | — | — | **written here** (U2) |
761
774
  | `time-through-the-door` | backend-workflows | `door` | `paths` + `options` | — | yes | during only, de-hardcoded |
762
775
  | `tissue-must-compose` | frontend | — | `paths` | yes | yes | diverged |
@@ -859,8 +872,9 @@ option not named here is one no rule reads.
859
872
  | `durable-body-reads-through-steps` | `durableKey`, `durableValue`, `ports`, `stepFactories`, `workflowFactories` |
860
873
  | `effect-hook-naming` | — |
861
874
  | `emit-declares-attempts` | `budgetHelpers`, `emitters` |
862
- | `font-roles-only` | `allow`, `prefix`, `roles` |
875
+ | `font-roles-only` | `allow`, `exemptModules`, `prefix`, `roles` |
863
876
  | `layer-walls` | `externals`, `layers`, `typeOnly` |
877
+ | `list-take-without-order` | `callees`, `orderKeys` |
864
878
  | `max-comment-density` | — |
865
879
  | `molecule-atoms-only` | — |
866
880
  | `molecule-must-compose` | — |
@@ -871,7 +885,9 @@ option not named here is one no rule reads.
871
885
  | `no-card-shaped-div` | — |
872
886
  | `no-cross-feature-stores` | — |
873
887
  | `no-d1-transaction` | — |
888
+ | `no-date-string-compare` | `fieldPattern` |
874
889
  | `no-duplicate-jsx-patterns` | — |
890
+ | `no-generic-utility-package` | `entries`, `names` |
875
891
  | `no-hook-in-component-disguise` | — |
876
892
  | `no-index-count-as-exact` | `estimateFields`, `labelKey` |
877
893
  | `no-inert-hidden-jsx` | — |
@@ -886,6 +902,8 @@ option not named here is one no rule reads.
886
902
  | `no-react-namespace` | — |
887
903
  | `no-renamed-html-props` | — |
888
904
  | `no-render-prop-reader` | — |
905
+ | `no-runtime-dead-affordance` | (a positional list) |
906
+ | `no-swallowed-catch` | `paths` |
889
907
  | `no-trivial-wrapper-component` | — |
890
908
  | `no-ts-in-bio-folders` | — |
891
909
  | `no-type-definitions-in-components` | — |
@@ -903,21 +921,38 @@ option not named here is one no rule reads.
903
921
  | `ssot-no-process-env` | — |
904
922
  | `step-declares-compensation-or-none` | `stepFactories` |
905
923
  | `step-opens-its-own-cell` | `engine`, `hosts`, `scopeKey`, `within`, `wrapper` |
906
- | `store-route-scopes-tenant-data` | |
924
+ | `store-route-scopes-tenant-data` | `entities`, `evidenceCalls`, `routes`, `scopingCalls` |
907
925
  | `subpath-over-barrel` | `packages` |
908
926
  | `subscriber-declares-id` | `paths` |
909
927
  | `tables-declare-their-plane` | — |
910
928
  | `tenant-tables-carry-org-id` | — |
911
- | `time-through-the-door` | `door`, `exempt`, `libraries` |
929
+ | `test-no-clock-polling-for-promise-flips` | `pollers`, `testGlobs` |
930
+ | `time-through-the-door` | `door`, `exempt`, `exemptModules`, `libraries` |
912
931
  | `tissue-must-compose` | — |
913
932
  | `tissue-no-data-props` | — |
914
933
  | `tissue-no-hooks` | — |
915
934
  | `tissue-no-organelles` | — |
916
935
  | `tissue-no-stores` | — |
917
936
  | `todo-names-its-plan` | `id`, `markers` |
918
- | `tokens-only-colors` | `allow`, `tokenPrefix` |
937
+ | `tokens-only-colors` | `allow`, `exemptModules`, `tokenPrefix` |
919
938
  | `unique-query-step-name` | `queryStepCalls`, `workflowFactories` |
920
939
  | `worker-handles-are-scoped` | `within`, `wrapper` |
921
940
  | `zustand-v5-best-practices` | — |
922
941
 
923
942
  <!-- END GENERATED -->
943
+
944
+ <!-- GENERATED: the floor each rule’s pattern belongs to -->
945
+
946
+ ## The floor each rule’s pattern belongs to
947
+
948
+ A rule here lints a pattern one of the kit’s floors implements. While the floor is PENDING the
949
+ rule is a requirement with nothing to install behind it — a repo enabling it writes the pattern
950
+ itself, and the doctor says so on the `exercised` line.
951
+
952
+ | Rule | Floor | Shipped |
953
+ |---|---|---|
954
+ | `document-sagas-are-generic` | `document` | PENDING |
955
+ | `documents-share-one-table` | `document` | PENDING |
956
+ | `step-opens-its-own-cell` | `workflows` | yes |
957
+
958
+ <!-- END GENERATED FLOORS -->
@@ -106,6 +106,7 @@
106
106
  ]
107
107
  }
108
108
  ],
109
+ "biological-architecture/list-take-without-order": "error",
109
110
  "biological-architecture/max-comment-density": "error",
110
111
  "biological-architecture/molecule-atoms-only": "error",
111
112
  "biological-architecture/molecule-must-compose": "error",
@@ -126,7 +127,9 @@
126
127
  "biological-architecture/no-card-shaped-div": "error",
127
128
  "biological-architecture/no-cross-feature-stores": "error",
128
129
  "biological-architecture/no-d1-transaction": "error",
130
+ "biological-architecture/no-date-string-compare": "error",
129
131
  "biological-architecture/no-duplicate-jsx-patterns": "error",
132
+ "biological-architecture/no-generic-utility-package": "error",
130
133
  "biological-architecture/no-hook-in-component-disguise": "error",
131
134
  "biological-architecture/no-index-count-as-exact": "error",
132
135
  "biological-architecture/no-inert-hidden-jsx": "error",
@@ -149,6 +152,14 @@
149
152
  }
150
153
  ],
151
154
  "biological-architecture/no-orm-outside-db": "error",
155
+ "biological-architecture/no-swallowed-catch": [
156
+ "error",
157
+ {
158
+ "paths": [
159
+ "(?:__fixtures__|corpus)/failure/"
160
+ ]
161
+ }
162
+ ],
152
163
  "biological-architecture/no-raw-html-atoms": [
153
164
  "error",
154
165
  [
@@ -161,6 +172,16 @@
161
172
  ],
162
173
  "biological-architecture/no-raw-sql-outside-allowed": "error",
163
174
  "biological-architecture/no-react-namespace": "error",
175
+ "biological-architecture/no-runtime-dead-affordance": [
176
+ "error",
177
+ [
178
+ {
179
+ "element": "button",
180
+ "atom": "Button",
181
+ "importPath": "@kit/ui/atoms/button"
182
+ }
183
+ ]
184
+ ],
164
185
  "biological-architecture/no-renamed-html-props": "error",
165
186
  "biological-architecture/no-render-prop-reader": "error",
166
187
  "biological-architecture/no-trivial-wrapper-component": "error",
@@ -170,11 +191,16 @@
170
191
  "error",
171
192
  {
172
193
  "roots": [
173
- "integrations/providers"
194
+ "integrations/providers",
195
+ {
196
+ "path": "messaging/providers",
197
+ "files": true
198
+ }
174
199
  ],
175
200
  "registry": [
176
201
  "integrations/registry.ts",
177
- "integrations/registry.fulfillment.ts"
202
+ "integrations/registry.fulfillment.ts",
203
+ "messaging/registry.ts"
178
204
  ],
179
205
  "manifests": [
180
206
  "index.ts"
@@ -235,7 +261,14 @@
235
261
  "wrapper": "(?:__fixtures__|corpus)/workflows/step\\.ts$"
236
262
  }
237
263
  ],
238
- "biological-architecture/store-route-scopes-tenant-data": "error",
264
+ "biological-architecture/store-route-scopes-tenant-data": [
265
+ "error",
266
+ {
267
+ "evidenceCalls": [
268
+ "assertTrackingEvidence"
269
+ ]
270
+ }
271
+ ],
239
272
  "biological-architecture/subpath-over-barrel": [
240
273
  "error",
241
274
  {
@@ -260,12 +293,13 @@
260
293
  "biological-architecture/subscriber-declares-id": "error",
261
294
  "biological-architecture/tables-declare-their-plane": "error",
262
295
  "biological-architecture/tenant-tables-carry-org-id": "error",
296
+ "biological-architecture/test-no-clock-polling-for-promise-flips": "error",
263
297
  "biological-architecture/time-through-the-door": [
264
298
  "error",
265
299
  {
266
300
  "door": "(?:__fixtures__|corpus)/time/door\\.ts$",
267
- "exempt": [
268
- "(?:__fixtures__|corpus)/time/generated/"
301
+ "exemptModules": [
302
+ "time/generated/from-openapi.ts"
269
303
  ]
270
304
  }
271
305
  ],
@@ -310,8 +344,8 @@
310
344
  "body",
311
345
  "display"
312
346
  ],
313
- "allow": [
314
- "__fixtures__/tokens/theme/"
347
+ "exemptModules": [
348
+ "tokens/theme/palette.tsx"
315
349
  ]
316
350
  }
317
351
  ],
@@ -322,8 +356,8 @@
322
356
  "--mc-",
323
357
  "--theme-"
324
358
  ],
325
- "allow": [
326
- "__fixtures__/tokens/theme/"
359
+ "exemptModules": [
360
+ "tokens/theme/palette.tsx"
327
361
  ]
328
362
  }
329
363
  ]
@@ -0,0 +1,6 @@
1
+ // BREACH: it renders, it looks pressable, and pressing it does nothing.
2
+ import { Button } from '@kit/ui/atoms/button'
3
+
4
+ export const DielineDownload = (): JSX.Element => (
5
+ <Button type="button">Download the dieline</Button>
6
+ )
@@ -0,0 +1,12 @@
1
+ // ACCEPTED: one carries a handler, one hands the press to the child, one is the form's submit.
2
+ import { Button } from '@kit/ui/atoms/button'
3
+
4
+ export const Downloads = ({ download }: { download: () => void }): JSX.Element => (
5
+ <form>
6
+ <Button onClick={download}>Download</Button>
7
+ <Button asChild>
8
+ <a href="/dieline.pdf">Open</a>
9
+ </Button>
10
+ <Button type="submit">Save</Button>
11
+ </form>
12
+ )
@@ -0,0 +1,8 @@
1
+ // BREACH, one file sideways: the read the route delegates, narrowed to nobody.
2
+ export const findOrders = async (query: {
3
+ graph: (c: unknown) => Promise<{ data: unknown[] }>
4
+ }): Promise<unknown[]> => {
5
+ const { data } = await query.graph({ entity: 'order', fields: ['id'] })
6
+
7
+ return data
8
+ }
@@ -0,0 +1,7 @@
1
+ // BREACH: the route itself names no entity — the read is in the module beside it.
2
+ import { findOrders } from './find-orders'
3
+
4
+ export async function GET(req: unknown, res: { json: (b: unknown) => void }): Promise<void> {
5
+ const query = { graph: async (_c: unknown) => ({ data: [] as unknown[] }) }
6
+ res.json({ orders: await findOrders(query) })
7
+ }
@@ -0,0 +1,12 @@
1
+ // ACCEPTED: a public route scoped by caller-supplied evidence — a reference and an email.
2
+ export const findTrackedOrder = async (
3
+ query: { graph: (c: unknown) => Promise<{ data: unknown[] }> },
4
+ input: { email: string; reference: string },
5
+ ): Promise<unknown> => {
6
+ assertTrackingEvidence(input)
7
+ const { data } = await query.graph({ entity: 'order', fields: ['id'] })
8
+
9
+ return data[0]
10
+ }
11
+
12
+ declare function assertTrackingEvidence(input: { email: string; reference: string }): void
@@ -0,0 +1,10 @@
1
+ // ACCEPTED: the evidence check the config names stands in for a session.
2
+ import { findTrackedOrder } from './find-tracked-order'
3
+
4
+ export async function GET(
5
+ req: { body: { email: string; reference: string } },
6
+ res: { json: (b: unknown) => void },
7
+ ): Promise<void> {
8
+ const query = { graph: async (_c: unknown) => ({ data: [] as unknown[] }) }
9
+ res.json({ order: await findTrackedOrder(query, req.body) })
10
+ }
@@ -0,0 +1,7 @@
1
+ // BREACH: "newest first" becomes "starts with F" — the sort is stable, plausible and wrong.
2
+ type Load = { created_at: Date; id: string; updatedAt: Date }
3
+
4
+ export const newestFirst = (loads: Load[]): Load[] =>
5
+ [...loads].sort((a, b) => String(b.created_at).localeCompare(String(a.created_at)))
6
+
7
+ export const changedSince = (load: Load, cut: string): boolean => `${load.updatedAt}` > cut
@@ -0,0 +1,7 @@
1
+ // ACCEPTED: one orders by the time, the other by the ISO string the API returned.
2
+ type Load = { created_at: Date; id: string; shipped_at: string }
3
+
4
+ export const newestFirst = (loads: Load[]): Load[] =>
5
+ [...loads].sort((a, b) => b.created_at.getTime() - a.created_at.getTime())
6
+
7
+ export const shippedSince = (load: Load, cut: string): boolean => load.shipped_at > cut
@@ -0,0 +1,17 @@
1
+ // ACCEPTED: the failure is written where the caller reads it, and the other one is rethrown.
2
+ export const refresh = (
3
+ load: () => Promise<void>,
4
+ setError: (message: string) => void,
5
+ ): void => {
6
+ load().catch((error: Error) => {
7
+ setError(error.message)
8
+ })
9
+ }
10
+
11
+ export const reload = async (load: () => Promise<void>): Promise<void> => {
12
+ try {
13
+ await load()
14
+ } catch (error) {
15
+ throw error
16
+ }
17
+ }
@@ -0,0 +1,6 @@
1
+ // BREACH: a log is not a state the caller can read — downstream, this refresh succeeded.
2
+ export const refresh = (load: () => Promise<void>): void => {
3
+ load().catch((error: Error) => {
4
+ console.error('refresh failed', error)
5
+ })
6
+ }
@@ -0,0 +1,4 @@
1
+ // BREACH: the refresh that failed reads as a refresh that succeeded.
2
+ export const refresh = (load: () => Promise<void>): void => {
3
+ load().catch(() => {})
4
+ }
@@ -0,0 +1,6 @@
1
+ // BREACH: an empty catch block, and a handler whose only answer is a log.
2
+ export const reload = async (load: () => Promise<void>): Promise<void> => {
3
+ try {
4
+ await load()
5
+ } catch {}
6
+ }
@@ -19,6 +19,7 @@
19
19
  "biological-architecture/emit-declares-attempts",
20
20
  "biological-architecture/font-roles-only",
21
21
  "biological-architecture/layer-walls",
22
+ "biological-architecture/list-take-without-order",
22
23
  "biological-architecture/max-comment-density",
23
24
  "biological-architecture/molecule-atoms-only",
24
25
  "biological-architecture/molecule-must-compose",
@@ -29,7 +30,9 @@
29
30
  "biological-architecture/no-card-shaped-div",
30
31
  "biological-architecture/no-cross-feature-stores",
31
32
  "biological-architecture/no-d1-transaction",
33
+ "biological-architecture/no-date-string-compare",
32
34
  "biological-architecture/no-duplicate-jsx-patterns",
35
+ "biological-architecture/no-generic-utility-package",
33
36
  "biological-architecture/no-hook-in-component-disguise",
34
37
  "biological-architecture/no-index-count-as-exact",
35
38
  "biological-architecture/no-inert-hidden-jsx",
@@ -44,6 +47,8 @@
44
47
  "biological-architecture/no-react-namespace",
45
48
  "biological-architecture/no-renamed-html-props",
46
49
  "biological-architecture/no-render-prop-reader",
50
+ "biological-architecture/no-runtime-dead-affordance",
51
+ "biological-architecture/no-swallowed-catch",
47
52
  "biological-architecture/no-trivial-wrapper-component",
48
53
  "biological-architecture/no-ts-in-bio-folders",
49
54
  "biological-architecture/no-type-definitions-in-components",
@@ -66,6 +71,7 @@
66
71
  "biological-architecture/subscriber-declares-id",
67
72
  "biological-architecture/tables-declare-their-plane",
68
73
  "biological-architecture/tenant-tables-carry-org-id",
74
+ "biological-architecture/test-no-clock-polling-for-promise-flips",
69
75
  "biological-architecture/time-through-the-door",
70
76
  "biological-architecture/tissue-must-compose",
71
77
  "biological-architecture/tissue-no-data-props",
@@ -0,0 +1,20 @@
1
+ // BREACH: the stage before this one wrote the plan, and this page never sees it.
2
+ declare const query: { graph: (config: unknown) => Promise<{ data: unknown[] }> }
3
+ declare function listPlannedOrders(filters: unknown, config: unknown): Promise<unknown[]>
4
+
5
+ export const latestPlan = async (orderId: string): Promise<unknown> => {
6
+ const { data } = await query.graph({
7
+ entity: 'planned_order',
8
+ fields: ['id'],
9
+ filters: { order_id: orderId },
10
+ pagination: { take: 1 },
11
+ })
12
+
13
+ return data[0]
14
+ }
15
+
16
+ export const firstOpen = async (orderId: string): Promise<unknown> => {
17
+ const rows = await listPlannedOrders({ order_id: orderId }, { take: 1 })
18
+
19
+ return rows[0]
20
+ }
@@ -0,0 +1,23 @@
1
+ // ACCEPTED: the page names the column it is a page of, so the first row is the first row.
2
+ declare const query: { graph: (config: unknown) => Promise<{ data: unknown[] }> }
3
+ declare function listPlannedOrders(filters: unknown, config: unknown): Promise<unknown[]>
4
+
5
+ export const latestPlan = async (orderId: string): Promise<unknown> => {
6
+ const { data } = await query.graph({
7
+ entity: 'planned_order',
8
+ fields: ['id'],
9
+ filters: { order_id: orderId },
10
+ pagination: { order: { created_at: 'DESC' }, take: 1 },
11
+ })
12
+
13
+ return data[0]
14
+ }
15
+
16
+ export const firstOpen = async (orderId: string): Promise<unknown> => {
17
+ const rows = await listPlannedOrders(
18
+ { order_id: orderId },
19
+ { order: { created_at: 'ASC' }, take: 1 },
20
+ )
21
+
22
+ return rows[0]
23
+ }
@@ -0,0 +1,2 @@
1
+ // ACCEPTED: the registry names this file.
2
+ export default { channel: 'email' }
@@ -0,0 +1,2 @@
1
+ // ACCEPTED: the registry names this file too.
2
+ export default { channel: 'sms' }
@@ -0,0 +1,2 @@
1
+ // BREACH: a provider file nothing reaches — it reads as a channel and behaves as dead weight.
2
+ export default { channel: 'whatsapp' }
@@ -0,0 +1,5 @@
1
+ // The flat seam's registry: providers are FILES here, one per channel.
2
+ export const providers = {
3
+ email: './providers/email-gmail',
4
+ sms: './providers/sms-twilio',
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@corpus/pricing",
3
+ "version": "0.0.0",
4
+ "private": true
5
+ }
@@ -0,0 +1,2 @@
1
+ // ACCEPTED: named for what it does, so a wrong import is obviously wrong.
2
+ export const priceOf = (cents: number, quantity: number): number => cents * quantity
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@corpus/utils",
3
+ "version": "0.0.0",
4
+ "private": true
5
+ }
@@ -0,0 +1,4 @@
1
+ // BREACH: named for nothing, so nothing can be refused entry and everything arrived.
2
+ export const formatMoney = (cents: number): string => `${(cents / 100).toFixed(2)}`
3
+ export const SUPPORT_EMAIL = 'support@corpus.example'
4
+ export const slugify = (value: string): string => value.toLowerCase().replaceAll(' ', '-')
@@ -0,0 +1,17 @@
1
+ // ACCEPTED: the promise is awaited where it was created, and the assertion runs once after it.
2
+ declare function waitFor(check: () => void): Promise<void>
3
+ declare function expect(value: unknown): { toBe: (other: unknown) => void }
4
+
5
+ export const savesTheCart = async (
6
+ save: () => Promise<void>,
7
+ subject: { done: boolean },
8
+ ): Promise<void> => {
9
+ await save()
10
+ expect(subject.done).toBe(true)
11
+ }
12
+
13
+ export const rendersTheRow = async (subject: { done: boolean }): Promise<void> => {
14
+ await waitFor(() => {
15
+ expect(subject.done).toBe(true)
16
+ })
17
+ }
@@ -0,0 +1,14 @@
1
+ // BREACH: the test holds the promise and polls the clock for the flip it decides.
2
+ declare function waitFor(check: () => void): Promise<void>
3
+ declare function expect(value: unknown): { toBe: (other: unknown) => void }
4
+
5
+ export const savesTheCart = async (
6
+ save: () => Promise<void>,
7
+ subject: { done: boolean },
8
+ ): Promise<void> => {
9
+ const settled = save()
10
+ await waitFor(() => {
11
+ expect(subject.done).toBe(true)
12
+ })
13
+ await settled
14
+ }
@@ -25,6 +25,7 @@ var FRONTEND = [
25
25
  "no-react-namespace",
26
26
  "no-renamed-html-props",
27
27
  "no-render-prop-reader",
28
+ "no-runtime-dead-affordance",
28
29
  "no-trivial-wrapper-component",
29
30
  "no-ts-in-bio-folders",
30
31
  "no-type-definitions-in-components",
@@ -45,6 +46,7 @@ var BACKEND_WORKFLOWS = [
45
46
  "documents-share-one-table",
46
47
  "durable-body-reads-through-steps",
47
48
  "mutating-step-declares-retry",
49
+ "no-date-string-compare",
48
50
  "no-orm-outside-db",
49
51
  "no-raw-sql-outside-allowed",
50
52
  "no-void-port",
@@ -71,6 +73,7 @@ var announceRename = (from) => {
71
73
  };
72
74
  var BACKEND_MEDUSA = [
73
75
  "emit-declares-attempts",
76
+ "list-take-without-order",
74
77
  "mutating-step-declares-retry",
75
78
  "named-when-block",
76
79
  "no-index-count-as-exact",
@@ -83,6 +86,7 @@ var BACKEND_MEDUSA = [
83
86
  "subscriber-declares-id",
84
87
  "unique-query-step-name"
85
88
  ];
89
+ var FAILURE_PATHS = ["no-swallowed-catch"];
86
90
  var INTEGRATIONS = ["no-unregistered-plugin-dir"];
87
91
  var BACKEND_TRPC = ["router-schema-parity"];
88
92
  var TENANT_SCOPING = [
@@ -91,9 +95,9 @@ var TENANT_SCOPING = [
91
95
  "tables-declare-their-plane",
92
96
  "tenant-tables-carry-org-id"
93
97
  ];
94
- var TOOLING = ["layer-walls", "todo-names-its-plan"];
98
+ var TOOLING = ["layer-walls", "no-generic-utility-package", "todo-names-its-plan"];
95
99
  var THEMEKIT = ["font-roles-only", "tokens-only-colors"];
96
- var TESTBED = ["no-mock-db-in-integration"];
100
+ var TESTBED = ["no-mock-db-in-integration", "test-no-clock-polling-for-promise-flips"];
97
101
  var PACKAGE_SUBPATHS = ["subpath-over-barrel"];
98
102
  var RULE_PREFIX = "biological-architecture";
99
103
  var asRules = (names) => Object.fromEntries(names.map((name) => [`${RULE_PREFIX}/${name}`, "error"]));
@@ -123,6 +127,7 @@ var PRESETS = {
123
127
  "backend-workflows": { rules: asRules(BACKEND_WORKFLOWS) },
124
128
  "backend-workflows-cf": { rules: asRules(BACKEND_WORKFLOWS_CF) },
125
129
  "comment-ceiling": COMMENT_CEILING,
130
+ "failure-paths": { rules: asRules(FAILURE_PATHS) },
126
131
  frontend: { rules: asRules(FRONTEND) },
127
132
  integrations: { rules: asRules(INTEGRATIONS) },
128
133
  "package-subpaths": { rules: asRules(PACKAGE_SUBPATHS) },
package/dist/index.d.ts CHANGED
@@ -88,6 +88,9 @@ declare const plugin: {
88
88
  rules: Record<string, "error">;
89
89
  };
90
90
  'comment-ceiling': Preset;
91
+ 'failure-paths': {
92
+ rules: Record<string, "error">;
93
+ };
91
94
  frontend: {
92
95
  rules: Record<string, "error">;
93
96
  };