@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
@@ -6,8 +6,8 @@ import {
6
6
  offsetToLine,
7
7
  parse,
8
8
  walk,
9
- } from './ast.js';
10
- import type { AstNode } from './ast.js';
9
+ } from '@ontrails/source';
10
+ import type { AstNode } from '@ontrails/source';
11
11
  import { isTestFile } from './scan.js';
12
12
  import { collectTrailIds } from './specs.js';
13
13
  import type {
@@ -21,8 +21,8 @@ import {
21
21
  getNodeDeclaration,
22
22
  getNodeDeclarations,
23
23
  getNodeElements,
24
- getNodeExportKind,
25
24
  getNodeExported,
25
+ getNodeExportKind,
26
26
  getNodeId,
27
27
  getNodeKey,
28
28
  getNodeLeft,
@@ -35,8 +35,8 @@ import {
35
35
  offsetToLine,
36
36
  parse,
37
37
  walk,
38
- } from './ast.js';
39
- import type { AstNode } from './ast.js';
38
+ } from '@ontrails/source';
39
+ import type { AstNode } from '@ontrails/source';
40
40
  import { registeredRuleNames } from './registry-names.js';
41
41
  import type { WardenDiagnostic, WardenRule } from './types.js';
42
42
 
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Self-governance rule: warden rules must inspect the AST via the helpers in
3
- * `./ast.ts` rather than regex-scanning raw source text. Raw-text scans
3
+ * `../source/*` helpers rather than regex-scanning raw source text. Raw-text scans
4
4
  * produce false positives on string literals, template payloads, and
5
5
  * docstrings — see TRL-335 and ADR-0036.
6
6
  *
@@ -16,13 +16,12 @@
16
16
  * bug as scanning with one — see TRL-345.
17
17
  * 4. `rawNodeFieldCastSite` — asserting AST nodes through
18
18
  * `as unknown as { field?: ... }` where a typed accessor exists on
19
- * `./ast.ts`.
19
+ * `../source/*`.
20
20
  *
21
21
  * This rule is path-anchored to this package's own `src/rules/` directory so
22
22
  * it never fires against a consumer repo that happens to share the same
23
- * folder layout. `ast.ts` itself is excluded because it IS the raw-text
24
- * interface to the parser; `types.ts`, `index.ts`, `registry-names.ts`, and
25
- * anything under `__tests__` are also excluded.
23
+ * folder layout. Support modules such as `types.ts`, `index.ts`,
24
+ * `registry-names.ts`, and anything under `__tests__` are excluded.
26
25
  */
27
26
  import { basename as pathBasename, dirname, resolve, sep } from 'node:path';
28
27
  import { fileURLToPath } from 'node:url';
@@ -54,8 +53,8 @@ import {
54
53
  offsetToLine,
55
54
  parse,
56
55
  walk,
57
- } from './ast.js';
58
- import type { AstNode } from './ast.js';
56
+ } from '@ontrails/source';
57
+ import type { AstNode } from '@ontrails/source';
59
58
  import type { WardenDiagnostic, WardenRule } from './types.js';
60
59
 
61
60
  const RULE_NAME = 'warden-rules-use-ast';
@@ -109,12 +108,10 @@ const SELF_RULES_DIRS: ReadonlySet<string> = new Set(
109
108
 
110
109
  /**
111
110
  * Stems of files in `src/rules/` (and their bundled `dist/rules/` twins) that
112
- * are NOT themselves warden rules and therefore must not be checked. `ast` is
113
- * the raw-text interface to the parser and legitimately touches source text;
114
- * the others are support modules without a `check()` function.
111
+ * are not themselves Warden rules and therefore must not be checked. These
112
+ * are support modules without a `check()` function.
115
113
  */
116
114
  const EXCLUDED_STEMS: readonly string[] = [
117
- 'ast',
118
115
  'index',
119
116
  'registry-names',
120
117
  'scan',
@@ -125,9 +122,7 @@ const EXCLUDED_STEMS: readonly string[] = [
125
122
 
126
123
  /**
127
124
  * Both `.ts` (source layout) and `.js` (dist layout) basenames must be
128
- * excluded so the rule stays silent when pointed at a bundled tree. The
129
- * dist-layout `ast.js` contains the same raw-text parser entry point as
130
- * `ast.ts` and would false-positive if scanned.
125
+ * excluded so the rule stays silent when pointed at a bundled tree.
131
126
  */
132
127
  const EXCLUDED_BASENAMES: ReadonlySet<string> = new Set(
133
128
  EXCLUDED_STEMS.flatMap((stem) => [`${stem}.ts`, `${stem}.js`])
@@ -374,7 +369,7 @@ const regexConstructionSite = (node: AstNode): RegexConstructionSite | null => {
374
369
  };
375
370
 
376
371
  const DIAGNOSTIC_ADVICE =
377
- 'Warden rules must inspect the AST via packages/warden/src/rules/ast.ts helpers, not regex-scan raw source text. ' +
372
+ 'Warden rules must inspect source code via @ontrails/source helpers, not regex-scan raw source text. ' +
378
373
  'Use findStringLiterals, findTrailDefinitions, findConfigProperty, or a similar AST walker. ' +
379
374
  'Raw-text scanning produces false positives on string literals, template payloads, and docstrings — see TRL-335, ADR-0036.';
380
375
 
@@ -514,7 +509,7 @@ const detectRawNodeFieldCast = (node: AstNode): DetectedSite | null => {
514
509
  const fields = entries.map(([field, accessor]) => `${field} -> ${accessor}`);
515
510
  return {
516
511
  identifier: expression,
517
- message: `${RULE_NAME}: raw AST node-field cast should use typed helpers from ./ast.js (${fields.join(', ')}). Raw node-field casts drift from the curated @ontrails/warden/ast guard surface.`,
512
+ message: `${RULE_NAME}: raw AST node-field cast should use typed helpers from the shared source helpers (${fields.join(', ')}). Raw node-field casts drift from the shared source helper surface.`,
518
513
  severity: 'warn',
519
514
  start: node.start,
520
515
  };
@@ -1108,7 +1103,7 @@ export const wardenRulesUseAst: WardenRule = {
1108
1103
  return analyze(sourceCode, filePath, ast);
1109
1104
  },
1110
1105
  description:
1111
- 'Enforces that warden rules inspect the AST via packages/warden/src/rules/ast.ts helpers rather than regex-scanning raw source text or hand-casting node fields.',
1106
+ 'Enforces that warden rules inspect source code via @ontrails/source helpers rather than regex-scanning raw source text or hand-casting node fields.',
1112
1107
  name: RULE_NAME,
1113
1108
  severity: 'error',
1114
1109
  };
@@ -9,7 +9,7 @@ const orphanSignal = signal('invoice.paid', {
9
9
  });
10
10
 
11
11
  const orphanConsumer = trail('invoice.audit', {
12
- blaze: () => Result.ok({ ok: true }),
12
+ implementation: () => Result.ok({ ok: true }),
13
13
  input: z.object({ invoiceId: z.string() }),
14
14
  on: [orphanSignal],
15
15
  output: z.object({ ok: z.boolean() }),
@@ -21,24 +21,24 @@ const producedSignal = signal('invoice.created', {
21
21
  });
22
22
 
23
23
  const producerTrail = trail('invoice.create', {
24
- blaze: async (_input, ctx) => {
24
+ fires: [producedSignal],
25
+ implementation: async (_input, ctx) => {
25
26
  await ctx.fire?.(producedSignal, { invoiceId: 'inv_1' });
26
27
  return Result.ok({ invoiceId: 'inv_1' });
27
28
  },
28
- fires: [producedSignal],
29
29
  input: z.object({}),
30
30
  output: z.object({ invoiceId: z.string() }),
31
31
  });
32
32
 
33
33
  const producedConsumer = trail('invoice.index', {
34
- blaze: () => Result.ok({ ok: true }),
34
+ implementation: () => Result.ok({ ok: true }),
35
35
  input: z.object({ invoiceId: z.string() }),
36
36
  on: [producedSignal],
37
37
  output: z.object({ ok: z.boolean() }),
38
38
  });
39
39
 
40
40
  const scheduledConsumer = trail('invoice.reconcile', {
41
- blaze: () => Result.ok({ ok: true }),
41
+ implementation: () => Result.ok({ ok: true }),
42
42
  input: z.object({}),
43
43
  on: [schedule('schedule.invoice.reconcile', { cron: '0 * * * *' })],
44
44
  output: z.object({ ok: z.boolean() }),
@@ -0,0 +1,108 @@
1
+ import { capturedKernel } from '../rules/captured-kernel.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const capturedKernelTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: {
8
+ diagnostics: [
9
+ {
10
+ filePath: 'packages/core/src/kernel.ts',
11
+ guidance: {
12
+ steps: [
13
+ '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.',
14
+ '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.',
15
+ 'Otherwise, preserve the current owner or choose another doctrinal owner.',
16
+ ],
17
+ summary:
18
+ 'Review ownership before an internal re-exported kernel hardens into a public package seam.',
19
+ },
20
+ line: 1,
21
+ message:
22
+ '@ontrails/core export target "@ontrails/core/kernel" re-exports internal target "./internal/kernel.js" and is consumed by external production packages @ontrails/cli, @ontrails/store. Review ownership of the exported subpath and its captured kernel before it becomes a durable public seam.',
23
+ rule: 'captured-kernel',
24
+ severity: 'warn',
25
+ },
26
+ ],
27
+ },
28
+ input: {
29
+ filePath: 'packages/core/src/kernel.ts',
30
+ importResolutionsByFile: {
31
+ 'packages/cli/src/index.ts': [
32
+ {
33
+ crossesPackageBoundary: true,
34
+ importSource: '@ontrails/core/kernel',
35
+ importerPath: 'packages/cli/src/index.ts',
36
+ isInternalTarget: false,
37
+ line: 1,
38
+ packageName: '@ontrails/core',
39
+ packageRoot: 'packages/core',
40
+ resolvedPath: 'packages/core/src/kernel.ts',
41
+ usesPublicExport: true,
42
+ },
43
+ ],
44
+ 'packages/core/src/kernel.ts': [
45
+ {
46
+ crossesPackageBoundary: false,
47
+ importSource: './internal/kernel.js',
48
+ importerPath: 'packages/core/src/kernel.ts',
49
+ isInternalTarget: true,
50
+ line: 1,
51
+ packageName: '@ontrails/core',
52
+ packageRoot: 'packages/core',
53
+ resolvedPath: 'packages/core/src/internal/kernel.ts',
54
+ usesPublicExport: false,
55
+ },
56
+ ],
57
+ 'packages/store/src/index.ts': [
58
+ {
59
+ crossesPackageBoundary: true,
60
+ importSource: '@ontrails/core/kernel',
61
+ importerPath: 'packages/store/src/index.ts',
62
+ isInternalTarget: false,
63
+ line: 1,
64
+ packageName: '@ontrails/core',
65
+ packageRoot: 'packages/core',
66
+ resolvedPath: 'packages/core/src/kernel.ts',
67
+ usesPublicExport: true,
68
+ },
69
+ ],
70
+ },
71
+ knownTrailIds: [],
72
+ publicWorkspaces: {
73
+ '@ontrails/cli': {
74
+ exportTargets: {
75
+ '@ontrails/cli': 'packages/cli/src/index.ts',
76
+ },
77
+ hasExports: true,
78
+ name: '@ontrails/cli',
79
+ packageJsonPath: 'packages/cli/package.json',
80
+ rootDir: 'packages/cli',
81
+ },
82
+ '@ontrails/core': {
83
+ exportTargets: {
84
+ '@ontrails/core': 'packages/core/src/index.ts',
85
+ '@ontrails/core/kernel': 'packages/core/src/kernel.ts',
86
+ },
87
+ hasExports: true,
88
+ name: '@ontrails/core',
89
+ packageJsonPath: 'packages/core/package.json',
90
+ rootDir: 'packages/core',
91
+ },
92
+ '@ontrails/store': {
93
+ exportTargets: {
94
+ '@ontrails/store': 'packages/store/src/index.ts',
95
+ },
96
+ hasExports: true,
97
+ name: '@ontrails/store',
98
+ packageJsonPath: 'packages/store/package.json',
99
+ rootDir: 'packages/store',
100
+ },
101
+ },
102
+ sourceCode: "export { kernel } from './internal/kernel.js';\n",
103
+ },
104
+ name: 'Flags captured kernels consumed by multiple production packages',
105
+ },
106
+ ],
107
+ rule: capturedKernel,
108
+ });
@@ -6,23 +6,23 @@ export const circularRefsTrail = wrapRule({
6
6
  {
7
7
  expected: { diagnostics: [] },
8
8
  input: {
9
- contourReferencesByName: {
9
+ entityReferencesByName: {
10
10
  gist: ['user'],
11
11
  user: [],
12
12
  },
13
- filePath: 'contours.ts',
14
- knownContourIds: ['gist', 'user'],
13
+ filePath: 'entities.ts',
14
+ knownEntityIds: ['gist', 'user'],
15
15
  knownTrailIds: [],
16
- sourceCode: `const user = contour("user", {
16
+ sourceCode: `const user = entity("user", {
17
17
  id: z.string().uuid(),
18
18
  }, { identity: "id" });
19
19
 
20
- const gist = contour("gist", {
20
+ const gist = entity("gist", {
21
21
  id: z.string().uuid(),
22
22
  ownerId: user.id(),
23
23
  }, { identity: "id" });`,
24
24
  },
25
- name: 'Acyclic contour references stay clean',
25
+ name: 'Acyclic entity references stay clean',
26
26
  },
27
27
  ],
28
28
  rule: circularRefs,
@@ -5,16 +5,16 @@ import { cliCommandRouteCoherence } from '../rules/cli-command-route-coherence.j
5
5
  import { wrapTopoRule } from './wrap-rule.js';
6
6
 
7
7
  const searchTrail = trail('wayfind.search', {
8
- blaze: () => Result.ok([]),
9
8
  cli: {
10
9
  aliases: ['find'],
11
10
  },
11
+ implementation: () => Result.ok([]),
12
12
  input: z.object({ query: z.string() }),
13
13
  output: z.array(z.string()),
14
14
  });
15
15
 
16
16
  const collidingTrail = trail('wayfind.find', {
17
- blaze: () => Result.ok([]),
17
+ implementation: () => Result.ok([]),
18
18
  input: z.object({ query: z.string() }),
19
19
  output: z.array(z.string()),
20
20
  });
@@ -9,7 +9,7 @@ export const composesDeclarationsTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.onboard", {
11
11
  composes: ["entity.create"],
12
- blaze: async (input, ctx) => {
12
+ implementation: async (input, ctx) => {
13
13
  const result = await ctx.compose("entity.create", input);
14
14
  return Result.ok(result);
15
15
  }
@@ -9,7 +9,7 @@ export const contextNoSurfaceTypesTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `import { trail, Result } from "@ontrails/core";
11
11
  trail("entity.show", {
12
- blaze: async (input, ctx) => {
12
+ implementation: async (input, ctx) => {
13
13
  return Result.ok({ name: "test" });
14
14
  }
15
15
  })`,
@@ -11,12 +11,12 @@ export const deadInternalTrailTrail = wrapRule({
11
11
  knownTrailIds: ['entity.public', 'entity.sync'],
12
12
  sourceCode: `trail('entity.public', {
13
13
  composes: ['entity.sync'],
14
- blaze: async (_input, ctx) => ctx.compose('entity.sync', {}),
14
+ implementation: async (_input, ctx) => ctx.compose('entity.sync', {}),
15
15
  });
16
16
 
17
17
  trail('entity.sync', {
18
18
  visibility: 'internal',
19
- blaze: async () => Result.ok({}),
19
+ implementation: async () => Result.ok({}),
20
20
  });`,
21
21
  },
22
22
  name: 'Internal trails stay clean when another trail composes them',
@@ -20,7 +20,7 @@ export const deadPublicTrailTrail = wrapRule({
20
20
  filePath: 'packages/regrade/src/downstream/report.ts',
21
21
  knownTrailIds: ['regrade.downstream.report', 'regrade'],
22
22
  sourceCode: `export const regradeReportTrail = trail('regrade.downstream.report', {
23
- blaze: async () => Result.ok({}),
23
+ implementation: async () => Result.ok({}),
24
24
  });`,
25
25
  topoTrailIds: ['regrade'],
26
26
  },
@@ -1,5 +1,5 @@
1
1
  import { topo } from '@ontrails/core';
2
- import { deriveTopoGraph } from '@ontrails/topographer';
2
+ import { deriveTopoGraph } from '@ontrails/topography';
3
3
 
4
4
  import { deprecationWithoutGuidance } from '../rules/trail-versioning-topo.js';
5
5
  import { wrapTopoRule } from './wrap-rule.js';
@@ -8,13 +8,13 @@ const sharedInput = z.object({ target: z.string() });
8
8
  const sharedOutput = z.object({ ok: z.boolean() });
9
9
 
10
10
  const canonicalTrail = trail('survey.diff', {
11
- blaze: () => Result.ok({ ok: true }),
11
+ implementation: () => Result.ok({ ok: true }),
12
12
  input: sharedInput,
13
13
  output: sharedOutput,
14
14
  });
15
15
 
16
16
  const duplicateTrail = trail('diff', {
17
- blaze: () => Result.ok({ ok: true }),
17
+ implementation: () => Result.ok({ ok: true }),
18
18
  input: sharedInput,
19
19
  output: sharedOutput,
20
20
  });
@@ -28,7 +28,7 @@ export const duplicatePublicContractTrail = wrapTopoRule({
28
28
  filePath: '<topo>',
29
29
  line: 1,
30
30
  message:
31
- 'Likely duplicate public trail contracts "diff", "survey.diff" share the same input, output, intent, permits, resources, contours, composes, signals, and detours. Keep one contract with aliases/input mappings, compose a distinct wrapper, or document why these public contracts are separate.',
31
+ 'Likely duplicate public trail contracts "diff", "survey.diff" share the same input, output, intent, permits, resources, entities, composes, signals, and detours. Keep one contract with aliases/input mappings, compose a distinct wrapper, or document why these public contracts are separate.',
32
32
  rule: 'duplicate-public-contract',
33
33
  severity: 'warn',
34
34
  },
@@ -0,0 +1,21 @@
1
+ import { entityExists } from '../rules/entity-exists.js';
2
+ import { wrapRule } from './wrap-rule.js';
3
+
4
+ export const entityExistsTrail = wrapRule({
5
+ examples: [
6
+ {
7
+ expected: { diagnostics: [] },
8
+ input: {
9
+ filePath: 'entity.ts',
10
+ knownEntityIds: ['user'],
11
+ knownTrailIds: ['user.create'],
12
+ sourceCode: `trail("user.create", {
13
+ entities: [user],
14
+ implementation: async (input, ctx) => Result.ok({ ok: true }),
15
+ })`,
16
+ },
17
+ name: 'Declared entities resolve to known project entities',
18
+ },
19
+ ],
20
+ rule: entityExists,
21
+ });
@@ -6,8 +6,8 @@ export const exampleValidTrail = wrapRule({
6
6
  {
7
7
  expected: { diagnostics: [] },
8
8
  input: {
9
- filePath: 'contours.ts',
10
- sourceCode: `const user = contour("user", {
9
+ filePath: 'entities.ts',
10
+ sourceCode: `const user = entity("user", {
11
11
  id: z.string().uuid(),
12
12
  name: z.string(),
13
13
  }, {
@@ -18,7 +18,7 @@ export const exampleValidTrail = wrapRule({
18
18
  identity: "id",
19
19
  });`,
20
20
  },
21
- name: 'Contour examples validate against their schema',
21
+ name: 'Entity examples validate against their schema',
22
22
  },
23
23
  ],
24
24
  rule: exampleValid,
@@ -10,7 +10,7 @@ export const firesDeclarationsTrail = wrapRule({
10
10
  sourceCode: `const entityCreated = signal("entity.created", { payload: z.object({}) });
11
11
  trail("entity.onboard", {
12
12
  fires: [entityCreated],
13
- blaze: async (input, ctx) => {
13
+ implementation: async (input, ctx) => {
14
14
  await ctx.fire(entityCreated, { id: input.id });
15
15
  return Result.ok({});
16
16
  }
@@ -1,7 +1,7 @@
1
- import { forkWithoutPreservedBlaze } from '../rules/trail-versioning-source.js';
1
+ import { forkWithoutPreservedImplementation } from '../rules/trail-versioning-source.js';
2
2
  import { wrapRule } from './wrap-rule.js';
3
3
 
4
- export const forkWithoutPreservedBlazeTrail = wrapRule({
4
+ export const forkWithoutPreservedImplementationTrail = wrapRule({
5
5
  examples: [
6
6
  {
7
7
  expected: { diagnostics: [] },
@@ -20,12 +20,12 @@ trail('versioned.clean', {
20
20
  },
21
21
  },
22
22
  },
23
- blaze: async () => Result.ok({ message: 'ok' }),
23
+ implementation: async () => Result.ok({ message: 'ok' }),
24
24
  });
25
25
  `,
26
26
  },
27
27
  name: 'Revision entries use transpose',
28
28
  },
29
29
  ],
30
- rule: forkWithoutPreservedBlaze,
30
+ rule: forkWithoutPreservedImplementation,
31
31
  });
@@ -8,7 +8,7 @@ export const implementationReturnsResultTrail = wrapRule({
8
8
  input: {
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.show", {
11
- blaze: async (input, ctx) => {
11
+ implementation: async (input, ctx) => {
12
12
  return Result.ok({ name: "test" });
13
13
  }
14
14
  })`,
@@ -9,10 +9,10 @@ type Connection = Readonly<Record<string, Accessor>>;
9
9
 
10
10
  const noop = (): undefined => undefined;
11
11
 
12
- const buildResource = (id: string, contourName: string, accessor: Accessor) =>
12
+ const buildResource = (id: string, entityName: string, accessor: Accessor) =>
13
13
  resource<Connection>(id, {
14
- create: () => Result.ok({ [contourName]: accessor }),
15
- mock: () => ({ [contourName]: accessor }),
14
+ create: () => Result.ok({ [entityName]: accessor }),
15
+ mock: () => ({ [entityName]: accessor }),
16
16
  });
17
17
 
18
18
  const buildCrudTrail = (
@@ -20,7 +20,7 @@ const buildCrudTrail = (
20
20
  resourceValue: ReturnType<typeof buildResource>
21
21
  ) =>
22
22
  trail(trailId, {
23
- blaze: () => Result.ok({ ok: true }),
23
+ implementation: () => Result.ok({ ok: true }),
24
24
  input: z.object({}),
25
25
  output: z.object({ ok: z.boolean() }),
26
26
  pattern: 'crud',
@@ -1,7 +1,8 @@
1
1
  export { activationOrphanTrail } from './activation-orphan.trail.js';
2
+ export { capturedKernelTrail } from './captured-kernel.trail.js';
2
3
  export { cliCommandRouteCoherenceTrail } from './cli-command-route-coherence.trail.js';
3
4
  export { circularRefsTrail } from './circular-refs.trail.js';
4
- export { contourExistsTrail } from './contour-exists.trail.js';
5
+ export { entityExistsTrail } from './entity-exists.trail.js';
5
6
  export { contextNoSurfaceTypesTrail } from './context-no-surface-types.trail.js';
6
7
  export { composesDeclarationsTrail } from './composes-declarations.trail.js';
7
8
  export { deadInternalTrailTrail } from './dead-internal-trail.trail.js';
@@ -14,7 +15,7 @@ export { duplicateExportedSymbolTrail } from './duplicate-exported-symbol.trail.
14
15
  export { errorMappingCompletenessTrail } from './error-mapping-completeness.trail.js';
15
16
  export { exampleValidTrail } from './example-valid.trail.js';
16
17
  export { firesDeclarationsTrail } from './fires-declarations.trail.js';
17
- export { forkWithoutPreservedBlazeTrail } from './fork-without-preserved-blaze.trail.js';
18
+ export { forkWithoutPreservedImplementationTrail } from './fork-without-preserved-implementation.trail.js';
18
19
  export { governedSymbolResidueTrail } from './governed-symbol-residue.trail.js';
19
20
  export { implementationReturnsResultTrail } from './implementation-returns-result.trail.js';
20
21
  export { incompleteAccessorForStandardOpTrail } from './incomplete-accessor-for-standard-op.trail.js';
@@ -11,12 +11,12 @@ export const intentPropagationTrail = wrapRule({
11
11
  sourceCode: `trail('entity.read', {
12
12
  intent: 'read',
13
13
  composes: ['entity.lookup'],
14
- blaze: async (_input, ctx) => ctx.compose('entity.lookup', {}),
14
+ implementation: async (_input, ctx) => ctx.compose('entity.lookup', {}),
15
15
  });
16
16
 
17
17
  trail('entity.lookup', {
18
18
  intent: 'read',
19
- blaze: async () => Result.ok({}),
19
+ implementation: async () => Result.ok({}),
20
20
  });`,
21
21
  trailIntentsById: {
22
22
  'entity.lookup': 'read',
@@ -7,13 +7,13 @@ import { wrapTopoRule } from './wrap-rule.js';
7
7
  const output = z.object({ ok: z.boolean() });
8
8
 
9
9
  const dotted = trail('widget.ping', {
10
- blaze: () => Result.ok({ ok: true }),
10
+ implementation: () => Result.ok({ ok: true }),
11
11
  input: z.object({}),
12
12
  output,
13
13
  });
14
14
 
15
15
  const kebab = trail('widget-ping', {
16
- blaze: () => Result.ok({ ok: true }),
16
+ implementation: () => Result.ok({ ok: true }),
17
17
  input: z.object({}),
18
18
  output,
19
19
  });
@@ -12,7 +12,7 @@ trail('versioned.schema', {
12
12
  version: 2,
13
13
  input: z.object({ name: z.string() }),
14
14
  output: z.object({ message: z.string() }),
15
- blaze: async () => Result.ok({ message: 'ok' }),
15
+ implementation: async () => Result.ok({ message: 'ok' }),
16
16
  });
17
17
  `,
18
18
  },
@@ -12,7 +12,7 @@ export const missingVisibilityTrail = wrapRule({
12
12
  sourceCode: `trail('entity.resolve', {
13
13
  visibility: 'internal',
14
14
  composeInput: z.object({ forkedFrom: z.string() }),
15
- blaze: async () => Result.ok({}),
15
+ implementation: async () => Result.ok({}),
16
16
  });`,
17
17
  },
18
18
  name: 'Composition-only trails stay quiet when already internal',
@@ -9,10 +9,10 @@ export const noDestructuredComposeTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `trail("entity.onboard", {
11
11
  composes: ["entity.create"],
12
- blaze: async (input, ctx) => ctx.compose("entity.create", input),
12
+ implementation: async (input, ctx) => ctx.compose("entity.create", input),
13
13
  });`,
14
14
  },
15
- name: 'Clean blaze using ctx.compose directly',
15
+ name: 'Clean implementation using ctx.compose directly',
16
16
  },
17
17
  {
18
18
  expected: {
@@ -21,7 +21,7 @@ export const noDestructuredComposeTrail = wrapRule({
21
21
  filePath: 'destructured.ts',
22
22
  line: 4,
23
23
  message:
24
- 'Trail "entity.onboard" destructures compose from the blaze context. Use ctx.compose(...) directly so composition stays visible and Warden can recognize composed Result values.',
24
+ 'Trail "entity.onboard" destructures compose from the implementation context. Use ctx.compose(...) directly so composition stays visible and Warden can recognize composed Result values.',
25
25
  rule: 'no-destructured-compose',
26
26
  severity: 'warn',
27
27
  },
@@ -31,13 +31,13 @@ export const noDestructuredComposeTrail = wrapRule({
31
31
  filePath: 'destructured.ts',
32
32
  sourceCode: `trail("entity.onboard", {
33
33
  composes: ["entity.create"],
34
- blaze: async (input, ctx) => {
34
+ implementation: async (input, ctx) => {
35
35
  const { compose } = ctx;
36
36
  return compose("entity.create", input);
37
37
  },
38
38
  });`,
39
39
  },
40
- name: 'Warns when compose is destructured from the blaze context',
40
+ name: 'Warns when compose is destructured from the implementation context',
41
41
  },
42
42
  ],
43
43
  rule: noDestructuredCompose,
@@ -9,7 +9,7 @@ export const noDirectImplementationCallTrail = wrapRule({
9
9
  filePath: 'clean.ts',
10
10
  sourceCode: `const data = await ctx.compose("entity.show", { id: "1" });`,
11
11
  },
12
- name: 'Clean code using ctx.compose instead of .blaze()',
12
+ name: 'Clean code using ctx.compose instead of .implementation()',
13
13
  },
14
14
  ],
15
15
  rule: noDirectImplementationCall,
@@ -10,7 +10,7 @@ export const noRedundantResultErrorWrapTrail = wrapRule({
10
10
  sourceCode: `import { Result, trail } from "@ontrails/core";
11
11
 
12
12
  trail("entity.load", {
13
- blaze: async (input, ctx) => {
13
+ implementation: async (input, ctx) => {
14
14
  const loaded = await ctx.compose("entity.fetch", input);
15
15
  if (loaded.isErr()) {
16
16
  return loaded;
@@ -39,7 +39,7 @@ trail("entity.load", {
39
39
  sourceCode: `import { Result, trail } from "@ontrails/core";
40
40
 
41
41
  trail("entity.load", {
42
- blaze: async (input, ctx) => {
42
+ implementation: async (input, ctx) => {
43
43
  const loaded = await ctx.compose("entity.fetch", input);
44
44
  if (loaded.isErr()) {
45
45
  return Result.err(loaded.error);