@geonosis/oxlint-plugin-biological-architecture 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @geonosis/oxlint-plugin-biological-architecture
2
2
 
3
+ ## 2.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 08c9653: The kit knows no document kind. `document-sagas-are-generic` and `documents-share-one-table` are
8
+ gone from the plugin and from the `backend-workflows` preset, with their corpus and fixtures: both
9
+ took a list of one consumer's document kinds (`perKindPrefixes`, `perKindTables`) and enforced a
10
+ vocabulary the kit has no business holding — a document entity is whatever extends `Document`, and
11
+ the registry a consumer builds is the only enumeration there is. Their intent is held by types
12
+ now: a consumer's per-entity maps are folds over the registry, and a per-entity saga has nowhere to
13
+ be written once `documentWorkflows` mints them. Their ids stay for one release: oxlint refuses a
14
+ config that names a rule its plugin does not export, so a config naming either still parses, the
15
+ id reports nothing and says once that it is retired, and `geonosis doctor` WARNs naming it where
16
+ it would have failed. Migration: remove the block before the next major, when the ids go; the
17
+ doctor's floor table no longer lists them.
18
+
19
+ ### Patch Changes
20
+
21
+ - c8c5d51: `documentWorkflows(entity, { effects, ports })` at `@geonosis/document/workflows` — T3, the org
22
+ workflows over the engine, one body per Document: `create`, `save`, `setStatus`, `remove`, `send`
23
+ (durable), `convert`, `duplicate`, `share`, each named for the entity. A host decides
24
+ `effects.announce(fact)` — the product's events off a `DocumentFact` — and answers three ports,
25
+ `door`, `link` and `mail` (`Result<void, MailUndelivered>` from `better-result`); `beforeCreate`,
26
+ `conversion`, the mail words, `counter`, `actor`, `guard`, `now` and `seed` default, and a
27
+ counter-numbered entity or a template on a host that declares neither is refused by name. The
28
+ factory makes the two steps that reach the outside world, so the durable-body law holds on every
29
+ host. A third entry, like `./store`, so the core stays off every floor and browser-safe. The
30
+ Standard Schema builders the surface wrote for itself move to the core (`objectOf`, `enumOf`,
31
+ `optional`, `nullable`, `arrayOf`, `merge`, the scalars, `validate`) and `SchemaAdapter` is the
32
+ adapter's name (`SurfaceSchemaAdapter` stays as an alias). `@geonosis/workflows` and
33
+ `better-result` join the package's dependencies. The plugin's `document-sagas-are-generic` and
34
+ `documents-share-one-table` no longer carry `floor: pending`: the store shipped at 0.4 and the
35
+ workflows ship here. `documentIdentifiers` spells every identifier as a literal type off the name it was given (`CamelOf`, `DocumentIdentifiers`), so a consumer indexes a typed router map by the camel name without a cast. `documentEntities(map).keyed(pick)` answers one value per entity under its name, total by construction.
36
+
37
+ ## 2.6.1
38
+
3
39
  ## 2.6.0
4
40
 
5
41
  ## 2.5.3
package/README.md CHANGED
@@ -136,8 +136,6 @@ months. Carry these across, or turn the rule off:
136
136
  | `no-brand-names` | `{ brands, allowedSubstrings }` | refuses the run |
137
137
  | `organelle-dependency` | `{ sharedFeatures }` | only `shared` crosses a feature boundary |
138
138
  | `cell-no-cross-feature-organelles` | `{ sharedFeatures }` | only `shared` crosses a feature boundary |
139
- | `documents-share-one-table` | `{ perKindTables }` | refuses the run |
140
- | `document-sagas-are-generic` | `{ perKindPrefixes }` | refuses the run |
141
139
  | `step-opens-its-own-cell` | `{ engine, within, wrapper }` | refuses the run |
142
140
  | `no-raw-html-atoms` | the positional element→atom map | refuses the run |
143
141
  | `layer-walls` | `{ layers: [{ name, paths, mayImport }], externals? }` | refuses the run |
package/RULES.md CHANGED
@@ -9,11 +9,14 @@ 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-eight the kit wrote for itself `layer-walls`,
13
- `emit-declares-attempts`, `cell-no-cross-feature-organelles`, the `backend-medusa` ten,
14
- `no-unregistered-plugin-dir` + `router-schema-parity`, the `themekit` two,
15
- `no-mock-db-in-integration`, `todo-names-its-plan`, `boundary-time-is-primitive`,
16
- `durable-body-reads-through-steps`, `subpath-over-barrel` and the six of the W67 batch — **82**.
12
+ The union here is **52 rules** 54 when it was cut, less `document-sagas-are-generic` and
13
+ `documents-share-one-table`, retired in 2.7.0 because both enforced one consumer's document kinds —
14
+ plus twenty-eight the kit wrote for itself: `layer-walls`, `emit-declares-attempts`,
15
+ `cell-no-cross-feature-organelles`, the `backend-medusa` ten, `no-unregistered-plugin-dir` +
16
+ `router-schema-parity`, the `themekit` two, `no-mock-db-in-integration`, `todo-names-its-plan`,
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.
17
20
  Every one of the twenty-eight is marked **written here** in the inventory, and the table below is
18
21
  held against that column rather than kept by hand.
19
22
 
@@ -22,10 +25,10 @@ held against that column rather than kept by hand.
22
25
  | in both trees, byte-identical | 3 |
23
26
  | in both trees, **diverged** | 30 |
24
27
  | dielime only | 3 |
25
- | during.day only | 18 |
26
- | **union** | **54** |
28
+ | during.day only | 16 |
29
+ | **union** | **52** |
27
30
  | written here | 28 |
28
- | **total** | **82** |
31
+ | **total** | **80** |
29
32
 
30
33
  ## How divergence was resolved
31
34
 
@@ -513,26 +516,6 @@ Closing it also fixed a hole in the barrel rule's own pattern, which required a
513
516
  therefore no-op'd on the cwd-relative `cells/index.ts` oxlint reports for a package whose own root
514
517
  holds `cells/`.
515
518
 
516
- ### `documents-share-one-table` — matches `pgTable` as well as `sqliteTable`
517
-
518
- during.day's fork matched `literalNamedBy(node, 'sqliteTable')` and nothing else, and every tenant
519
- table on that tree has been `pgTable` since its plan 037. Its own probe: a well-formed per-kind
520
- table
521
-
522
- ```ts
523
- export const invoices = pgTable('invoices', { id: text('id').primaryKey(), orgId: orgIdColumn() })
524
- ```
525
-
526
- under `packages/db/src/schema/tenant/` produced **zero** findings from this rule; only the malformed
527
- variant was caught, by `tenant-tables-carry-org-id`, and by accident. The fork's rule had been dead
528
- since the tenant plane moved to Postgres.
529
-
530
- The kit matching both constructors is therefore a fix, not a widening for its own sake. It stands
531
- because the rule is about the second storage a base entity retired — which is true whatever the
532
- dialect — and because `dialect-through-the-seam` already says the dialect is a seam fact rather than
533
- a thing a document kind gets to have an opinion about. Both branches are exercised through the real
534
- binary in `src/rules/__fixtures__/packages/db/src/schema/tenant/`, so neither can go quiet again.
535
-
536
519
  ### `no-raw-html-atoms` — the tier it was always documented for (0.3.0)
537
520
 
538
521
  Its own description said levels 2-6 and it fired at every level, `atoms/` included, where a raw
@@ -567,13 +550,9 @@ rule the option refusal exists to stop, one option along.
567
550
  | `organelle-dependency` | `sharedFeatures` | `['shared']` — during.day's `charts`/`document` are config, not law |
568
551
  | `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 |
569
552
  | `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` |
570
- | `documents-share-one-table` | `perKindTables` | **refuses the run** |
571
553
  | `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 |
572
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 |
573
555
  | `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 |
574
- | `document-sagas-are-generic` | `perKindPrefixes` | **refuses the run** |
575
- | `document-sagas-are-generic` | `within` | **refuses the run** — `/packages/workflows/src/` was compiled in, so the same per-kind saga one directory over was silent and no other layout could enable the rule at all |
576
- | `document-sagas-are-generic` | `factories` | `['saga', 'createWorkflow']` — the callee was `saga` alone, so a per-entity `createWorkflow('invoice.create', …)` produced no finding (026-W2, measured). A name assembled from the entity is a template literal the rule cannot read: that is the mint the rule sends people to, not a gap in it |
577
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`) |
578
557
  | `no-raw-html-atoms` | (positional) element→atom map | **refuses the run** |
579
558
  | `no-raw-html-atoms` | `scope` (second positional, `{ scope: ["cells", …] }`) | every file except the atom tier |
@@ -710,8 +689,6 @@ has, and one a kit that guessed at it would keep stale on somebody else's behalf
710
689
  | `compound-no-stores` | frontend | — | `directionRule` | yes | yes | diverged |
711
690
  | `constants-in-constants-file` | backend-workflows | — | `paths` | — | yes | during only |
712
691
  | `dialect-through-the-seam` | backend-workflows | — | `onImportSource` + `mergeVisitors` + `paths` | — | yes | during only |
713
- | `document-sagas-are-generic` | backend-workflows | `perKindPrefixes` | `paths` + `options` + `literal-call` | — | yes | during only |
714
- | `documents-share-one-table` | backend-workflows | `perKindTables` | `paths` + `options` + `literal-call` | — | yes | during only |
715
692
  | `durable-body-reads-through-steps` | backend-workflows | `ports` | `nodes` + `objects` + `steps` + `options` | — | — | **written here** (#167, design-026 §8) |
716
693
  | `effect-hook-naming` | frontend | — | `paths` | yes | yes | diverged |
717
694
  | `emit-declares-attempts` | backend-medusa | — | `options` | — | — | **written here** |
@@ -867,8 +844,6 @@ option not named here is one no rule reads.
867
844
  | `compound-no-stores` | — |
868
845
  | `constants-in-constants-file` | — |
869
846
  | `dialect-through-the-seam` | — |
870
- | `document-sagas-are-generic` | `factories`, `perKindPrefixes`, `within` |
871
- | `documents-share-one-table` | `perKindTables` |
872
847
  | `durable-body-reads-through-steps` | `durableKey`, `durableValue`, `ports`, `stepFactories`, `stepProxies`, `workflowFactories` |
873
848
  | `effect-hook-naming` | — |
874
849
  | `emit-declares-attempts` | `budgetHelpers`, `emitters` |
@@ -951,8 +926,6 @@ itself, and the doctor says so on the `exercised` line.
951
926
 
952
927
  | Rule | Floor | Shipped |
953
928
  |---|---|---|
954
- | `document-sagas-are-generic` | `document` | PENDING |
955
- | `documents-share-one-table` | `document` | PENDING |
956
929
  | `step-opens-its-own-cell` | `workflows` | yes |
957
930
 
958
931
  <!-- END GENERATED FLOORS -->
@@ -37,24 +37,6 @@
37
37
  "biological-architecture/compound-no-stores": "error",
38
38
  "biological-architecture/constants-in-constants-file": "error",
39
39
  "biological-architecture/dialect-through-the-seam": "error",
40
- "biological-architecture/document-sagas-are-generic": [
41
- "error",
42
- {
43
- "perKindPrefixes": [
44
- "invoice"
45
- ],
46
- "within": "(?:__fixtures__|corpus)/domains/[^/]+/workflows/"
47
- }
48
- ],
49
- "biological-architecture/documents-share-one-table": [
50
- "error",
51
- {
52
- "perKindTables": [
53
- "invoices",
54
- "quotes"
55
- ]
56
- }
57
- ],
58
40
  "biological-architecture/durable-body-reads-through-steps": [
59
41
  "error",
60
42
  {
@@ -12,8 +12,6 @@
12
12
  "biological-architecture/compound-no-stores",
13
13
  "biological-architecture/constants-in-constants-file",
14
14
  "biological-architecture/dialect-through-the-seam",
15
- "biological-architecture/document-sagas-are-generic",
16
- "biological-architecture/documents-share-one-table",
17
15
  "biological-architecture/durable-body-reads-through-steps",
18
16
  "biological-architecture/effect-hook-naming",
19
17
  "biological-architecture/emit-declares-attempts",
@@ -42,8 +42,6 @@ var BACKEND_WORKFLOWS = [
42
42
  "boundary-time-is-primitive",
43
43
  "constants-in-constants-file",
44
44
  "dialect-through-the-seam",
45
- "document-sagas-are-generic",
46
- "documents-share-one-table",
47
45
  "durable-body-reads-through-steps",
48
46
  "mutating-step-declares-retry",
49
47
  "no-date-string-compare",
package/dist/index.d.ts CHANGED
@@ -36,6 +36,16 @@ type ProbeTree = {
36
36
  };
37
37
  type ProbeFn = (options: readonly unknown[]) => Probe | ProbeTree;
38
38
 
39
+ type RetiredRule = Rule & {
40
+ meta: Rule['meta'] & {
41
+ retired: {
42
+ reason: string;
43
+ since: string;
44
+ };
45
+ };
46
+ };
47
+ declare const RETIRED_RULES: Record<'document-sagas-are-generic' | 'documents-share-one-table', RetiredRule>;
48
+
39
49
  type Rule = {
40
50
  create: (context: never) => Record<string, unknown>;
41
51
  fixShape: string;
@@ -123,4 +133,4 @@ declare const plugin: {
123
133
  rules: Record<string, Rule>;
124
134
  };
125
135
 
126
- export { Preset, type Rule, type RuleName, plugin as default, rules };
136
+ export { Preset, RETIRED_RULES, type Rule, type RuleName, plugin as default, rules };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PRESUMED_ENGINES,
4
4
  RENAMED_TO,
5
5
  rulesOfPreset
6
- } from "./chunk-KV7JRNAP.js";
6
+ } from "./chunk-24K5232K.js";
7
7
 
8
8
  // src/index.ts
9
9
  import { createRequire } from "module";
@@ -257,16 +257,6 @@ var requireOption = (rule, value, option, accepts) => {
257
257
  }
258
258
  return value;
259
259
  };
260
- var requireOptionHere = (rule, value, option, file, accepts) => {
261
- if (isMissing(value)) {
262
- throw new OptionMissing(
263
- rule,
264
- option,
265
- `${refusalHeader(rule, option)} It is enabled over "${file}", which it would judge, and its \`${option}\` option is ${NOTHING}. Configure \`${option}\`, or turn the rule off for this tree.${accepts === void 0 ? "" : ` It accepts ${accepts}.`}`
266
- );
267
- }
268
- return value;
269
- };
270
260
  var raisedOver = /* @__PURE__ */ new Map();
271
261
  var fileOf2 = (context) => {
272
262
  const named = context?.filename;
@@ -884,155 +874,6 @@ configs. Everywhere else, use the dialect-free aliases the seam exports (\`Datab
884
874
  };
885
875
  var dialect_through_the_seam_default = dialectThroughTheSeam;
886
876
 
887
- // src/rules/document-sagas-are-generic.ts
888
- var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
889
- var RULE2 = "document-sagas-are-generic";
890
- var DEFAULT_FACTORIES = ["saga", "createWorkflow"];
891
- var perEntityNameOf = (node, factories) => {
892
- for (const factory of factories) {
893
- const name = literalNamedBy(node, factory);
894
- if (name !== null) return name;
895
- }
896
- return null;
897
- };
898
- var declaresAFlow = (text, factories) => factories.some((factory) => new RegExp(`\\b${escapeForRegex(factory)}\\s*\\(`).test(text));
899
- var documentSagasAreGeneric = {
900
- create(context) {
901
- const factories = requireOption(
902
- RULE2,
903
- context.options?.[0]?.factories ?? DEFAULT_FACTORIES,
904
- "factories"
905
- );
906
- const filename = normalise(context.filename);
907
- const named = context.options?.[0]?.within;
908
- const insideTree = named === void 0 || named === "" ? void 0 : new RegExp(named).test(filename);
909
- if (insideTree === false) return {};
910
- if (insideTree === void 0 && !declaresAFlow(context.sourceCode?.text ?? "", factories)) {
911
- return {};
912
- }
913
- const prefixes = requireOptionHere(
914
- RULE2,
915
- context.options?.[0]?.perKindPrefixes,
916
- "perKindPrefixes",
917
- filename
918
- );
919
- const within = requireOptionHere(RULE2, named, "within", filename);
920
- if (!new RegExp(within).test(filename)) return {};
921
- const perKindPrefix = new RegExp(`^(?:${prefixes.map(escapeForRegex).join("|")})\\.`);
922
- return {
923
- CallExpression(node) {
924
- const name = perEntityNameOf(node, factories);
925
- if (name === null || !perKindPrefix.test(name)) return;
926
- context.report({ data: { name }, messageId: "perKindSaga", node });
927
- }
928
- };
929
- },
930
- fixShape: `A saga named for one document kind (\`invoice.issue\`, \`quote.accept\`) is the hand-written fork the
931
- generic base entity replaced. Mint it from the generic factory \u2014 \`documentSagas(entity, effects)\` \u2014
932
- and pass the kind as data. The same holds for a workflow: \`createWorkflow('invoice.create', \u2026)\` is
933
- the fork, \`documentWorkflows(entity)\` is the mint. A name assembled from the entity \u2014 a template
934
- literal \u2014 is what minting looks like and is left alone by construction. Configure the kind vocabulary
935
- through \`perKindPrefixes\`, the tree that holds the flows through \`within\` (a regex over the
936
- filename), and \`factories\` if the repo declares them under other names; enabled without
937
- \`perKindPrefixes\` or \`within\` the rule refuses the run.`,
938
- meta: {
939
- floor: { name: "document", pending: true },
940
- docs: {
941
- description: "Document sagas are minted, not written: `documentSagas(entity, effects)` gives every entity that extends Document its create/save/setStatus/remove/send/convert/duplicate/share from one body. A saga or workflow named with a LITERAL for one entity is a fork; a name assembled from the entity is the mint and is unreadable here on purpose. `factories`, `perKindPrefixes` and `within` are options."
942
- },
943
- messages: {
944
- perKindSaga: '"{{name}}" is a hand-written flow for one entity. Every entity that extends Document gets its sagas from `documentSagas(entity, effects)` and its workflows from the same mint; put what this entity decides in its `Document.extend` definition or in its `DocumentEffects`.'
945
- },
946
- schema: [
947
- {
948
- additionalProperties: false,
949
- properties: {
950
- factories: { items: { type: "string" }, type: "array" },
951
- perKindPrefixes: { items: { type: "string" }, type: "array" },
952
- within: { type: "string" }
953
- },
954
- type: "object"
955
- }
956
- ],
957
- type: "problem"
958
- },
959
- /** Every half comes from the options now: the vocabulary, the tree, and the factory it is minted by. */
960
- probe(options) {
961
- const { perKindPrefixes = [], within = "" } = bagOf(options);
962
- const factory = firstOf(bagOf(options).factories, DEFAULT_FACTORIES);
963
- return {
964
- path: pathMatching(within, "geonosis-probe-saga.ts"),
965
- source: `import { ${factory} } from 'sagaflow-js'
966
-
967
- export const geonosisProbe = ${factory}('${perKindPrefixes[0] ?? ""}.geonosis-probe', {})
968
- `
969
- };
970
- }
971
- };
972
- var document_sagas_are_generic_default = documentSagasAreGeneric;
973
-
974
- // src/rules/documents-share-one-table.ts
975
- var TABLE_CALLEES = ["sqliteTable", "pgTable"];
976
- var documentsShareOneTable = {
977
- create(context) {
978
- const perKindTables = new Set(
979
- requireOption(
980
- "documents-share-one-table",
981
- context.options?.[0]?.perKindTables,
982
- "perKindTables"
983
- )
984
- );
985
- if (!/\/packages\/db\/src\/schema\//.test(normalise(context.filename))) return {};
986
- return {
987
- CallExpression(node) {
988
- for (const callee of TABLE_CALLEES) {
989
- const table = literalNamedBy(node, callee);
990
- if (table === null || !perKindTables.has(table)) continue;
991
- context.report({ data: { table }, messageId: "perKindTable", node });
992
- return;
993
- }
994
- }
995
- };
996
- },
997
- fixShape: `Every document kind is a row of the one \`documents\` table with a \`kind\` column, not a table of its
998
- own. A \`pgTable('invoices', \u2026)\` beside a \`pgTable('quotes', \u2026)\` is the second storage the base entity
999
- retired: write the row, not the table. Both constructors count \u2014 \`pgTable\` and \`sqliteTable\` \u2014 since
1000
- the dialect is a seam fact, not a reason for a kind to own storage. The forbidden table names are the rule's
1001
- \`perKindTables\` option; enabled without it the rule refuses the run rather than passing every
1002
- schema silently.`,
1003
- meta: {
1004
- floor: { name: "document", pending: true },
1005
- docs: {
1006
- description: "Documents share ONE table: `documents` holds the head every kind derives and the body its kind declares. A table named for one kind of document is a second storage."
1007
- },
1008
- messages: {
1009
- perKindTable: '"{{table}}" is a table for one kind of document. Every kind is a row of `documents` \u2014 add the kind to `documentEntities` and let its `headOf` fill the columns.'
1010
- },
1011
- schema: [
1012
- {
1013
- additionalProperties: false,
1014
- properties: {
1015
- perKindTables: { items: { type: "string" }, type: "array" }
1016
- },
1017
- type: "object"
1018
- }
1019
- ],
1020
- type: "problem"
1021
- },
1022
- /** The schema directory is compiled in; the forbidden table names are this repo's vocabulary. */
1023
- probe(options) {
1024
- const { perKindTables = [] } = bagOf(options);
1025
- return {
1026
- path: "packages/db/src/schema/geonosis-probe-table.ts",
1027
- source: `import { pgTable, text } from 'drizzle-orm/pg-core'
1028
-
1029
- export const geonosisProbe = pgTable('${perKindTables[0] ?? ""}', { id: text('id') })
1030
- `
1031
- };
1032
- }
1033
- };
1034
- var documents_share_one_table_default = documentsShareOneTable;
1035
-
1036
877
  // src/rules/lib/objects.ts
1037
878
  var propertiesOf = (node) => {
1038
879
  const object = node;
@@ -1084,7 +925,7 @@ var stepNameOf = (node, config) => {
1084
925
  };
1085
926
 
1086
927
  // src/rules/durable-body-reads-through-steps.ts
1087
- var RULE3 = "durable-body-reads-through-steps";
928
+ var RULE2 = "durable-body-reads-through-steps";
1088
929
  var ACCEPTS2 = "{ ports: string[] (required), durableKey: string, durableValue: boolean | string, workflowFactories: string[], stepFactories: string[], stepProxies: string[] }";
1089
930
  var DEFAULT_WORKFLOW_FACTORIES = ["createWorkflow"];
1090
931
  var DEFAULT_DURABLE_KEY = "execution";
@@ -1116,13 +957,13 @@ var insideAStep = (node, factories) => {
1116
957
  var durableBodyReadsThroughSteps = {
1117
958
  create(context) {
1118
959
  const bag = context.options?.[0] ?? {};
1119
- const ports = new Set(requireOption(RULE3, bag.ports, "ports", ACCEPTS2));
960
+ const ports = new Set(requireOption(RULE2, bag.ports, "ports", ACCEPTS2));
1120
961
  const proxies = new Set(bag.stepProxies ?? []);
1121
- const steps = stepFactoriesOf(RULE3, bag.stepFactories);
962
+ const steps = stepFactoriesOf(RULE2, bag.stepFactories);
1122
963
  const workflows = new Set(
1123
- requireOption(RULE3, bag.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES, "workflowFactories")
964
+ requireOption(RULE2, bag.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES, "workflowFactories")
1124
965
  );
1125
- const durableKey = requireOption(RULE3, bag.durableKey ?? DEFAULT_DURABLE_KEY, "durableKey");
966
+ const durableKey = requireOption(RULE2, bag.durableKey ?? DEFAULT_DURABLE_KEY, "durableKey");
1126
967
  const durableValue = bag.durableValue ?? DEFAULT_DURABLE_VALUE;
1127
968
  return {
1128
969
  CallExpression(node) {
@@ -1272,7 +1113,7 @@ file and the export together \u2014 half the pair still fires.`,
1272
1113
  var effect_hook_naming_default = effectHookNaming;
1273
1114
 
1274
1115
  // src/rules/emit-declares-attempts.ts
1275
- var RULE4 = "emit-declares-attempts";
1116
+ var RULE3 = "emit-declares-attempts";
1276
1117
  var DEFAULT_EMITTERS = ["emitEventStep"];
1277
1118
  var OPTIONS = "options";
1278
1119
  var ATTEMPTS = "attempts";
@@ -1287,7 +1128,7 @@ var valueOf2 = (properties, name) => properties.find((one) => one.key?.type ===
1287
1128
  var emitDeclaresAttempts = {
1288
1129
  create(context) {
1289
1130
  const emitters = new Set(
1290
- requireOption(RULE4, context.options?.[0]?.emitters ?? DEFAULT_EMITTERS, "emitters")
1131
+ requireOption(RULE3, context.options?.[0]?.emitters ?? DEFAULT_EMITTERS, "emitters")
1291
1132
  );
1292
1133
  const budgetHelpers = new Set(context.options?.[0]?.budgetHelpers ?? []);
1293
1134
  return {
@@ -1957,21 +1798,21 @@ var writesWith = (prefixes) => (node) => {
1957
1798
  var writeNameOf = (node) => propertyNameOf(node.callee ?? {}) ?? "a service write";
1958
1799
 
1959
1800
  // src/rules/mutating-step-declares-retry.ts
1960
- var RULE5 = "mutating-step-declares-retry";
1801
+ var RULE4 = "mutating-step-declares-retry";
1961
1802
  var DEFAULT_RETRY_KEY = "maxRetries";
1962
1803
  var DEFAULT_RETRY_LIMIT_KEY = "limit";
1963
1804
  var NO_RETRY2 = 0;
1964
1805
  var mutatingStepDeclaresRetry = {
1965
1806
  create(context) {
1966
- const factories = stepFactoriesOf(RULE5, context.options?.[0]?.stepFactories);
1967
- const isWrite = writesWith(mutationPrefixesOf(RULE5, context.options?.[0]?.mutationPrefixes));
1807
+ const factories = stepFactoriesOf(RULE4, context.options?.[0]?.stepFactories);
1808
+ const isWrite = writesWith(mutationPrefixesOf(RULE4, context.options?.[0]?.mutationPrefixes));
1968
1809
  const retryKey = requireOption(
1969
- RULE5,
1810
+ RULE4,
1970
1811
  context.options?.[0]?.retryKey ?? DEFAULT_RETRY_KEY,
1971
1812
  "retryKey"
1972
1813
  );
1973
1814
  const retryLimitKey = requireOption(
1974
- RULE5,
1815
+ RULE4,
1975
1816
  context.options?.[0]?.retryLimitKey ?? DEFAULT_RETRY_LIMIT_KEY,
1976
1817
  "retryLimitKey"
1977
1818
  );
@@ -2048,13 +1889,13 @@ the repo wraps \`createStep\`, and \`mutationPrefixes\` if its writes are named
2048
1889
  var mutating_step_declares_retry_default = mutatingStepDeclaresRetry;
2049
1890
 
2050
1891
  // src/rules/named-when-block.ts
2051
- var RULE6 = "named-when-block";
1892
+ var RULE5 = "named-when-block";
2052
1893
  var DEFAULT_BLOCKS = ["when"];
2053
1894
  var NAMED = 3;
2054
1895
  var namedWhenBlock = {
2055
1896
  create(context) {
2056
1897
  const blocks = new Set(
2057
- requireOption(RULE6, context.options?.[0]?.blocks ?? DEFAULT_BLOCKS, "blocks")
1898
+ requireOption(RULE5, context.options?.[0]?.blocks ?? DEFAULT_BLOCKS, "blocks")
2058
1899
  );
2059
1900
  return {
2060
1901
  CallExpression(node) {
@@ -2272,16 +2113,16 @@ var next_route_segment_is_thin_delegate_default = nextRouteSegmentIsThinDelegate
2272
2113
  // src/rules/no-brand-names.ts
2273
2114
  import * as fs3 from "fs";
2274
2115
  var EXEMPT_PATH = /\/examples\//;
2275
- var escapeForRegex2 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
2116
+ var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
2276
2117
  var noBrandNames = {
2277
2118
  create(context) {
2278
2119
  const { allowedSubstrings = [], brands = [] } = context.options?.[0] ?? {};
2279
2120
  requireOption("no-brand-names", brands, "brands");
2280
2121
  const filename = normalise(context.filename);
2281
2122
  if (EXEMPT_PATH.test(filename)) return {};
2282
- const brandPattern = new RegExp(`\\b(?:${brands.map(escapeForRegex2).join("|")})\\b`, "i");
2123
+ const brandPattern = new RegExp(`\\b(?:${brands.map(escapeForRegex).join("|")})\\b`, "i");
2283
2124
  const allowedPatterns = allowedSubstrings.map(
2284
- (allowed) => new RegExp(escapeForRegex2(allowed), "gi")
2125
+ (allowed) => new RegExp(escapeForRegex(allowed), "gi")
2285
2126
  );
2286
2127
  let programNode = null;
2287
2128
  return {
@@ -2956,20 +2797,20 @@ A component returns UI; a hook returns state.`,
2956
2797
  var no_hook_in_component_disguise_default = noHookInComponentDisguise;
2957
2798
 
2958
2799
  // src/rules/no-index-count-as-exact.ts
2959
- var RULE7 = "no-index-count-as-exact";
2800
+ var RULE6 = "no-index-count-as-exact";
2960
2801
  var DEFAULT_ESTIMATE_FIELDS = ["estimate_count", "count_estimate"];
2961
2802
  var DEFAULT_LABEL_KEY = "count_is_estimate";
2962
2803
  var noIndexCountAsExact = {
2963
2804
  create(context) {
2964
2805
  const fields = new Set(
2965
2806
  requireOption(
2966
- RULE7,
2807
+ RULE6,
2967
2808
  context.options?.[0]?.estimateFields ?? DEFAULT_ESTIMATE_FIELDS,
2968
2809
  "estimateFields"
2969
2810
  )
2970
2811
  );
2971
2812
  const label = requireOption(
2972
- RULE7,
2813
+ RULE6,
2973
2814
  context.options?.[0]?.labelKey ?? DEFAULT_LABEL_KEY,
2974
2815
  "labelKey"
2975
2816
  );
@@ -3225,20 +3066,20 @@ logic in \`lib/\`.`,
3225
3066
  var no_invalid_feature_folders_default = noInvalidFeatureFolders;
3226
3067
 
3227
3068
  // src/rules/no-loader-side-effect-in-index-file.ts
3228
- var RULE8 = "no-loader-side-effect-in-index-file";
3069
+ var RULE7 = "no-loader-side-effect-in-index-file";
3229
3070
  var DEFAULT_DIRECTORIES = ["(?:^|/)subscribers/", "(?:^|/)jobs/"];
3230
3071
  var DEFAULT_RESOURCE_FACTORIES = ["createWorkflow", "createStep", "createHook"];
3231
3072
  var INDEX = /(?:^|\/)index\.[cm]?[jt]sx?$/;
3232
3073
  var noLoaderSideEffectInIndexFile = {
3233
3074
  create(context) {
3234
3075
  const directories = requireOption(
3235
- RULE8,
3076
+ RULE7,
3236
3077
  context.options?.[0]?.directories ?? DEFAULT_DIRECTORIES,
3237
3078
  "directories"
3238
3079
  ).map((source) => new RegExp(source));
3239
3080
  const factories = new Set(
3240
3081
  requireOption(
3241
- RULE8,
3082
+ RULE7,
3242
3083
  context.options?.[0]?.resourceFactories ?? DEFAULT_RESOURCE_FACTORIES,
3243
3084
  "resourceFactories"
3244
3085
  )
@@ -3401,7 +3242,7 @@ the same component. Thin preset wrappers over one imported component do not coun
3401
3242
  var no_logic_in_component_files_default = noLogicInComponentFiles;
3402
3243
 
3403
3244
  // src/rules/no-mock-db-in-integration.ts
3404
- var RULE9 = "no-mock-db-in-integration";
3245
+ var RULE8 = "no-mock-db-in-integration";
3405
3246
  var BUN_MOCK = "mock.module";
3406
3247
  var MOCK_CALLS = /* @__PURE__ */ new Set(["jest.mock", BUN_MOCK, "vi.mock"]);
3407
3248
  var calleeOf = (node) => {
@@ -3419,8 +3260,8 @@ var inDirectory = (filename, directory) => {
3419
3260
  var noMockDbInIntegration = {
3420
3261
  create(context) {
3421
3262
  const options = context.options?.[0] ?? {};
3422
- const dirs = requireOption(RULE9, options.integrationDirs, "integrationDirs");
3423
- const modules = requireOption(RULE9, options.dbModules, "dbModules");
3263
+ const dirs = requireOption(RULE8, options.integrationDirs, "integrationDirs");
3264
+ const modules = requireOption(RULE8, options.dbModules, "dbModules");
3424
3265
  const mockNames = new Set(options.mockNames ?? []);
3425
3266
  const filename = normalise(context.filename);
3426
3267
  const spans = /* @__PURE__ */ new Set();
@@ -3521,14 +3362,14 @@ below the seam, and a caller that names it has crossed it.`,
3521
3362
  var no_orm_outside_db_default = noOrmOutsideDb;
3522
3363
 
3523
3364
  // src/rules/no-raw-html-atoms.ts
3524
- var RULE10 = "no-raw-html-atoms";
3365
+ var RULE9 = "no-raw-html-atoms";
3525
3366
  var WHERE_RAW_HTML_IS_THE_POINT = "atoms";
3526
3367
  var noRawHtmlAtoms = {
3527
3368
  create(context) {
3528
- const restrictions = requireOption(RULE10, context.options[0], "element\u2192atom map");
3369
+ const restrictions = requireOption(RULE9, context.options[0], "element\u2192atom map");
3529
3370
  const scope = context.options[1]?.scope;
3530
3371
  const filename = context.filename;
3531
- const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(RULE10, scope).some((tier) => inTier(filename, tier));
3372
+ const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(RULE9, scope).some((tier) => inTier(filename, tier));
3532
3373
  if (!inScope) return {};
3533
3374
  const elementMap = /* @__PURE__ */ new Map();
3534
3375
  for (const r of restrictions) {
@@ -4178,7 +4019,7 @@ var no_type_definitions_in_components_default = noTypeDefinitionsInComponents;
4178
4019
 
4179
4020
  // src/rules/no-unregistered-plugin-dir.ts
4180
4021
  import { readFileSync as readFileSync4 } from "fs";
4181
- var RULE11 = "no-unregistered-plugin-dir";
4022
+ var RULE10 = "no-unregistered-plugin-dir";
4182
4023
  var ACCEPTS3 = "{ roots: (string | { path: string, files: boolean })[], registry: string | string[], manifests: string[] }";
4183
4024
  var trimSlashes = (path2) => path2.replace(/^\/+/, "").replace(/\/+$/, "");
4184
4025
  var withoutComments = (source) => source.replaceAll(/\/\*[\s\S]*?\*\//g, " ").replaceAll(/\/\/[^\n]*/g, " ");
@@ -4206,12 +4047,12 @@ var oneFile = (segments) => {
4206
4047
  var noUnregisteredPluginDir = {
4207
4048
  create(context) {
4208
4049
  const options = context.options?.[0] ?? {};
4209
- const roots = requireOption(RULE11, options.roots, "roots", ACCEPTS3).map(
4050
+ const roots = requireOption(RULE10, options.roots, "roots", ACCEPTS3).map(
4210
4051
  (one) => typeof one === "string" ? { files: false, path: trimSlashes(one) } : { files: one.files === true, path: trimSlashes(one.path) }
4211
4052
  );
4212
- const declared = requireOption(RULE11, options.registry, "registry", ACCEPTS3);
4053
+ const declared = requireOption(RULE10, options.registry, "registry", ACCEPTS3);
4213
4054
  const registries = (typeof declared === "string" ? [declared] : declared).map(trimSlashes);
4214
- const manifests = new Set(requireOption(RULE11, options.manifests, "manifests", ACCEPTS3));
4055
+ const manifests = new Set(requireOption(RULE10, options.manifests, "manifests", ACCEPTS3));
4215
4056
  const filename = normalise(context.filename);
4216
4057
  return {
4217
4058
  Program(node) {
@@ -4488,16 +4329,16 @@ source. The CELL is the only level where several sources meet; it derives and pa
4488
4329
  var organelle_single_source_default = organelleSingleSource;
4489
4330
 
4490
4331
  // src/rules/plugin-route-namespaced.ts
4491
- var RULE12 = "plugin-route-namespaced";
4332
+ var RULE11 = "plugin-route-namespaced";
4492
4333
  var DEFAULT_SCOPES = ["store", "admin"];
4493
4334
  var ROUTE_FILE = /(?:^|\/)api\/(.+\/)?route\.[cm]?[jt]sx?$/;
4494
4335
  var NAMESPACED_FROM = 2;
4495
4336
  var readPackages = (configured) => {
4496
- const rules2 = requireOption(RULE12, configured, "packages");
4337
+ const rules2 = requireOption(RULE11, configured, "packages");
4497
4338
  for (const rule of rules2) {
4498
4339
  if (typeof rule.under !== "string" || rule.under === "") {
4499
4340
  throw new Error(
4500
- `biological-architecture/${RULE12}: every \`packages\` entry needs \`under\` \u2014 the directory whose children are packages. Without it the rule cannot tell which files belong to a plugin.`
4341
+ `biological-architecture/${RULE11}: every \`packages\` entry needs \`under\` \u2014 the directory whose children are packages. Without it the rule cannot tell which files belong to a plugin.`
4501
4342
  );
4502
4343
  }
4503
4344
  }
@@ -4524,7 +4365,7 @@ var pluginRouteNamespaced = {
4524
4365
  create(context) {
4525
4366
  const rules2 = readPackages(context.options?.[0]?.packages);
4526
4367
  const scopes = new Set(
4527
- requireOption(RULE12, context.options?.[0]?.scopes ?? DEFAULT_SCOPES, "scopes")
4368
+ requireOption(RULE11, context.options?.[0]?.scopes ?? DEFAULT_SCOPES, "scopes")
4528
4369
  );
4529
4370
  const path2 = normalise(context.filename);
4530
4371
  const route = routePathOf(path2);
@@ -4710,8 +4551,55 @@ decides what an event MEANS and throws when it cannot.`,
4710
4551
  };
4711
4552
  var queue_loop_is_the_library_default = queueLoopIsTheLibrary;
4712
4553
 
4554
+ // src/rules/retired.ts
4555
+ var SINCE = "2.7.0";
4556
+ var REASON = "it enforced one consumer's document kinds, which is vocabulary the kit does not hold";
4557
+ var announced = /* @__PURE__ */ new Set();
4558
+ var retired = (name, schema) => ({
4559
+ create: () => {
4560
+ if (!announced.has(name)) {
4561
+ announced.add(name);
4562
+ process.stderr.write(
4563
+ `biological-architecture: rule "${name}" is retired since ${SINCE} \u2014 ${REASON}. It reports nothing and goes at the next major. Remove it from your config.
4564
+ `
4565
+ );
4566
+ }
4567
+ return {};
4568
+ },
4569
+ fixShape: `\`${name}\` is retired since ${SINCE} and reports nothing. Remove it from the config.`,
4570
+ meta: {
4571
+ docs: {
4572
+ description: `Retired since ${SINCE}: ${REASON}. Reports nothing; goes at the next major.`
4573
+ },
4574
+ messages: {},
4575
+ retired: { reason: REASON, since: SINCE },
4576
+ schema,
4577
+ type: "problem"
4578
+ }
4579
+ });
4580
+ var RETIRED_RULES = {
4581
+ "document-sagas-are-generic": retired("document-sagas-are-generic", [
4582
+ {
4583
+ additionalProperties: false,
4584
+ properties: {
4585
+ factories: { items: { type: "string" }, type: "array" },
4586
+ perKindPrefixes: { items: { type: "string" }, type: "array" },
4587
+ within: { type: "string" }
4588
+ },
4589
+ type: "object"
4590
+ }
4591
+ ]),
4592
+ "documents-share-one-table": retired("documents-share-one-table", [
4593
+ {
4594
+ additionalProperties: false,
4595
+ properties: { perKindTables: { items: { type: "string" }, type: "array" } },
4596
+ type: "object"
4597
+ }
4598
+ ])
4599
+ };
4600
+
4713
4601
  // src/rules/route-no-inline-mutation.ts
4714
- var RULE13 = "route-no-inline-mutation";
4602
+ var RULE12 = "route-no-inline-mutation";
4715
4603
  var DEFAULT_PATHS = [String.raw`(?:^|/)api/.*/route\.[cm]?[jt]sx?$`];
4716
4604
  var DEFAULT_INLINE_MUTATION_OK = "arch:inline-mutation-ok";
4717
4605
  var commentsOf2 = (sourceCode) => sourceCode?.getAllComments?.() ?? sourceCode?.ast?.comments ?? [];
@@ -4755,15 +4643,15 @@ var annotated = (found, comments, marker) => {
4755
4643
  };
4756
4644
  var routeNoInlineMutation = {
4757
4645
  create(context) {
4758
- const paths = requireOption(RULE13, context.options?.[0]?.paths ?? DEFAULT_PATHS, "paths").map(
4646
+ const paths = requireOption(RULE12, context.options?.[0]?.paths ?? DEFAULT_PATHS, "paths").map(
4759
4647
  (source) => new RegExp(source)
4760
4648
  );
4761
4649
  const marker = requireOption(
4762
- RULE13,
4650
+ RULE12,
4763
4651
  context.options?.[0]?.inlineMutationOk ?? DEFAULT_INLINE_MUTATION_OK,
4764
4652
  "inlineMutationOk"
4765
4653
  );
4766
- const isWrite = writesWith(mutationPrefixesOf(RULE13, context.options?.[0]?.mutationPrefixes));
4654
+ const isWrite = writesWith(mutationPrefixesOf(RULE12, context.options?.[0]?.mutationPrefixes));
4767
4655
  const filename = normalise(context.filename);
4768
4656
  if (!paths.some((pattern) => pattern.test(filename))) return {};
4769
4657
  return {
@@ -4833,7 +4721,7 @@ var route_no_inline_mutation_default = routeNoInlineMutation;
4833
4721
 
4834
4722
  // src/rules/router-schema-parity.ts
4835
4723
  import { existsSync as existsSync2 } from "fs";
4836
- var RULE14 = "router-schema-parity";
4724
+ var RULE13 = "router-schema-parity";
4837
4725
  var EXTENSIONS2 = [".ts", ".tsx"];
4838
4726
  var trimSlashes2 = (path2) => path2.replace(/^\/+/, "").replace(/\/+$/, "");
4839
4727
  var baseOf = (file) => file.replace(/\.[cm]?[jt]sx?$/, "");
@@ -4851,8 +4739,8 @@ var directlyUnder = (filename, directory) => {
4851
4739
  var routerSchemaParity = {
4852
4740
  create(context) {
4853
4741
  const options = context.options?.[0] ?? {};
4854
- const routers = trimSlashes2(requireOption(RULE14, options.routers, "routers"));
4855
- const schemas = trimSlashes2(requireOption(RULE14, options.schemas, "schemas"));
4742
+ const routers = trimSlashes2(requireOption(RULE13, options.routers, "routers"));
4743
+ const schemas = trimSlashes2(requireOption(RULE13, options.schemas, "schemas"));
4856
4744
  const suffix = options.suffix ?? "";
4857
4745
  const ignored = new Set(options.ignore ?? []);
4858
4746
  const mounts = options.mounts ?? {};
@@ -4924,7 +4812,7 @@ repo-root-relative, because in one consumer they live in different packages.`,
4924
4812
  var router_schema_parity_default = routerSchemaParity;
4925
4813
 
4926
4814
  // src/rules/service-name-not-reserved.ts
4927
- var RULE15 = "service-name-not-reserved";
4815
+ var RULE14 = "service-name-not-reserved";
4928
4816
  var DEFAULT_MODULE_FACTORIES = ["Module"];
4929
4817
  var MODULES_2_19 = [
4930
4818
  "analytics",
@@ -4964,13 +4852,13 @@ var serviceNameNotReserved = {
4964
4852
  create(context) {
4965
4853
  const factories = new Set(
4966
4854
  requireOption(
4967
- RULE15,
4855
+ RULE14,
4968
4856
  context.options?.[0]?.moduleFactories ?? DEFAULT_MODULE_FACTORIES,
4969
4857
  "moduleFactories"
4970
4858
  )
4971
4859
  );
4972
4860
  const reserved = new Set(
4973
- requireOption(RULE15, context.options?.[0]?.reserved ?? MODULES_2_19, "reserved")
4861
+ requireOption(RULE14, context.options?.[0]?.reserved ?? MODULES_2_19, "reserved")
4974
4862
  );
4975
4863
  const constants = /* @__PURE__ */ new Map();
4976
4864
  const pending = [];
@@ -5281,12 +5169,12 @@ can be misspelled and a second default nobody agreed to.`,
5281
5169
  var ssot_no_process_env_default = ssotNoProcessEnv;
5282
5170
 
5283
5171
  // src/rules/step-declares-compensation-or-none.ts
5284
- var RULE16 = "step-declares-compensation-or-none";
5172
+ var RULE15 = "step-declares-compensation-or-none";
5285
5173
  var NO_COMPENSATION = "noCompensation";
5286
5174
  var WITH_COMPENSATION = 3;
5287
5175
  var stepDeclaresCompensationOrNone = {
5288
5176
  create(context) {
5289
- const factories = stepFactoriesOf(RULE16, context.options?.[0]?.stepFactories);
5177
+ const factories = stepFactoriesOf(RULE15, context.options?.[0]?.stepFactories);
5290
5178
  return {
5291
5179
  CallExpression(node) {
5292
5180
  if (!isStepCall(node, factories)) return;
@@ -5330,7 +5218,7 @@ variable \u2014 passes, since the flag may be in the base. Configure \`stepFacto
5330
5218
  var step_declares_compensation_or_none_default = stepDeclaresCompensationOrNone;
5331
5219
 
5332
5220
  // src/rules/step-opens-its-own-cell.ts
5333
- var RULE17 = "step-opens-its-own-cell";
5221
+ var RULE16 = "step-opens-its-own-cell";
5334
5222
  var STEP = "step";
5335
5223
  var ACCEPTS4 = "{ within: string (required), hosts: string[], scopeKey: string, engine: string, wrapper: string }";
5336
5224
  var DEFAULT_HOSTS = ["createRuntime", "entrypointFor"];
@@ -5365,9 +5253,9 @@ var declaresScope = (call, scopeKey) => {
5365
5253
  var stepOpensItsOwnCell = {
5366
5254
  create(context) {
5367
5255
  const bag = context.options?.[0] ?? {};
5368
- const within = requireOption(RULE17, bag.within, "within", ACCEPTS4);
5369
- const hosts = new Set(requireOption(RULE17, bag.hosts ?? DEFAULT_HOSTS, "hosts", ACCEPTS4));
5370
- const scopeKey = requireOption(RULE17, bag.scopeKey ?? DEFAULT_SCOPE_KEY, "scopeKey", ACCEPTS4);
5256
+ const within = requireOption(RULE16, bag.within, "within", ACCEPTS4);
5257
+ const hosts = new Set(requireOption(RULE16, bag.hosts ?? DEFAULT_HOSTS, "hosts", ACCEPTS4));
5258
+ const scopeKey = requireOption(RULE16, bag.scopeKey ?? DEFAULT_SCOPE_KEY, "scopeKey", ACCEPTS4);
5371
5259
  const { engine, wrapper } = bag;
5372
5260
  const filename = normalise(context.filename);
5373
5261
  if (!new RegExp(within).test(filename)) return {};
@@ -5470,7 +5358,7 @@ without \`within\` the rule refuses the run.`,
5470
5358
  const path2 = pathMatching(within, "geonosis-probe-engine-step.ts");
5471
5359
  if (wrapper !== void 0 && new RegExp(wrapper).test(path2)) {
5472
5360
  throw new Error(
5473
- `biological-architecture/${RULE17}: the probe path "${path2}" is also the \`wrapper\`, which is the one file allowed to reach \`${engine}\` \u2014 no single file inside \`within\` can both land where the rule looks and be somewhere it speaks.`
5361
+ `biological-architecture/${RULE16}: the probe path "${path2}" is also the \`wrapper\`, which is the one file allowed to reach \`${engine}\` \u2014 no single file inside \`within\` can both land where the rule looks and be somewhere it speaks.`
5474
5362
  );
5475
5363
  }
5476
5364
  return {
@@ -5675,10 +5563,10 @@ data, it belongs on the admin surface.`,
5675
5563
  var store_route_scopes_tenant_data_default = storeRouteScopesTenantData;
5676
5564
 
5677
5565
  // src/rules/subpath-over-barrel.ts
5678
- var RULE18 = "subpath-over-barrel";
5566
+ var RULE17 = "subpath-over-barrel";
5679
5567
  var ACCEPTS5 = "{ packages: { name: string, subpaths: { [subpath: string]: string[] } }[] (required) }";
5680
5568
  var homesOf = (declared) => {
5681
- const subpaths = requireOption(RULE18, Object.keys(declared.subpaths ?? {}), "subpaths", ACCEPTS5);
5569
+ const subpaths = requireOption(RULE17, Object.keys(declared.subpaths ?? {}), "subpaths", ACCEPTS5);
5682
5570
  return new Map(
5683
5571
  subpaths.flatMap(
5684
5572
  (subpath) => (declared.subpaths?.[subpath] ?? []).map((name) => [name, subpath])
@@ -5701,9 +5589,9 @@ var importedNamesOf = (node) => {
5701
5589
  };
5702
5590
  var subpathOverBarrel = {
5703
5591
  create(context) {
5704
- const declared = requireOption(RULE18, context.options?.[0]?.packages, "packages", ACCEPTS5);
5592
+ const declared = requireOption(RULE17, context.options?.[0]?.packages, "packages", ACCEPTS5);
5705
5593
  const homes = new Map(
5706
- declared.map((one) => [requireOption(RULE18, one.name, "name", ACCEPTS5), homesOf(one)])
5594
+ declared.map((one) => [requireOption(RULE17, one.name, "name", ACCEPTS5), homesOf(one)])
5707
5595
  );
5708
5596
  return {
5709
5597
  ImportDeclaration(node) {
@@ -5773,7 +5661,7 @@ is a type-only import, which erases before any of that happens. Declare the map
5773
5661
  const [exported] = first?.subpaths?.[subpath ?? ""] ?? [];
5774
5662
  if (exported === void 0) {
5775
5663
  throw new Error(
5776
- `biological-architecture/${RULE18}: the configured packages declare no subpath with a name in it, so there is nothing a probe could import from the root.`
5664
+ `biological-architecture/${RULE17}: the configured packages declare no subpath with a name in it, so there is nothing a probe could import from the root.`
5777
5665
  );
5778
5666
  }
5779
5667
  return {
@@ -5788,7 +5676,7 @@ export const geonosisProbe = ${exported}
5788
5676
  var subpath_over_barrel_default = subpathOverBarrel;
5789
5677
 
5790
5678
  // src/rules/subscriber-declares-id.ts
5791
- var RULE19 = "subscriber-declares-id";
5679
+ var RULE18 = "subscriber-declares-id";
5792
5680
  var DEFAULT_PATHS2 = ["(?:^|/)subscribers/"];
5793
5681
  var CONFIG = "config";
5794
5682
  var CONTEXT = "context";
@@ -5802,7 +5690,7 @@ var declaredName2 = (node) => {
5802
5690
  };
5803
5691
  var subscriberDeclaresId = {
5804
5692
  create(context) {
5805
- const paths = requireOption(RULE19, context.options?.[0]?.paths ?? DEFAULT_PATHS2, "paths").map(
5693
+ const paths = requireOption(RULE18, context.options?.[0]?.paths ?? DEFAULT_PATHS2, "paths").map(
5806
5694
  (source) => new RegExp(source)
5807
5695
  );
5808
5696
  const filename = normalise(context.filename);
@@ -6065,7 +5953,7 @@ are tests is \`testGlobs\` and which calls poll is \`pollers\`, both defaulted.`
6065
5953
  var test_no_clock_polling_for_promise_flips_default = testNoClockPollingForPromiseFlips;
6066
5954
 
6067
5955
  // src/rules/time-through-the-door.ts
6068
- var RULE20 = "time-through-the-door";
5956
+ var RULE19 = "time-through-the-door";
6069
5957
  var ACCEPTS6 = "{ door: string (a regex over the filename), exempt: string[] (regex sources, default the test paths: __tests__/, .test., .spec.), libraries: string[] (default ['dayjs']) }";
6070
5958
  var DEFAULT_LIBRARIES = ["dayjs"];
6071
5959
  var DEFAULT_EXEMPT = ["__tests__/", String.raw`\.test\.`, String.raw`\.spec\.`];
@@ -6093,11 +5981,11 @@ var memberName = (callee) => ({
6093
5981
  var readable2 = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
6094
5982
  var timeThroughTheDoor = {
6095
5983
  create(context) {
6096
- const door = requireOption(RULE20, context.options?.[0]?.door, "door", ACCEPTS6);
5984
+ const door = requireOption(RULE19, context.options?.[0]?.door, "door", ACCEPTS6);
6097
5985
  const exempt = context.options?.[0]?.exempt ?? DEFAULT_EXEMPT;
6098
5986
  const exemptModules = context.options?.[0]?.exemptModules ?? [];
6099
5987
  const libraries = requireOption(
6100
- RULE20,
5988
+ RULE19,
6101
5989
  context.options?.[0]?.libraries ?? DEFAULT_LIBRARIES,
6102
5990
  "libraries",
6103
5991
  ACCEPTS6
@@ -6201,7 +6089,7 @@ it internally cannot be tested at a chosen instant.`,
6201
6089
  );
6202
6090
  if (outside === void 0) {
6203
6091
  throw new Error(
6204
- `biological-architecture/${RULE20}: every path a probe could be written at is inside the \`door\` (${door}) or one of its \`exempt\` patterns, so there is nowhere left for this rule to speak.`
6092
+ `biological-architecture/${RULE19}: every path a probe could be written at is inside the \`door\` (${door}) or one of its \`exempt\` patterns, so there is nowhere left for this rule to speak.`
6205
6093
  );
6206
6094
  }
6207
6095
  return {
@@ -6588,13 +6476,13 @@ var tissue_no_stores_default = tissueNoStores;
6588
6476
  // src/rules/todo-names-its-plan.ts
6589
6477
  var ID_DEFAULT = String.raw`\d{3,}`;
6590
6478
  var commentsOf3 = (sourceCode) => sourceCode?.getAllComments?.() ?? sourceCode?.ast?.comments ?? [];
6591
- var escapeForRegex3 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
6479
+ var escapeForRegex2 = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
6592
6480
  var todoNamesItsPlan = {
6593
6481
  create(context) {
6594
6482
  const { id = ID_DEFAULT, markers = [] } = context.options?.[0] ?? {};
6595
6483
  requireOption("todo-names-its-plan", markers, "markers", "{ markers: string[], id?: string }");
6596
6484
  const marker = new RegExp(
6597
- `\\b(${markers.map(escapeForRegex3).join("|")})\\b(?:\\(([^)]*)\\))?`,
6485
+ `\\b(${markers.map(escapeForRegex2).join("|")})\\b(?:\\(([^)]*)\\))?`,
6598
6486
  "g"
6599
6487
  );
6600
6488
  const names2 = new RegExp(`^${id}$`);
@@ -7011,7 +6899,7 @@ var isNamedOnly = (value) => /^[a-z]+$/i.test(value.trim());
7011
6899
  var tokens_only_colors_default = tokensOnlyColors;
7012
6900
 
7013
6901
  // src/rules/unique-query-step-name.ts
7014
- var RULE21 = "unique-query-step-name";
6902
+ var RULE20 = "unique-query-step-name";
7015
6903
  var ACCEPTS7 = "{ queryStepCalls: string[] (required), workflowFactories: string[] }";
7016
6904
  var DEFAULT_WORKFLOW_FACTORIES2 = ["createWorkflow"];
7017
6905
  var CONFIG2 = "config";
@@ -7055,13 +6943,13 @@ var uniqueQueryStepName = {
7055
6943
  create(context) {
7056
6944
  const factories = new Set(
7057
6945
  requireOption(
7058
- RULE21,
6946
+ RULE20,
7059
6947
  context.options?.[0]?.workflowFactories ?? DEFAULT_WORKFLOW_FACTORIES2,
7060
6948
  "workflowFactories"
7061
6949
  )
7062
6950
  );
7063
6951
  const calls = new Set(
7064
- requireOption(RULE21, context.options?.[0]?.queryStepCalls, "queryStepCalls", ACCEPTS7)
6952
+ requireOption(RULE20, context.options?.[0]?.queryStepCalls, "queryStepCalls", ACCEPTS7)
7065
6953
  );
7066
6954
  return {
7067
6955
  CallExpression(node) {
@@ -7142,7 +7030,7 @@ because the collision it reports is one framework's step-handler map and not eve
7142
7030
  var unique_query_step_name_default = uniqueQueryStepName;
7143
7031
 
7144
7032
  // src/rules/worker-handles-are-scoped.ts
7145
- var RULE22 = "worker-handles-are-scoped";
7033
+ var RULE21 = "worker-handles-are-scoped";
7146
7034
  var ACCEPTS8 = "{ within: string (a regex over the filename), wrapper: string[] }";
7147
7035
  var handlerKeys = /* @__PURE__ */ new Set(["fetch", "queue", "scheduled", "email"]);
7148
7036
  var entrypointMakers = /* @__PURE__ */ new Set([
@@ -7152,8 +7040,8 @@ var entrypointMakers = /* @__PURE__ */ new Set([
7152
7040
  ]);
7153
7041
  var cellHandlesAreScoped = {
7154
7042
  create(context) {
7155
- const within = requireOption(RULE22, context.options?.[0]?.within, "within", ACCEPTS8);
7156
- const wrappers = requireOption(RULE22, context.options?.[0]?.wrapper, "wrapper", ACCEPTS8);
7043
+ const within = requireOption(RULE21, context.options?.[0]?.within, "within", ACCEPTS8);
7044
+ const wrappers = requireOption(RULE21, context.options?.[0]?.wrapper, "wrapper", ACCEPTS8);
7157
7045
  if (!new RegExp(within).test(normalise(context.filename))) return {};
7158
7046
  const scopedBy = new Set(wrappers);
7159
7047
  let handler = null;
@@ -7356,8 +7244,6 @@ var rules = refusingOnce(
7356
7244
  "compound-no-stores": compound_no_stores_default,
7357
7245
  "constants-in-constants-file": constants_in_constants_file_default,
7358
7246
  "dialect-through-the-seam": dialect_through_the_seam_default,
7359
- "document-sagas-are-generic": document_sagas_are_generic_default,
7360
- "documents-share-one-table": documents_share_one_table_default,
7361
7247
  "durable-body-reads-through-steps": durable_body_reads_through_steps_default,
7362
7248
  "effect-hook-naming": effect_hook_naming_default,
7363
7249
  "emit-declares-attempts": emit_declares_attempts_default,
@@ -7430,18 +7316,22 @@ var rules = refusingOnce(
7430
7316
  })
7431
7317
  );
7432
7318
  var { version } = createRequire(import.meta.url)("../package.json");
7319
+ var loaded = { ...rules, ...RETIRED_RULES };
7433
7320
  var plugin = {
7434
7321
  configs: PRESETS,
7435
7322
  meta: {
7436
7323
  name: "biological-architecture",
7437
7324
  version
7438
7325
  },
7439
- rules
7326
+ // A retired id resolves here and nowhere else: a config that still names one parses, and the id
7327
+ // reports nothing. `rules` — what the kit's own tooling reads as its rule set — does not carry it.
7328
+ rules: loaded
7440
7329
  };
7441
7330
  var index_default = plugin;
7442
7331
  export {
7443
7332
  PRESETS,
7444
7333
  RENAMED_TO,
7334
+ RETIRED_RULES,
7445
7335
  index_default as default,
7446
7336
  rules,
7447
7337
  rulesOfPreset
package/dist/presets.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  PRESUMED_ENGINES,
4
4
  RENAMED_TO,
5
5
  rulesOfPreset
6
- } from "./chunk-KV7JRNAP.js";
6
+ } from "./chunk-24K5232K.js";
7
7
  export {
8
8
  PRESETS,
9
9
  PRESUMED_ENGINES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geonosis/oxlint-plugin-biological-architecture",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "types": "./dist/index.d.ts",
5
5
  "description": "Biological tier architecture as lint — the union of the dielime and during.day rule sets, shipped as presets.",
6
6
  "keywords": [
@@ -45,7 +45,7 @@
45
45
  "oxlint": ">=1.77"
46
46
  },
47
47
  "devDependencies": {
48
- "@geonosis/lint-parity": "2.6.0"
48
+ "@geonosis/lint-parity": "2.7.0"
49
49
  },
50
50
  "engines": {
51
51
  "node": ">=22"
@@ -1,3 +0,0 @@
1
- import { saga } from 'sagaflow-js'
2
-
3
- export const issue = saga('invoice.issue', {})
@@ -1,5 +0,0 @@
1
- import { createWorkflow } from '@corpus/workflows'
2
-
3
- // BREACH: the same hand-written fork under the other engine's factory. A plain string names ONE
4
- // entity, which is what the generic base entity replaced.
5
- export const createInvoice = createWorkflow('invoice.create', async (input) => input)
@@ -1,8 +0,0 @@
1
- import { createWorkflow } from '@corpus/workflows'
2
-
3
- // ACCEPTED: the generic factory. Every entity's create flow comes from one body, and the name is
4
- // assembled from the entity — a template literal the rule cannot read, which is the point rather
5
- // than a gap: what is minted is exactly what this rule is asking for.
6
- export const documentWorkflows = (entity: string) => ({
7
- create: createWorkflow(`${entity}.create`, async (input) => input),
8
- })