@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,5 +1,5 @@
1
+ import { buildSignalIdentifierResolver } from './source/signals.js';
1
2
  import {
2
- buildSignalIdentifierResolver,
3
3
  deriveConstString,
4
4
  extractStringLiteral,
5
5
  findConfigProperty,
@@ -13,8 +13,9 @@ import {
13
13
  offsetToLine,
14
14
  parse,
15
15
  walk,
16
- } from './ast.js';
17
- import type { AstNode, SignalIdentifierResolver } from './ast.js';
16
+ } from '@ontrails/source';
17
+ import type { AstNode } from '@ontrails/source';
18
+ import type { SignalIdentifierResolver } from './source/signals.js';
18
19
  import { isTestFile } from './scan.js';
19
20
  import type { WardenDiagnostic, WardenRule } from './types.js';
20
21
 
@@ -1,11 +1,10 @@
1
1
  import {
2
- collectContourDefinitionIds,
3
- collectContourReferenceSites,
4
- offsetToLine,
5
- parse,
6
- } from './ast.js';
7
- import type { AstNode } from './ast.js';
8
- import { mergeKnownContourIds } from './contour-ids.js';
2
+ collectEntityDefinitionIds,
3
+ collectEntityReferenceSites,
4
+ } from './source/entities.js';
5
+ import { offsetToLine, parse } from '@ontrails/source';
6
+ import type { AstNode } from '@ontrails/source';
7
+ import { mergeKnownEntityIds } from './entity-ids.js';
9
8
  import { isTestFile } from './scan.js';
10
9
  import type {
11
10
  ProjectAwareWardenRule,
@@ -14,32 +13,32 @@ import type {
14
13
  } from './types.js';
15
14
 
16
15
  const buildMissingReferenceDiagnostic = (
17
- sourceContour: string,
16
+ sourceEntity: string,
18
17
  field: string,
19
- targetContour: string,
18
+ targetEntity: string,
20
19
  filePath: string,
21
20
  line: number
22
21
  ): WardenDiagnostic => ({
23
22
  filePath,
24
23
  line,
25
- message: `Contour "${sourceContour}" field "${field}" references contour "${targetContour}" which is not defined in the project. Define it with contour('${targetContour}', ...) and include it in the topo, or fix the field reference if this is a typo.`,
24
+ message: `Entity "${sourceEntity}" field "${field}" references entity "${targetEntity}" which is not defined in the project. Define it with entity('${targetEntity}', ...) and include it in the topo, or fix the field reference if this is a typo.`,
26
25
  rule: 'reference-exists',
27
26
  severity: 'error',
28
27
  });
29
28
 
30
- const checkContourReferences = (
29
+ const checkEntityReferences = (
31
30
  ast: AstNode,
32
31
  sourceCode: string,
33
32
  filePath: string,
34
- knownContourIds: ReadonlySet<string>
33
+ knownEntityIds: ReadonlySet<string>
35
34
  ): readonly WardenDiagnostic[] => {
36
35
  if (isTestFile(filePath)) {
37
36
  return [];
38
37
  }
39
38
 
40
- return collectContourReferenceSites(ast, knownContourIds).flatMap(
39
+ return collectEntityReferenceSites(ast, knownEntityIds).flatMap(
41
40
  (reference) => {
42
- if (knownContourIds.has(reference.target)) {
41
+ if (knownEntityIds.has(reference.target)) {
43
42
  return [];
44
43
  }
45
44
 
@@ -57,7 +56,7 @@ const checkContourReferences = (
57
56
  };
58
57
 
59
58
  /**
60
- * Checks that every contour `.id()` reference resolves to a known contour.
59
+ * Checks that every entity `.id()` reference resolves to a known entity.
61
60
  */
62
61
  export const referenceExists: ProjectAwareWardenRule = {
63
62
  check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
@@ -66,11 +65,11 @@ export const referenceExists: ProjectAwareWardenRule = {
66
65
  return [];
67
66
  }
68
67
 
69
- return checkContourReferences(
68
+ return checkEntityReferences(
70
69
  ast,
71
70
  sourceCode,
72
71
  filePath,
73
- collectContourDefinitionIds(ast)
72
+ collectEntityDefinitionIds(ast)
74
73
  );
75
74
  },
76
75
  checkWithContext(
@@ -83,16 +82,16 @@ export const referenceExists: ProjectAwareWardenRule = {
83
82
  return [];
84
83
  }
85
84
 
86
- const localContourIds = collectContourDefinitionIds(ast);
87
- return checkContourReferences(
85
+ const localEntityIds = collectEntityDefinitionIds(ast);
86
+ return checkEntityReferences(
88
87
  ast,
89
88
  sourceCode,
90
89
  filePath,
91
- mergeKnownContourIds(localContourIds, context.knownContourIds)
90
+ mergeKnownEntityIds(localEntityIds, context.knownEntityIds)
92
91
  );
93
92
  },
94
93
  description:
95
- 'Ensure every contour field declared via .id() resolves to a known contour.',
94
+ 'Ensure every entity field declared via .id() resolves to a known entity.',
96
95
  name: 'reference-exists',
97
96
  severity: 'error',
98
97
  };
@@ -7,9 +7,10 @@
7
7
  * the `warden-export-symmetry` rule will fail the build if they drift.
8
8
  */
9
9
  import { activationOrphan } from './activation-orphan.js';
10
+ import { capturedKernel } from './captured-kernel.js';
10
11
  import { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
11
12
  import { circularRefs } from './circular-refs.js';
12
- import { contourExists } from './contour-exists.js';
13
+ import { entityExists } from './entity-exists.js';
13
14
  import { contextNoSurfaceTypes } from './context-no-surface-types.js';
14
15
  import { composesDeclarations } from './composes-declarations.js';
15
16
  import { deadInternalTrail } from './dead-internal-trail.js';
@@ -66,7 +67,7 @@ import { surfaceTrailheadCoherence } from './surface-trailhead-coherence.js';
66
67
  import { trailForkCoaching } from './trail-fork-coaching.js';
67
68
  import { trailheadOverrideDivergence } from './trailhead-override-divergence.js';
68
69
  import {
69
- forkWithoutPreservedBlaze,
70
+ forkWithoutPreservedImplementation,
70
71
  markerSchemaUnsupported,
71
72
  versionPinnedCompose,
72
73
  } from './trail-versioning-source.js';
@@ -91,10 +92,11 @@ import { webhookRouteCollision } from './webhook-route-collision.js';
91
92
  */
92
93
  export const registeredRuleNames: readonly string[] = [
93
94
  activationOrphan.name,
95
+ capturedKernel.name,
94
96
  cliCommandRouteCoherence.name,
95
97
  circularRefs.name,
96
98
  contextNoSurfaceTypes.name,
97
- contourExists.name,
99
+ entityExists.name,
98
100
  composesDeclarations.name,
99
101
  deadInternalTrail.name,
100
102
  deadPublicTrail.name,
@@ -107,7 +109,7 @@ export const registeredRuleNames: readonly string[] = [
107
109
  exampleValid.name,
108
110
  firesDeclarations.name,
109
111
  governedSymbolResidue.name,
110
- forkWithoutPreservedBlaze.name,
112
+ forkWithoutPreservedImplementation.name,
111
113
  implementationReturnsResult.name,
112
114
  incompleteAccessorForStandardOp.name,
113
115
  incompleteCrud.name,
@@ -1,4 +1,4 @@
1
- import { hasIgnoreCommentOnLine, splitSourceLines } from './ast.js';
1
+ import { hasIgnoreCommentOnLine, splitSourceLines } from './source/pragmas.js';
2
2
  import { isTestFile } from './scan.js';
3
3
  import type { WardenImportResolution } from '../resolve.js';
4
4
  import type {
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * Validates that resource access matches the declared `resources` array.
3
3
  *
4
- * Statically analyzes trail `blaze` functions to find `db.from(ctx)` and
4
+ * Statically analyzes trail `implementation` functions to find `db.from(ctx)` and
5
5
  * `ctx.resource('db.main')` calls and compares them against the declared
6
6
  * `resources: [...]` array in the trail config. Reports errors for undeclared
7
7
  * access and warnings for unused declarations.
8
8
  */
9
9
 
10
+ import { collectNamedResourceIds } from './source/resources.js';
10
11
  import {
11
- collectNamedResourceIds,
12
12
  extractFirstStringArg,
13
- findBlazeBodies,
14
13
  findConfigProperty,
14
+ findImplementationBodies,
15
15
  findTrailDefinitions,
16
16
  getNodeCallee,
17
17
  getNodeId,
@@ -27,8 +27,8 @@ import {
27
27
  offsetToLine,
28
28
  parse,
29
29
  walkScope,
30
- } from './ast.js';
31
- import type { AstNode } from './ast.js';
30
+ } from '@ontrails/source';
31
+ import type { AstNode } from '@ontrails/source';
32
32
  import { isTestFile } from './scan.js';
33
33
  import type { WardenDiagnostic, WardenRule } from './types.js';
34
34
 
@@ -127,9 +127,11 @@ const extractDeclaredResources = (
127
127
  // Called resource extraction
128
128
  // ---------------------------------------------------------------------------
129
129
 
130
- /** Extract the raw second parameter node from a blaze function. */
131
- const extractContextParamNode = (blazeBody: AstNode): AstNode | null => {
132
- const params = blazeBody['params'] as readonly AstNode[] | undefined;
130
+ /** Extract the raw second parameter node from a implementation function. */
131
+ const extractContextParamNode = (
132
+ implementationBody: AstNode
133
+ ): AstNode | null => {
134
+ const params = implementationBody['params'] as readonly AstNode[] | undefined;
133
135
  if (!params || params.length < 2) {
134
136
  return null;
135
137
  }
@@ -137,7 +139,7 @@ const extractContextParamNode = (blazeBody: AstNode): AstNode | null => {
137
139
  };
138
140
 
139
141
  /**
140
- * Extract the second parameter name from a blaze function node.
142
+ * Extract the second parameter name from a implementation function node.
141
143
  *
142
144
  * Returns null when the parameter is not a plain Identifier (e.g. when the
143
145
  * author destructures `{ resource }` in the parameter list). Parameter-level
@@ -147,8 +149,10 @@ const extractContextParamNode = (blazeBody: AstNode): AstNode | null => {
147
149
  * (AssignmentPattern whose `.left` is the Identifier). Without this, valid
148
150
  * signatures would silently drop out of ctx-access analysis.
149
151
  */
150
- const extractContextParamName = (blazeBody: AstNode): string | null => {
151
- const param = extractContextParamNode(blazeBody);
152
+ const extractContextParamName = (
153
+ implementationBody: AstNode
154
+ ): string | null => {
155
+ const param = extractContextParamNode(implementationBody);
152
156
  if (!param) {
153
157
  return null;
154
158
  }
@@ -175,7 +179,7 @@ const extractResourceAlias = (property: AstNode): string | null => {
175
179
  * Collect `resource` aliases bound via parameter-level destructuring.
176
180
  *
177
181
  * Recognizes `(input, { resource }) => ...` and `(input, { resource: r }) => ...`.
178
- * When the blaze author destructures in the parameter list, there is no
182
+ * When the implementation author destructures in the parameter list, there is no
179
183
  * enclosing `ctx` identifier to track — we seed the resource alias set directly
180
184
  * from the ObjectPattern in `params[1]`.
181
185
  */
@@ -198,10 +202,10 @@ const collectParamResourceAliases = (body: AstNode): ReadonlySet<string> => {
198
202
  /**
199
203
  * Build the set of context parameter names to match against.
200
204
  *
201
- * Returns ONLY the actual second-parameter name from the blaze signature.
202
- * No seeded defaults: if the blaze has no second parameter, the returned set
205
+ * Returns ONLY the actual second-parameter name from the implementation signature.
206
+ * No seeded defaults: if the implementation has no second parameter, the returned set
203
207
  * is empty and no `ctx.resource(...)` / `context.resource(...)` calls are
204
- * tracked for that blaze. An unrelated closure-scoped `ctx` identifier is not
208
+ * tracked for that implementation. An unrelated closure-scoped `ctx` identifier is not
205
209
  * the trail context and must not be treated as one.
206
210
  *
207
211
  * Mirrors `fires-declarations.ts` `buildCtxNames` for the same reason.
@@ -363,13 +367,13 @@ const collectResourceAliases = (
363
367
  return aliases;
364
368
  };
365
369
 
366
- /** Walk blaze bodies and collect resource access that can be resolved statically. */
370
+ /** Walk implementation bodies and collect resource access that can be resolved statically. */
367
371
  const extractCalledResources = (config: AstNode): CalledResources => {
368
372
  const fromNames = new Set<string>();
369
373
  const lookupIds = new Set<string>();
370
374
  const lookupNames = new Set<string>();
371
375
 
372
- for (const body of findBlazeBodies(config)) {
376
+ for (const body of findImplementationBodies(config)) {
373
377
  const ctxNames = buildCtxNames(body);
374
378
  const paramAliases = collectParamResourceAliases(body);
375
379
  const bodyAliases = collectResourceAliases(body, ctxNames);
@@ -3,6 +3,8 @@ import { isDraftId } from '@ontrails/core';
3
3
  import {
4
4
  collectNamedResourceIds,
5
5
  collectResourceDefinitionIds,
6
+ } from './source/resources.js';
7
+ import {
6
8
  extractFirstStringArg,
7
9
  findConfigProperty,
8
10
  findTrailDefinitions,
@@ -12,8 +14,8 @@ import {
12
14
  isStringLiteral,
13
15
  offsetToLine,
14
16
  parse,
15
- } from './ast.js';
16
- import type { AstNode } from './ast.js';
17
+ } from '@ontrails/source';
18
+ import type { AstNode } from '@ontrails/source';
17
19
  import { isTestFile } from './scan.js';
18
20
  import type {
19
21
  ProjectAwareWardenRule,
@@ -5,8 +5,8 @@ import {
5
5
  offsetToLine,
6
6
  parse,
7
7
  walk,
8
- } from './ast.js';
9
- import type { AstNode } from './ast.js';
8
+ } from '@ontrails/source';
9
+ import type { AstNode } from '@ontrails/source';
10
10
  import { isTestFile } from './scan.js';
11
11
  import type { WardenDiagnostic, WardenRule } from './types.js';
12
12
 
@@ -22,8 +22,8 @@ import {
22
22
  offsetToLine,
23
23
  parse,
24
24
  walk,
25
- } from './ast.js';
26
- import type { AstNode } from './ast.js';
25
+ } from '@ontrails/source';
26
+ import type { AstNode } from '@ontrails/source';
27
27
  import { isFrameworkInternalFile, isTestFile } from './scan.js';
28
28
  import type { WardenDiagnostic, WardenRule } from './types.js';
29
29
 
@@ -42,14 +42,26 @@ export const governedVocabularyScopeSchema = z.object({
42
42
  include: z.array(z.string().min(1)).optional(),
43
43
  });
44
44
 
45
+ export const governedVocabularySymbolRenameMatchModes = [
46
+ 'exact',
47
+ 'identifier-segment',
48
+ ] as const;
49
+
45
50
  export const governedVocabularySymbolRenameSchema = z.object({
46
51
  from: z.string().min(1),
52
+ match: z.enum(governedVocabularySymbolRenameMatchModes).default('exact'),
47
53
  reviewDeclarationTypes: z.array(z.string().min(1)).default([]),
48
54
  to: z.string().min(1),
49
55
  });
50
56
 
51
57
  export const governedVocabularyLiteralRenameSchema = z.object({
52
58
  from: z.string().min(1),
59
+ match: z.enum(['exact', 'property-key', 'review']).optional(),
60
+ moduleSpecifier: z
61
+ .object({
62
+ targetPackage: z.string().min(1),
63
+ })
64
+ .optional(),
53
65
  to: z.string().min(1),
54
66
  });
55
67
 
@@ -136,23 +148,67 @@ const reviewFunctionParamDeclarations = {
136
148
  };
137
149
 
138
150
  const v1VocabularyHistoricalExcludes = [
151
+ '.agents/goals/**',
152
+ '**/.agents/goals/**',
139
153
  '.agents/memory/**',
154
+ '**/.agents/memory/**',
155
+ '.agents/notes/**',
156
+ '**/.agents/notes/**',
157
+ '.claude/agent-memory/**',
158
+ '**/.claude/agent-memory/**',
140
159
  '.agents/plans/archive/**',
160
+ '**/.agents/plans/archive/**',
141
161
  '.changeset/**',
162
+ '**/.changeset/**',
163
+ '.scratch/**',
164
+ '**/.scratch/**',
165
+ '.trails/regrade/history/**',
166
+ '**/.trails/regrade/history/**',
142
167
  '**/CHANGELOG.md',
168
+ '**/.tmp-tests/**',
169
+ ];
170
+
171
+ const v1VocabularyHistoricalPreservePaths = [
172
+ '.agents/plans/**',
173
+ '**/.agents/plans/**',
174
+ 'docs/adr/0*.md',
175
+ 'docs/adr/decision-map.json',
176
+ 'docs/migration/**',
177
+ 'docs/releases/beta*.md',
178
+ 'docs/releases/v1-vocabulary-reset.md',
179
+ 'docs/releases/v1-vocabulary-transition-workflow.md',
180
+ 'scripts/vocab-cutover-*.ts',
143
181
  ];
144
182
 
145
183
  const v1VocabularySelfExcludes = [
184
+ 'packages/warden/src/__tests__/retired-vocabulary.test.ts',
146
185
  'packages/warden/src/rules/retired-vocabulary.ts',
147
186
  ];
148
187
 
149
188
  const unique = (values: readonly string[]): string[] => [...new Set(values)];
150
189
 
190
+ const escapeRegExp = (value: string): string =>
191
+ value.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&');
192
+
193
+ const formsPattern = (forms: readonly string[]): string =>
194
+ `(?:${forms.map(escapeRegExp).join('|')})`;
195
+
151
196
  const defineV1Transition = (
152
197
  input: GovernedVocabularyTransitionInput
153
198
  ): GovernedVocabularyTransition =>
154
199
  defineTransition({
155
200
  ...input,
201
+ preserve: [
202
+ {
203
+ paths: v1VocabularyHistoricalPreservePaths,
204
+ pattern: formsPattern(
205
+ unique([...(input.oldForms ?? []), ...(input.reviewForms ?? [])])
206
+ ),
207
+ reason:
208
+ 'Preserve authored migration plans and historical decision/release evidence while keeping occurrences visible to the run ledger.',
209
+ },
210
+ ...(input.preserve ?? []),
211
+ ],
156
212
  scope: {
157
213
  ...input.scope,
158
214
  exclude: unique([
@@ -220,11 +276,15 @@ export const governedVocabularyTransitions =
220
276
  blaze: 'implementation',
221
277
  blazes: 'implementations',
222
278
  },
223
- status: 'planned',
279
+ status: 'complete',
280
+ stringLiteralRenames: [
281
+ { from: 'blaze', match: 'property-key', to: 'implementation' },
282
+ ],
224
283
  symbolRenames: [
225
284
  {
226
285
  ...reviewFunctionParamDeclarations,
227
286
  from: 'blaze',
287
+ match: 'identifier-segment',
228
288
  to: 'implementation',
229
289
  },
230
290
  {
@@ -236,10 +296,12 @@ export const governedVocabularyTransitions =
236
296
  target: { kind: 'single', to: 'implementation' },
237
297
  }),
238
298
  defineV1Transition({
239
- codeIdentifiers: ['contour', 'contours'],
299
+ codeIdentifiers: ['contour', 'contours', 'wayfind.contours'],
240
300
  docs: {
241
301
  guidance: [
242
302
  'Keep domain-object semantics distinct from entities in app data until the occurrence is classified.',
303
+ 'Treat contour code/API identifiers as governed symbols, with FunctionParam shadows routed to review.',
304
+ 'Rewrite exact framework string literals only; prose, substrings, and inflections stay outside mechanical apply.',
243
305
  ],
244
306
  summary:
245
307
  'The domain object declaration term is moving from contour to entity.',
@@ -250,22 +312,121 @@ export const governedVocabularyTransitions =
250
312
  'Move the domain object declaration vocabulary from contour to entity for v1.',
251
313
  kind: 'vocabulary',
252
314
  oldForms: ['contour', 'contours', 'Contour'],
253
- reviewForms: ['Contour'],
315
+ reviewForms: ['Contour', 'Contours', 'contoured', 'contouring'],
254
316
  safeRewriteForms: {
255
317
  contour: 'entity',
256
318
  contours: 'entities',
257
319
  },
258
- status: 'planned',
320
+ status: 'complete',
321
+ stringLiteralRenames: [
322
+ { from: 'contour', match: 'review', to: 'entity' },
323
+ { from: 'contours', match: 'review', to: 'entities' },
324
+ { from: 'wayfind.contours', to: 'wayfind.entities' },
325
+ ],
259
326
  symbolRenames: [
260
- { ...reviewFunctionParamDeclarations, from: 'contour', to: 'entity' },
327
+ {
328
+ ...reviewFunctionParamDeclarations,
329
+ from: 'contour',
330
+ match: 'identifier-segment',
331
+ to: 'entity',
332
+ },
261
333
  {
262
334
  ...reviewFunctionParamDeclarations,
263
335
  from: 'contours',
336
+ match: 'identifier-segment',
264
337
  to: 'entities',
265
338
  },
266
339
  ],
267
340
  target: { kind: 'single', to: 'entity' },
268
341
  }),
342
+ defineV1Transition({
343
+ codeIdentifiers: [
344
+ '@ontrails/topographer',
345
+ '@ontrails/topographer/backend-support',
346
+ '0042-core-topographer-boundary-doctrine',
347
+ 'core-topographer-boundary-doctrine',
348
+ 'Topographer-owned',
349
+ 'packages/topographer',
350
+ 'topographer',
351
+ 'topographers',
352
+ ],
353
+ docs: {
354
+ guidance: [
355
+ 'Treat topographer code/API identifiers as governed symbols, with case and compound forms handled by identifier-segment matching.',
356
+ 'Rewrite only exact package route/module-specifier occurrences; near routes and larger route strings stay untouched.',
357
+ ],
358
+ summary:
359
+ 'The graph artifact package and vocabulary moved from topographer to topography.',
360
+ },
361
+ from: 'topographer',
362
+ id: 'v1-topographer-topography',
363
+ intent:
364
+ 'Move graph artifact package routes and code vocabulary from topographer to topography for v1.',
365
+ kind: 'vocabulary',
366
+ oldForms: [
367
+ '@ontrails/topographer',
368
+ '@ontrails/topographer/backend-support',
369
+ '0042-core-topographer-boundary-doctrine',
370
+ 'core-topographer-boundary-doctrine',
371
+ 'Topographer-owned',
372
+ 'packages/topographer',
373
+ 'topographer',
374
+ 'topographers',
375
+ 'Topographer',
376
+ ],
377
+ reviewForms: ['Topographer'],
378
+ safeRewriteForms: {
379
+ '0042-core-topographer-boundary-doctrine':
380
+ '0042-core-topography-boundary-doctrine',
381
+ '@ontrails/topographer': '@ontrails/topography',
382
+ '@ontrails/topographer/backend-support':
383
+ '@ontrails/topography/backend-support',
384
+ 'Topographer-owned': 'Topography-owned',
385
+ 'core-topographer-boundary-doctrine':
386
+ 'core-topography-boundary-doctrine',
387
+ 'packages/topographer': 'packages/topography',
388
+ topographer: 'topography',
389
+ topographers: 'topographies',
390
+ },
391
+ status: 'complete',
392
+ stringLiteralRenames: [
393
+ {
394
+ from: '@ontrails/topographer',
395
+ moduleSpecifier: { targetPackage: '@ontrails/topography' },
396
+ to: '@ontrails/topography',
397
+ },
398
+ {
399
+ from: '@ontrails/topographer/backend-support',
400
+ moduleSpecifier: { targetPackage: '@ontrails/topography' },
401
+ to: '@ontrails/topography/backend-support',
402
+ },
403
+ {
404
+ from: '0042-core-topographer-boundary-doctrine',
405
+ to: '0042-core-topography-boundary-doctrine',
406
+ },
407
+ {
408
+ from: 'core-topographer-boundary-doctrine',
409
+ to: 'core-topography-boundary-doctrine',
410
+ },
411
+ { from: 'Topographer-owned', to: 'Topography-owned' },
412
+ { from: 'packages/topographer', to: 'packages/topography' },
413
+ ],
414
+ symbolRenames: [
415
+ {
416
+ ...reviewFunctionParamDeclarations,
417
+ from: 'topographer',
418
+ match: 'identifier-segment',
419
+ to: 'topography',
420
+ },
421
+ {
422
+ ...reviewFunctionParamDeclarations,
423
+ from: 'topographers',
424
+ match: 'identifier-segment',
425
+ to: 'topographies',
426
+ },
427
+ ],
428
+ target: { kind: 'single', to: 'topography' },
429
+ }),
269
430
  defineV1Transition({
270
431
  codeIdentifiers: [
271
432
  'facets',
@@ -318,6 +479,82 @@ export const governedVocabularyTransitions =
318
479
  ],
319
480
  target: { kind: 'single', to: 'trailhead' },
320
481
  }),
482
+ defineV1Transition({
483
+ codeIdentifiers: ['@ontrails/warden/ast'],
484
+ docs: {
485
+ guidance: [
486
+ 'Treat the package route as an exact code string and module specifier, not as vocabulary prose or an identifier segment.',
487
+ 'Rewrite only exact string literal/module-specifier occurrences; near routes and larger strings stay untouched.',
488
+ ],
489
+ summary:
490
+ 'The reusable AST helper route moved from @ontrails/warden/ast to @ontrails/source.',
491
+ },
492
+ from: '@ontrails/warden/ast',
493
+ id: 'v1-warden-ast-source',
494
+ intent:
495
+ 'Move reusable AST helper imports from @ontrails/warden/ast to @ontrails/source for v1.',
496
+ kind: 'vocabulary',
497
+ oldForms: ['@ontrails/warden/ast'],
498
+ preserve: [
499
+ {
500
+ paths: [
501
+ 'adapters/commander/src/__tests__/to-commander.test.ts',
502
+ 'apps/trails/src/__tests__/mcp.test.ts',
503
+ 'apps/trails/src/__tests__/regrade.test.ts',
504
+ 'packages/regrade/src/downstream/__tests__/ast-rewrite.test.ts',
505
+ 'packages/regrade/src/downstream/__tests__/vocabulary.test.ts',
506
+ 'packages/warden/src/__tests__/retired-vocabulary.test.ts',
507
+ 'scripts/verify-oxc-resolver-published.ts',
508
+ ],
509
+ pattern: '^@ontrails/warden/ast$',
510
+ reason:
511
+ 'Preserve transition regression fixtures and the intentional negative resolver assertion after the public route is retired.',
512
+ },
513
+ ],
514
+ reviewForms: [],
515
+ safeRewriteForms: {
516
+ '@ontrails/warden/ast': '@ontrails/source',
517
+ },
518
+ status: 'complete',
519
+ stringLiteralRenames: [
520
+ {
521
+ from: '@ontrails/warden/ast',
522
+ moduleSpecifier: { targetPackage: '@ontrails/source' },
523
+ to: '@ontrails/source',
524
+ },
525
+ ],
526
+ target: { kind: 'single', to: '@ontrails/source' },
527
+ }),
528
+ defineV1Transition({
529
+ codeIdentifiers: ['@ontrails/wayfinder'],
530
+ docs: {
531
+ guidance: [
532
+ 'Treat the package route as an exact code string and module specifier, not as Wayfind product vocabulary.',
533
+ 'Rewrite only the exact package route; subpaths, near routes, and larger strings stay untouched for review.',
534
+ ],
535
+ summary:
536
+ 'The Wayfinder package API moved into @ontrails/topography while Wayfind remains the operator-facing product name.',
537
+ },
538
+ from: '@ontrails/wayfinder',
539
+ id: 'v1-wayfinder-topography',
540
+ intent:
541
+ 'Move programmatic Wayfinder imports into @ontrails/topography while preserving Wayfind surface vocabulary.',
542
+ kind: 'vocabulary',
543
+ oldForms: ['@ontrails/wayfinder'],
544
+ reviewForms: [],
545
+ safeRewriteForms: {
546
+ '@ontrails/wayfinder': '@ontrails/topography',
547
+ },
548
+ status: 'complete',
549
+ stringLiteralRenames: [
550
+ {
551
+ from: '@ontrails/wayfinder',
552
+ moduleSpecifier: { targetPackage: '@ontrails/topography' },
553
+ to: '@ontrails/topography',
554
+ },
555
+ ],
556
+ target: { kind: 'single', to: '@ontrails/topography' },
557
+ }),
321
558
  defineV1Transition({
322
559
  codeIdentifiers: [],
323
560
  docs: {