@ontrails/warden 1.0.0-beta.39 → 1.0.0-beta.41

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 (134) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/README.md +5 -5
  3. package/package.json +10 -12
  4. package/src/cli.ts +49 -44
  5. package/src/command.ts +2 -2
  6. package/src/drift.ts +4 -3
  7. package/src/index.ts +3 -2
  8. package/src/project-context.ts +3 -3
  9. package/src/resolve.ts +2 -2
  10. package/src/rules/captured-kernel.ts +375 -0
  11. package/src/rules/circular-refs.ts +12 -16
  12. package/src/rules/cli-command-route-coherence.ts +1 -1
  13. package/src/rules/composes-declarations.ts +15 -13
  14. package/src/rules/context-no-surface-types.ts +4 -3
  15. package/src/rules/dead-internal-trail.ts +3 -3
  16. package/src/rules/dead-public-trail.ts +3 -3
  17. package/src/rules/draft-file-marking.ts +4 -9
  18. package/src/rules/draft-visible-debt.ts +3 -8
  19. package/src/rules/duplicate-public-contract.ts +50 -5
  20. package/src/rules/{contour-exists.ts → entity-exists.ts} +67 -64
  21. package/src/rules/entity-ids.ts +15 -0
  22. package/src/rules/error-mapping-completeness.ts +2 -2
  23. package/src/rules/example-valid.ts +84 -84
  24. package/src/rules/fires-declarations.ts +27 -22
  25. package/src/rules/governed-symbol-residue.ts +339 -32
  26. package/src/rules/implementation-returns-result.ts +8 -8
  27. package/src/rules/incomplete-accessor-for-standard-op.ts +15 -15
  28. package/src/rules/incomplete-crud.ts +42 -40
  29. package/src/rules/index.ts +9 -6
  30. package/src/rules/intent-propagation.ts +3 -5
  31. package/src/rules/layer-field-name-drift.ts +2 -2
  32. package/src/rules/library-projection-coherence.ts +2 -2
  33. package/src/rules/metadata.ts +37 -15
  34. package/src/rules/missing-reconcile.ts +3 -4
  35. package/src/rules/missing-visibility.ts +2 -1
  36. package/src/rules/no-destructured-compose.ts +14 -12
  37. package/src/rules/no-direct-implementation-call.ts +12 -7
  38. package/src/rules/no-legacy-cli-alias-export.ts +2 -2
  39. package/src/rules/no-native-error-result.ts +2 -2
  40. package/src/rules/no-redundant-result-error-wrap.ts +10 -10
  41. package/src/rules/no-sync-result-assumption.ts +84 -78
  42. package/src/rules/no-throw-in-detour-recover.ts +2 -2
  43. package/src/rules/no-throw-in-implementation.ts +13 -7
  44. package/src/rules/no-top-level-surface.ts +2 -2
  45. package/src/rules/on-references-exist.ts +3 -3
  46. package/src/rules/orphaned-signal.ts +4 -5
  47. package/src/rules/owner-projection-parity.ts +2 -2
  48. package/src/rules/public-export-example-coverage.ts +10 -4
  49. package/src/rules/public-internal-deep-imports.ts +2 -3
  50. package/src/rules/read-intent-fires.ts +4 -3
  51. package/src/rules/reference-exists.ts +20 -21
  52. package/src/rules/registry-names.ts +6 -4
  53. package/src/rules/resolved-import-boundary.ts +1 -1
  54. package/src/rules/resource-declarations.ts +21 -17
  55. package/src/rules/resource-exists.ts +4 -2
  56. package/src/rules/resource-id-grammar.ts +2 -2
  57. package/src/rules/resource-mock-coverage.ts +2 -2
  58. package/src/rules/retired-vocabulary.ts +242 -5
  59. package/src/rules/source/composition.ts +165 -0
  60. package/src/rules/source/drafts.ts +164 -0
  61. package/src/rules/source/entities.ts +618 -0
  62. package/src/rules/source/pragmas.ts +45 -0
  63. package/src/rules/source/resources.ts +64 -0
  64. package/src/rules/source/signals.ts +397 -0
  65. package/src/rules/source/stores.ts +310 -0
  66. package/src/rules/static-resource-accessor-preference.ts +15 -11
  67. package/src/rules/surface-overlay-coherence.ts +1 -1
  68. package/src/rules/surface-trailhead-coherence.ts +2 -2
  69. package/src/rules/trail-fork-coaching.ts +27 -18
  70. package/src/rules/trail-versioning-source.ts +16 -12
  71. package/src/rules/trail-versioning-topo.ts +2 -2
  72. package/src/rules/trailhead-override-divergence.ts +2 -2
  73. package/src/rules/types.ts +7 -7
  74. package/src/rules/unmaterialized-activation-source.ts +1 -0
  75. package/src/rules/unreachable-detour-shadowing.ts +2 -2
  76. package/src/rules/valid-describe-refs.ts +2 -2
  77. package/src/rules/warden-export-symmetry.ts +3 -3
  78. package/src/rules/warden-rules-use-ast.ts +12 -17
  79. package/src/trails/activation-orphan.trail.ts +5 -5
  80. package/src/trails/captured-kernel.trail.ts +108 -0
  81. package/src/trails/circular-refs.trail.ts +6 -6
  82. package/src/trails/cli-command-route-coherence.trail.ts +2 -2
  83. package/src/trails/composes-declarations.trail.ts +1 -1
  84. package/src/trails/context-no-surface-types.trail.ts +1 -1
  85. package/src/trails/dead-internal-trail.trail.ts +2 -2
  86. package/src/trails/dead-public-trail.trail.ts +1 -1
  87. package/src/trails/deprecation-without-guidance.trail.ts +1 -1
  88. package/src/trails/duplicate-public-contract.trail.ts +3 -3
  89. package/src/trails/entity-exists.trail.ts +21 -0
  90. package/src/trails/example-valid.trail.ts +3 -3
  91. package/src/trails/fires-declarations.trail.ts +1 -1
  92. package/src/trails/{fork-without-preserved-blaze.trail.ts → fork-without-preserved-implementation.trail.ts} +4 -4
  93. package/src/trails/implementation-returns-result.trail.ts +1 -1
  94. package/src/trails/incomplete-accessor-for-standard-op.trail.ts +4 -4
  95. package/src/trails/index.ts +3 -2
  96. package/src/trails/intent-propagation.trail.ts +2 -2
  97. package/src/trails/library-projection-coherence.trail.ts +2 -2
  98. package/src/trails/marker-schema-unsupported.trail.ts +1 -1
  99. package/src/trails/missing-visibility.trail.ts +1 -1
  100. package/src/trails/no-destructured-compose.trail.ts +5 -5
  101. package/src/trails/no-direct-implementation-call.trail.ts +1 -1
  102. package/src/trails/no-redundant-result-error-wrap.trail.ts +2 -2
  103. package/src/trails/no-sync-result-assumption.trail.ts +2 -2
  104. package/src/trails/no-throw-in-detour-recover.trail.ts +1 -1
  105. package/src/trails/no-throw-in-implementation.trail.ts +1 -1
  106. package/src/trails/on-references-exist.trail.ts +1 -1
  107. package/src/trails/pending-force.trail.ts +1 -1
  108. package/src/trails/permit-governance.trail.ts +2 -2
  109. package/src/trails/prefer-schema-inference.trail.ts +1 -1
  110. package/src/trails/public-output-schema.trail.ts +2 -2
  111. package/src/trails/public-union-output-discriminants.trail.ts +1 -1
  112. package/src/trails/read-intent-fires.trail.ts +1 -1
  113. package/src/trails/reference-exists.trail.ts +5 -5
  114. package/src/trails/resource-declarations.trail.ts +1 -1
  115. package/src/trails/resource-exists.trail.ts +1 -1
  116. package/src/trails/run.ts +4 -6
  117. package/src/trails/scheduled-destroy-intent.trail.ts +2 -2
  118. package/src/trails/schema.ts +9 -9
  119. package/src/trails/signal-graph-coaching.trail.ts +2 -2
  120. package/src/trails/static-resource-accessor-preference.trail.ts +1 -1
  121. package/src/trails/surface-overlay-coherence.trail.ts +1 -1
  122. package/src/trails/trail-fork-coaching.trail.ts +1 -1
  123. package/src/trails/unmaterialized-activation-source.trail.ts +5 -5
  124. package/src/trails/valid-detour-contract.trail.ts +1 -1
  125. package/src/trails/version-gap.trail.ts +1 -1
  126. package/src/trails/version-pinned-compose.trail.ts +1 -1
  127. package/src/trails/version-without-examples.trail.ts +1 -1
  128. package/src/trails/warden-rules-use-ast.trail.ts +2 -2
  129. package/src/trails/webhook-route-collision.trail.ts +2 -2
  130. package/src/trails/wrap-rule.ts +17 -17
  131. package/src/ast.ts +0 -137
  132. package/src/rules/ast.ts +0 -4143
  133. package/src/rules/contour-ids.ts +0 -15
  134. package/src/trails/contour-exists.trail.ts +0 -21
@@ -1,9 +1,12 @@
1
1
  import { crudOperations } from '@ontrails/store';
2
2
 
3
+ import { collectNamedEntityIds } from './source/entities.js';
3
4
  import {
4
- collectNamedContourIds,
5
5
  collectNamedStoreTableIds,
6
6
  deriveStoreTableId,
7
+ isNamedCall,
8
+ } from './source/stores.js';
9
+ import {
7
10
  getNodeArguments,
8
11
  getNodeElements,
9
12
  getNodeId,
@@ -14,13 +17,12 @@ import {
14
17
  getNodeSpecifiers,
15
18
  getStringValue,
16
19
  identifierName,
17
- isNamedCall,
18
20
  isStringLiteral,
19
21
  offsetToLine,
20
22
  parse,
21
23
  walk,
22
- } from './ast.js';
23
- import type { AstNode } from './ast.js';
24
+ } from '@ontrails/source';
25
+ import type { AstNode } from '@ontrails/source';
24
26
  import { isTestFile } from './scan.js';
25
27
  import type {
26
28
  ProjectAwareWardenRule,
@@ -30,10 +32,10 @@ import type {
30
32
 
31
33
  const CRUD_OPERATION_SET = new Set<string>(crudOperations);
32
34
 
33
- /** Sentinel entity id prefix for contours imported from another module. */
34
- const IMPORTED_CONTOUR_PREFIX = 'imported:';
35
- const IMPORTED_CONTOUR_SOURCE_SEPARATOR = '#';
36
- const CONTOUR_BINDING_SUFFIX = 'Contour';
35
+ /** Sentinel entity id prefix for entities imported from another module. */
36
+ const IMPORTED_ENTITY_PREFIX = 'imported:';
37
+ const IMPORTED_ENTITY_SOURCE_SEPARATOR = '#';
38
+ const ENTITY_BINDING_SUFFIX = 'Entity';
37
39
 
38
40
  interface CrudCoverage {
39
41
  readonly entityId: string;
@@ -103,8 +105,8 @@ const collectImportDeclarationAliases = (
103
105
  }
104
106
  };
105
107
 
106
- const extractInlineContourId = (node: AstNode | undefined): string | null => {
107
- if (!isNamedCall(node, 'contour')) {
108
+ const extractInlineEntityId = (node: AstNode | undefined): string | null => {
109
+ if (!isNamedCall(node, 'entity')) {
108
110
  return null;
109
111
  }
110
112
 
@@ -127,18 +129,18 @@ const collectImportAliasResolutions = (
127
129
  return aliases;
128
130
  };
129
131
 
130
- const buildImportedContourId = (source: string, importedName: string): string =>
131
- `${IMPORTED_CONTOUR_PREFIX}${source}${IMPORTED_CONTOUR_SOURCE_SEPARATOR}${importedName}`;
132
+ const buildImportedEntityId = (source: string, importedName: string): string =>
133
+ `${IMPORTED_ENTITY_PREFIX}${source}${IMPORTED_ENTITY_SOURCE_SEPARATOR}${importedName}`;
132
134
 
133
- const parseImportedContourId = (
135
+ const parseImportedEntityId = (
134
136
  entityId: string
135
137
  ): { readonly bindingName: string; readonly source?: string } | null => {
136
- if (!entityId.startsWith(IMPORTED_CONTOUR_PREFIX)) {
138
+ if (!entityId.startsWith(IMPORTED_ENTITY_PREFIX)) {
137
139
  return null;
138
140
  }
139
141
 
140
- const remainder = entityId.slice(IMPORTED_CONTOUR_PREFIX.length);
141
- const separator = remainder.lastIndexOf(IMPORTED_CONTOUR_SOURCE_SEPARATOR);
142
+ const remainder = entityId.slice(IMPORTED_ENTITY_PREFIX.length);
143
+ const separator = remainder.lastIndexOf(IMPORTED_ENTITY_SOURCE_SEPARATOR);
142
144
  if (separator === -1) {
143
145
  return { bindingName: remainder };
144
146
  }
@@ -150,38 +152,38 @@ const parseImportedContourId = (
150
152
  };
151
153
 
152
154
  /**
153
- * Resolve an identifier reference (bound contour or imported alias) to a
155
+ * Resolve an identifier reference (bound entity or imported alias) to a
154
156
  * stable entity id. Imported identifiers return a `pending-resolution`
155
157
  * sentinel so coverage is still tracked instead of silently dropped.
156
158
  */
157
- const resolveContourIdentifier = (
159
+ const resolveEntityIdentifier = (
158
160
  name: string,
159
- namedContourIds: ReadonlyMap<string, string>,
161
+ namedEntityIds: ReadonlyMap<string, string>,
160
162
  importAliases: ReadonlyMap<string, ImportAliasResolution>
161
163
  ): string | null => {
162
- const local = namedContourIds.get(name);
164
+ const local = namedEntityIds.get(name);
163
165
  if (local) {
164
166
  return local;
165
167
  }
166
168
 
167
169
  const imported = importAliases.get(name);
168
170
  if (imported) {
169
- return buildImportedContourId(imported.source, imported.importedName);
171
+ return buildImportedEntityId(imported.source, imported.importedName);
170
172
  }
171
173
 
172
174
  return null;
173
175
  };
174
176
 
175
- const stripContourBindingSuffix = (entityId: string): string =>
176
- entityId.endsWith(CONTOUR_BINDING_SUFFIX)
177
- ? entityId.slice(0, -CONTOUR_BINDING_SUFFIX.length)
177
+ const stripEntityBindingSuffix = (entityId: string): string =>
178
+ entityId.endsWith(ENTITY_BINDING_SUFFIX)
179
+ ? entityId.slice(0, -ENTITY_BINDING_SUFFIX.length)
178
180
  : entityId;
179
181
 
180
182
  const normalizeProjectEntityId = (
181
183
  entityId: string,
182
184
  projectEntityIds: ReadonlySet<string>
183
185
  ): string => {
184
- const imported = parseImportedContourId(entityId);
186
+ const imported = parseImportedEntityId(entityId);
185
187
  if (!imported) {
186
188
  return entityId;
187
189
  }
@@ -190,11 +192,11 @@ const normalizeProjectEntityId = (
190
192
  if (projectEntityIds.has(localId)) {
191
193
  return localId;
192
194
  }
193
- const strippedId = stripContourBindingSuffix(localId);
195
+ const strippedId = stripEntityBindingSuffix(localId);
194
196
  if (
195
197
  strippedId !== localId &&
196
198
  (projectEntityIds.has(strippedId) ||
197
- projectEntityIds.has(`${IMPORTED_CONTOUR_PREFIX}${strippedId}`))
199
+ projectEntityIds.has(`${IMPORTED_ENTITY_PREFIX}${strippedId}`))
198
200
  ) {
199
201
  return strippedId;
200
202
  }
@@ -218,19 +220,19 @@ const normalizeProjectCoverage = (
218
220
  };
219
221
 
220
222
  /**
221
- * Resolve a `deriveTrail` contour argument to a stable entity id.
223
+ * Resolve a `deriveTrail` entity argument to a stable entity id.
222
224
  *
223
225
  * Resolution order:
224
- * 1. Inline `contour('name', …)` call — use the authored name.
225
- * 2. Local identifier bound to `contour('name', …)` via `namedContourIds`.
226
+ * 1. Inline `entity('name', …)` call — use the authored name.
227
+ * 2. Local identifier bound to `entity('name', …)` via `namedEntityIds`.
226
228
  * 3. Identifier imported from another module — mark as a pending
227
229
  * `imported:<local>` coverage observation so the rule still tracks the
228
230
  * entity across the file instead of silently dropping it. The prefix is
229
231
  * stripped from diagnostic output for readability.
230
232
  */
231
- const resolveContourId = (
233
+ const resolveEntityId = (
232
234
  node: AstNode | undefined,
233
- namedContourIds: ReadonlyMap<string, string>,
235
+ namedEntityIds: ReadonlyMap<string, string>,
234
236
  importAliases: ReadonlyMap<string, ImportAliasResolution>
235
237
  ): string | null => {
236
238
  if (!node) {
@@ -240,11 +242,11 @@ const resolveContourId = (
240
242
  if (node.type === 'Identifier') {
241
243
  const name = identifierName(node);
242
244
  return name
243
- ? resolveContourIdentifier(name, namedContourIds, importAliases)
245
+ ? resolveEntityIdentifier(name, namedEntityIds, importAliases)
244
246
  : null;
245
247
  }
246
248
 
247
- return extractInlineContourId(node);
249
+ return extractInlineEntityId(node);
248
250
  };
249
251
 
250
252
  const ensureCoverage = (
@@ -277,16 +279,16 @@ const extractCrudOperation = (node: AstNode | undefined): string | null => {
277
279
 
278
280
  const extractDerivedCrudEntry = (
279
281
  node: AstNode,
280
- namedContourIds: ReadonlyMap<string, string>,
282
+ namedEntityIds: ReadonlyMap<string, string>,
281
283
  importAliases: ReadonlyMap<string, ImportAliasResolution>
282
284
  ): { readonly entityId: string; readonly operation: string } | null => {
283
285
  if (!isNamedCall(node, 'deriveTrail')) {
284
286
  return null;
285
287
  }
286
288
 
287
- const [contourArg, operationArg] = getNodeArguments(node);
289
+ const [entityArg, operationArg] = getNodeArguments(node);
288
290
  const operation = extractCrudOperation(operationArg);
289
- const entityId = resolveContourId(contourArg, namedContourIds, importAliases);
291
+ const entityId = resolveEntityId(entityArg, namedEntityIds, importAliases);
290
292
  return operation && entityId ? { entityId, operation } : null;
291
293
  };
292
294
 
@@ -295,11 +297,11 @@ const collectDerivedCrudCoverage = (
295
297
  sourceCode: string
296
298
  ): ReadonlyMap<string, CrudCoverage> => {
297
299
  const coverageByEntityId = new Map<string, CrudCoverage>();
298
- const namedContourIds = collectNamedContourIds(ast);
300
+ const namedEntityIds = collectNamedEntityIds(ast);
299
301
  const importAliases = collectImportAliasResolutions(ast);
300
302
 
301
303
  walk(ast, (node) => {
302
- const entry = extractDerivedCrudEntry(node, namedContourIds, importAliases);
304
+ const entry = extractDerivedCrudEntry(node, namedEntityIds, importAliases);
303
305
  if (!entry) {
304
306
  return;
305
307
  }
@@ -504,7 +506,7 @@ const collectIncompleteEntities = (
504
506
  };
505
507
 
506
508
  const formatEntityLabel = (entityId: string): string => {
507
- const imported = parseImportedContourId(entityId);
509
+ const imported = parseImportedEntityId(entityId);
508
510
  return imported
509
511
  ? `${imported.bindingName} (imported, pending-resolution)`
510
512
  : entityId;
@@ -1,7 +1,8 @@
1
1
  import { activationOrphan } from './activation-orphan.js';
2
+ import { capturedKernel } from './captured-kernel.js';
2
3
  import { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
3
4
  import { circularRefs } from './circular-refs.js';
4
- import { contourExists } from './contour-exists.js';
5
+ import { entityExists } from './entity-exists.js';
5
6
  import { contextNoSurfaceTypes } from './context-no-surface-types.js';
6
7
  import { composesDeclarations } from './composes-declarations.js';
7
8
  import { deadInternalTrail } from './dead-internal-trail.js';
@@ -58,7 +59,7 @@ import { surfaceTrailheadCoherence } from './surface-trailhead-coherence.js';
58
59
  import { trailForkCoaching } from './trail-fork-coaching.js';
59
60
  import { trailheadOverrideDivergence } from './trailhead-override-divergence.js';
60
61
  import {
61
- forkWithoutPreservedBlaze,
62
+ forkWithoutPreservedImplementation,
62
63
  markerSchemaUnsupported,
63
64
  versionPinnedCompose,
64
65
  } from './trail-versioning-source.js';
@@ -139,10 +140,11 @@ export {
139
140
  export type { BuiltinWardenRuleName } from './metadata.js';
140
141
 
141
142
  export { activationOrphan } from './activation-orphan.js';
143
+ export { capturedKernel } from './captured-kernel.js';
142
144
  export { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
143
145
  export { noThrowInImplementation } from './no-throw-in-implementation.js';
144
146
  export { circularRefs } from './circular-refs.js';
145
- export { contourExists } from './contour-exists.js';
147
+ export { entityExists } from './entity-exists.js';
146
148
  export { contextNoSurfaceTypes } from './context-no-surface-types.js';
147
149
  export { composesDeclarations } from './composes-declarations.js';
148
150
  export { deadInternalTrail } from './dead-internal-trail.js';
@@ -197,7 +199,7 @@ export { surfaceTrailheadCoherence } from './surface-trailhead-coherence.js';
197
199
  export { trailForkCoaching } from './trail-fork-coaching.js';
198
200
  export { trailheadOverrideDivergence } from './trailhead-override-divergence.js';
199
201
  export {
200
- forkWithoutPreservedBlaze,
202
+ forkWithoutPreservedImplementation,
201
203
  markerSchemaUnsupported,
202
204
  versionPinnedCompose,
203
205
  } from './trail-versioning-source.js';
@@ -219,8 +221,9 @@ export const wardenRules: ReadonlyMap<string, WardenRule> = new Map<
219
221
  WardenRule
220
222
  >([
221
223
  [noThrowInImplementation.name, noThrowInImplementation],
224
+ [capturedKernel.name, capturedKernel],
222
225
  [circularRefs.name, circularRefs],
223
- [contourExists.name, contourExists],
226
+ [entityExists.name, entityExists],
224
227
  [contextNoSurfaceTypes.name, contextNoSurfaceTypes],
225
228
  [composesDeclarations.name, composesDeclarations],
226
229
  [deadInternalTrail.name, deadInternalTrail],
@@ -270,7 +273,7 @@ export const wardenRules: ReadonlyMap<string, WardenRule> = new Map<
270
273
  [unreachableDetourShadowing.name, unreachableDetourShadowing],
271
274
  [wardenExportSymmetry.name, wardenExportSymmetry],
272
275
  [wardenRulesUseAst.name, wardenRulesUseAst],
273
- [forkWithoutPreservedBlaze.name, forkWithoutPreservedBlaze],
276
+ [forkWithoutPreservedImplementation.name, forkWithoutPreservedImplementation],
274
277
  [markerSchemaUnsupported.name, markerSchemaUnsupported],
275
278
  [versionPinnedCompose.name, versionPinnedCompose],
276
279
  ]);
@@ -3,11 +3,9 @@ import {
3
3
  collectNamedTrailIds,
4
4
  collectTrailIntentsById,
5
5
  extractDefinitionComposeTargetIds,
6
- findTrailDefinitions,
7
- offsetToLine,
8
- parse,
9
- } from './ast.js';
10
- import type { AstNode } from './ast.js';
6
+ } from './source/composition.js';
7
+ import { findTrailDefinitions, offsetToLine, parse } from '@ontrails/source';
8
+ import type { AstNode } from '@ontrails/source';
11
9
  import { isTestFile } from './scan.js';
12
10
  import type {
13
11
  ProjectAwareWardenRule,
@@ -8,8 +8,8 @@ import {
8
8
  offsetToLine,
9
9
  parse,
10
10
  walk,
11
- } from './ast.js';
12
- import type { AstNode } from './ast.js';
11
+ } from '@ontrails/source';
12
+ import type { AstNode } from '@ontrails/source';
13
13
  import type { WardenDiagnostic, WardenRule } from './types.js';
14
14
 
15
15
  const RULE_NAME = 'layer-field-name-drift';
@@ -1,8 +1,8 @@
1
- import { deriveTopoGraph } from '@ontrails/topographer';
1
+ import { deriveTopoGraph } from '@ontrails/topography';
2
2
  import type {
3
3
  TopoGraph,
4
4
  TopoGraphLibraryProjection,
5
- } from '@ontrails/topographer';
5
+ } from '@ontrails/topography';
6
6
 
7
7
  import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
8
8
 
@@ -69,6 +69,7 @@ const depthByTier = {
69
69
 
70
70
  const concernByRuleName: Partial<Record<string, WardenRuleConcern>> = {
71
71
  'activation-orphan': 'signals',
72
+ 'captured-kernel': 'general',
72
73
  'cli-command-route-coherence': 'meta',
73
74
  'composes-declarations': 'composition',
74
75
  'context-no-surface-types': 'composition',
@@ -81,7 +82,7 @@ const concernByRuleName: Partial<Record<string, WardenRuleConcern>> = {
81
82
  'duplicate-public-contract': 'meta',
82
83
  'error-mapping-completeness': 'results',
83
84
  'fires-declarations': 'signals',
84
- 'fork-without-preserved-blaze': 'lifecycle',
85
+ 'fork-without-preserved-implementation': 'lifecycle',
85
86
  'governed-symbol-residue': 'lifecycle',
86
87
  'implementation-returns-result': 'results',
87
88
  'intent-propagation': 'composition',
@@ -157,9 +158,30 @@ const builtinWardenRuleMetadataInput = {
157
158
  'Signal activation consumers reference sources with producer declarations.',
158
159
  tier: 'topo-aware',
159
160
  },
161
+ 'captured-kernel': {
162
+ guidance: {
163
+ relatedRules: [
164
+ 'duplicate-exported-symbol',
165
+ 'public-internal-deep-imports',
166
+ 'resolved-import-boundary',
167
+ ],
168
+ steps: [
169
+ 'Review whether the public subpath should become an owned package surface, move back behind the package root, or be split into a better-owned package.',
170
+ 'If the imported capability is reusable source-code machinery, serves at least two independently owned toolchain capabilities, exposes one genuinely shared contract, and owns no verdict, migration plan, graph query, or surface rendering, consider relocating it to @ontrails/source.',
171
+ 'Otherwise, preserve the current owner or choose another doctrinal owner.',
172
+ ],
173
+ summary:
174
+ 'Review ownership before an internal re-exported kernel hardens into a public package seam.',
175
+ },
176
+ invariant:
177
+ 'Public subpath exports that re-export internal kernels receive ownership review after multiple production packages consume them.',
178
+ lifecycle: { state: 'durable' },
179
+ scope: 'advisory',
180
+ tier: 'project-static',
181
+ },
160
182
  'circular-refs': {
161
183
  ...durableExternal,
162
- invariant: 'Contour reference graphs must be acyclic.',
184
+ invariant: 'Entity reference graphs must be acyclic.',
163
185
  tier: 'project-static',
164
186
  },
165
187
  'cli-command-route-coherence': {
@@ -189,11 +211,6 @@ const builtinWardenRuleMetadataInput = {
189
211
  invariant: 'Trail logic stays surface-agnostic.',
190
212
  tier: 'source-static',
191
213
  },
192
- 'contour-exists': {
193
- ...durableExternal,
194
- invariant: 'Declared contour references resolve to known contours.',
195
- tier: 'project-static',
196
- },
197
214
  'dead-internal-trail': {
198
215
  ...durableExternal,
199
216
  invariant: 'Internal trails should be reachable through declared composes.',
@@ -263,6 +280,11 @@ const builtinWardenRuleMetadataInput = {
263
280
  'Public surface trails should not expose duplicate normalized contract facts.',
264
281
  tier: 'topo-aware',
265
282
  },
283
+ 'entity-exists': {
284
+ ...durableExternal,
285
+ invariant: 'Declared entity references resolve to known entities.',
286
+ tier: 'project-static',
287
+ },
266
288
  'error-mapping-completeness': {
267
289
  ...durableExtension,
268
290
  invariant: 'Registered surface error mappers cover every error category.',
@@ -286,9 +308,9 @@ const builtinWardenRuleMetadataInput = {
286
308
  invariant: 'Declared fires stay aligned with signal firing usage.',
287
309
  tier: 'source-static',
288
310
  },
289
- 'fork-without-preserved-blaze': {
311
+ 'fork-without-preserved-implementation': {
290
312
  ...durableExternal,
291
- invariant: 'Fork version entries preserve their historical blaze.',
313
+ invariant: 'Fork version entries preserve their historical implementation.',
292
314
  tier: 'source-static',
293
315
  },
294
316
  'governed-symbol-residue': {
@@ -300,7 +322,7 @@ const builtinWardenRuleMetadataInput = {
300
322
  },
301
323
  'implementation-returns-result': {
302
324
  ...durableExternal,
303
- invariant: 'Blazes return Result values.',
325
+ invariant: 'Implementations return Result values.',
304
326
  tier: 'source-static',
305
327
  },
306
328
  'incomplete-accessor-for-standard-op': {
@@ -368,7 +390,7 @@ const builtinWardenRuleMetadataInput = {
368
390
  'no-destructured-compose': {
369
391
  ...durableExternal,
370
392
  invariant:
371
- 'Trail blazes compose through ctx.compose() directly instead of destructuring compose from the context.',
393
+ 'Trail implementations compose through ctx.compose() directly instead of destructuring compose from the context.',
372
394
  tier: 'source-static',
373
395
  },
374
396
  'no-dev-permit-in-source': {
@@ -447,12 +469,12 @@ const builtinWardenRuleMetadataInput = {
447
469
  relatedRules: ['implementation-returns-result', 'no-native-error-result'],
448
470
  steps: [
449
471
  'Return Result.err() with the most specific TrailsError subclass available.',
450
- 'Use detours for recoverable runtime strategies instead of throwing inside the blaze.',
472
+ 'Use detours for recoverable runtime strategies instead of throwing inside the implementation.',
451
473
  ],
452
474
  summary:
453
- 'Convert thrown failures in blazes into explicit Result.err() outcomes.',
475
+ 'Convert thrown failures in implementations into explicit Result.err() outcomes.',
454
476
  },
455
- invariant: 'Blazes return Result.err() instead of throwing.',
477
+ invariant: 'Implementations return Result.err() instead of throwing.',
456
478
  tier: 'source-static',
457
479
  },
458
480
  'no-top-level-surface': {
@@ -564,7 +586,7 @@ const builtinWardenRuleMetadataInput = {
564
586
  },
565
587
  'reference-exists': {
566
588
  ...durableExternal,
567
- invariant: 'Reference declarations resolve to known contours.',
589
+ invariant: 'Reference declarations resolve to known entities.',
568
590
  tier: 'project-static',
569
591
  },
570
592
  'resolved-import-boundary': {
@@ -2,10 +2,9 @@ import {
2
2
  collectCrudTableIds,
3
3
  collectReconcileTableIds,
4
4
  findStoreTableDefinitions,
5
- offsetToLine,
6
- parse,
7
- } from './ast.js';
8
- import type { AstNode } from './ast.js';
5
+ } from './source/stores.js';
6
+ import { offsetToLine, parse } from '@ontrails/source';
7
+ import type { AstNode } from '@ontrails/source';
9
8
  import { isTestFile } from './scan.js';
10
9
  import type {
11
10
  ProjectAwareWardenRule,
@@ -1,4 +1,5 @@
1
- import { collectComposeTargetTrailIds, parse } from './ast.js';
1
+ import { collectComposeTargetTrailIds } from './source/composition.js';
2
+ import { parse } from '@ontrails/source';
2
3
  import { isTestFile } from './scan.js';
3
4
  import {
4
5
  findTrailLikeSpecs,
@@ -1,5 +1,5 @@
1
1
  import {
2
- findBlazeBodies,
2
+ findImplementationBodies,
3
3
  findTrailDefinitions,
4
4
  getNodeComputed,
5
5
  getNodeId,
@@ -16,15 +16,15 @@ import {
16
16
  offsetToLine,
17
17
  parse,
18
18
  walkWithScopes,
19
- } from './ast.js';
19
+ } from '@ontrails/source';
20
+ import type { AstNode } from '@ontrails/source';
20
21
  import { isFrameworkInternalFile, isTestFile } from './scan.js';
21
- import type { AstNode } from './ast.js';
22
22
  import type { WardenDiagnostic, WardenRule } from './types.js';
23
23
 
24
24
  const RULE_NAME = 'no-destructured-compose';
25
25
 
26
26
  const diagnosticMessage = (trailId: string): string =>
27
- `Trail "${trailId}" destructures compose from the blaze context. Use ctx.compose(...) directly so composition stays visible and Warden can recognize composed Result values.`;
27
+ `Trail "${trailId}" destructures compose from the implementation context. Use ctx.compose(...) directly so composition stays visible and Warden can recognize composed Result values.`;
28
28
 
29
29
  const propertyKeyName = (property: AstNode): string | null => {
30
30
  if (getNodeComputed(property) === true) {
@@ -60,8 +60,8 @@ const findComposeBinding = (pattern: AstNode | undefined): AstNode | null => {
60
60
  return null;
61
61
  };
62
62
 
63
- const blazeParams = (blaze: AstNode): readonly AstNode[] =>
64
- getNodeParams(blaze) ?? [];
63
+ const implementationParams = (implementation: AstNode): readonly AstNode[] =>
64
+ getNodeParams(implementation) ?? [];
65
65
 
66
66
  const destructuredComposeFromVariableDeclarator = (
67
67
  node: AstNode,
@@ -104,13 +104,13 @@ const checkBodyDestructuring = (
104
104
  sourceCode: string,
105
105
  filePath: string,
106
106
  trailId: string,
107
- blaze: AstNode,
107
+ implementation: AstNode,
108
108
  contextName: string
109
109
  ): WardenDiagnostic[] => {
110
110
  const diagnostics: WardenDiagnostic[] = [];
111
111
 
112
112
  walkWithScopes(
113
- blaze,
113
+ implementation,
114
114
  (node, scopes) => {
115
115
  if (isShadowed(contextName, scopes)) {
116
116
  return;
@@ -155,8 +155,10 @@ export const noDestructuredCompose: WardenRule = {
155
155
  continue;
156
156
  }
157
157
 
158
- for (const blaze of findBlazeBodies(definition.config)) {
159
- const params = blazeParams(blaze);
158
+ for (const implementation of findImplementationBodies(
159
+ definition.config
160
+ )) {
161
+ const params = implementationParams(implementation);
160
162
  const [, contextParam] = params;
161
163
  const paramComposeBinding = findComposeBinding(contextParam);
162
164
 
@@ -177,7 +179,7 @@ export const noDestructuredCompose: WardenRule = {
177
179
  sourceCode,
178
180
  filePath,
179
181
  definition.id,
180
- blaze,
182
+ implementation,
181
183
  contextName
182
184
  )
183
185
  );
@@ -188,7 +190,7 @@ export const noDestructuredCompose: WardenRule = {
188
190
  return diagnostics;
189
191
  },
190
192
  description:
191
- 'Coach trail blazes to compose with ctx.compose(...) directly instead of destructuring compose from the context.',
193
+ 'Coach trail implementations to compose with ctx.compose(...) directly instead of destructuring compose from the context.',
192
194
  name: RULE_NAME,
193
195
  severity: 'warn',
194
196
  };
@@ -1,16 +1,21 @@
1
1
  /**
2
- * Flags direct `.blaze()` calls in application code.
2
+ * Flags direct `.implementation()` calls in application code.
3
3
  *
4
- * Uses AST parsing to find `.blaze()` call expressions,
4
+ * Uses AST parsing to find `.implementation()` call expressions,
5
5
  * ignoring occurrences in strings and comments.
6
6
  */
7
7
 
8
- import { isBlazeCall, offsetToLine, parse, walk } from './ast.js';
8
+ import {
9
+ isImplementationCall,
10
+ offsetToLine,
11
+ parse,
12
+ walk,
13
+ } from '@ontrails/source';
9
14
  import { isFrameworkInternalFile, isTestFile } from './scan.js';
10
15
  import type { WardenDiagnostic, WardenRule } from './types.js';
11
16
 
12
17
  /**
13
- * Flags direct `.blaze()` calls in application code.
18
+ * Flags direct `.implementation()` calls in application code.
14
19
  */
15
20
  export const noDirectImplementationCall: WardenRule = {
16
21
  check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
@@ -26,12 +31,12 @@ export const noDirectImplementationCall: WardenRule = {
26
31
  const diagnostics: WardenDiagnostic[] = [];
27
32
 
28
33
  walk(ast, (node) => {
29
- if (isBlazeCall(node)) {
34
+ if (isImplementationCall(node)) {
30
35
  diagnostics.push({
31
36
  filePath,
32
37
  line: offsetToLine(sourceCode, node.start),
33
38
  message:
34
- 'Use ctx.compose("trailId", input) instead of direct .blaze() calls. Direct implementation access bypasses validation, tracing, and layers.',
39
+ 'Use ctx.compose("trailId", input) instead of direct .implementation() calls. Direct implementation access bypasses validation, tracing, and layers.',
35
40
  rule: 'no-direct-implementation-call',
36
41
  severity: 'warn',
37
42
  });
@@ -41,7 +46,7 @@ export const noDirectImplementationCall: WardenRule = {
41
46
  return diagnostics;
42
47
  },
43
48
  description:
44
- 'Disallow direct .blaze() calls in application code. Use ctx.compose() instead.',
49
+ 'Disallow direct .implementation() calls in application code. Use ctx.compose() instead.',
45
50
  name: 'no-direct-implementation-call',
46
51
  severity: 'warn',
47
52
  };
@@ -40,8 +40,8 @@ import {
40
40
  isStringLiteral,
41
41
  offsetToLine,
42
42
  parse,
43
- } from './ast.js';
44
- import type { AstNode } from './ast.js';
43
+ } from '@ontrails/source';
44
+ import type { AstNode } from '@ontrails/source';
45
45
  import type { WardenDiagnostic, WardenFix, WardenRule } from './types.js';
46
46
 
47
47
  const RULE_NAME = 'no-legacy-cli-alias-export';
@@ -7,9 +7,9 @@ import {
7
7
  offsetToLine,
8
8
  parse,
9
9
  walk,
10
- } from './ast.js';
10
+ } from '@ontrails/source';
11
+ import type { AstNode } from '@ontrails/source';
11
12
  import { isFrameworkInternalFile } from './scan.js';
12
- import type { AstNode } from './ast.js';
13
13
  import type { WardenDiagnostic, WardenRule } from './types.js';
14
14
 
15
15
  const RULE_NAME = 'no-native-error-result';