@ontrails/warden 1.0.0-beta.42 → 1.0.0-beta.45

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 (36) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/package.json +15 -10
  3. package/src/adapter-check.ts +1 -1
  4. package/src/cli.ts +81 -5
  5. package/src/index.ts +17 -4
  6. package/src/regrade-history.ts +599 -0
  7. package/src/rules/cli-command-route-coherence.ts +6 -6
  8. package/src/rules/draft-visible-debt.ts +1 -1
  9. package/src/rules/duplicate-exported-symbol.ts +4 -43
  10. package/src/rules/governed-symbol-residue.ts +146 -53
  11. package/src/rules/governed-vocabulary-permutation-watch.ts +76 -0
  12. package/src/rules/implementation-returns-result.ts +99 -9
  13. package/src/rules/index.ts +17 -6
  14. package/src/rules/layer-field-name-drift.ts +2 -2
  15. package/src/rules/{library-projection-coherence.ts → library-render-coherence.ts} +11 -14
  16. package/src/rules/metadata.ts +51 -14
  17. package/src/rules/no-redundant-result-error-wrap.ts +15 -17
  18. package/src/rules/no-retired-cross-vocabulary.ts +0 -1
  19. package/src/rules/{owner-projection-parity.ts → owner-render-parity.ts} +18 -18
  20. package/src/rules/public-internal-deep-imports.ts +1 -3
  21. package/src/rules/public-output-schema.ts +1 -1
  22. package/src/rules/registry-names.ts +6 -4
  23. package/src/rules/retired-vocabulary.ts +775 -41
  24. package/src/rules/surface-overlay-coherence.ts +1 -1
  25. package/src/rules/trail-versioning-source.ts +1 -1
  26. package/src/rules/trailhead-override-divergence.ts +4 -4
  27. package/src/rules/types.ts +51 -5
  28. package/src/trails/governed-vocabulary-permutation-watch.trail.ts +16 -0
  29. package/src/trails/index.ts +3 -2
  30. package/src/trails/layer-field-name-drift.trail.ts +1 -1
  31. package/src/trails/{library-projection-coherence.trail.ts → library-render-coherence.trail.ts} +6 -6
  32. package/src/trails/{owner-projection-parity.trail.ts → owner-render-parity.trail.ts} +4 -4
  33. package/src/trails/public-output-schema.trail.ts +1 -1
  34. package/src/trails/run.ts +44 -2
  35. package/src/trails/schema.ts +41 -0
  36. package/src/trails/wrap-rule.ts +44 -2
@@ -1,12 +1,9 @@
1
1
  import { deriveTopoGraph } from '@ontrails/topography';
2
- import type {
3
- TopoGraph,
4
- TopoGraphLibraryProjection,
5
- } from '@ontrails/topography';
2
+ import type { TopoGraph, TopoGraphLibraryDerived } from '@ontrails/topography';
6
3
 
7
4
  import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
8
5
 
9
- const RULE_NAME = 'library-projection-coherence';
6
+ const RULE_NAME = 'library-render-coherence';
10
7
  const TOPO_FILE = '<topo>';
11
8
 
12
9
  const resolveGraph = (
@@ -26,7 +23,7 @@ const collisionDiagnostic = (
26
23
  ): WardenDiagnostic => ({
27
24
  filePath: TOPO_FILE,
28
25
  line: 1,
29
- message: `Library projection export collision on "${exportName}": trails ${renderTrailIds(trailIds)} derive the same package export. Rename one trail or add a library export override before materializing the generated package.`,
26
+ message: `Library rendering export collision on "${exportName}": trails ${renderTrailIds(trailIds)} derive the same package export. Rename one trail or add a library export override before materializing the generated package.`,
30
27
  rule: RULE_NAME,
31
28
  severity: 'error',
32
29
  });
@@ -37,7 +34,7 @@ const missingTargetDiagnostic = (
37
34
  ): WardenDiagnostic => ({
38
35
  filePath: TOPO_FILE,
39
36
  line: 1,
40
- message: `Library projection export "${exportName}" targets unknown trail "${trailId}". Resolved library exports must stay attached to existing trail contracts.`,
37
+ message: `Library rendering export "${exportName}" targets unknown trail "${trailId}". Resolved library exports must stay attached to existing trail contracts.`,
41
38
  rule: RULE_NAME,
42
39
  severity: 'error',
43
40
  });
@@ -48,16 +45,16 @@ const duplicateExportDiagnostic = (
48
45
  ): WardenDiagnostic => ({
49
46
  filePath: TOPO_FILE,
50
47
  line: 1,
51
- message: `Library projection contains duplicate export "${exportName}" for trails ${renderTrailIds(trailIds)}. The resolved projection should record the collision and keep only one emitted export.`,
48
+ message: `Library rendering contains duplicate export "${exportName}" for trails ${renderTrailIds(trailIds)}. The resolved rendering should record the collision and keep only one emitted export.`,
52
49
  rule: RULE_NAME,
53
50
  severity: 'error',
54
51
  });
55
52
 
56
53
  const collectDuplicateExportDiagnostics = (
57
- projection: TopoGraphLibraryProjection
54
+ rendering: TopoGraphLibraryDerived
58
55
  ): readonly WardenDiagnostic[] => {
59
56
  const trailIdsByExport = new Map<string, string[]>();
60
- for (const entry of projection.exports) {
57
+ for (const entry of rendering.exports) {
61
58
  const trailIds = trailIdsByExport.get(entry.exportName) ?? [];
62
59
  trailIds.push(entry.trailId);
63
60
  trailIdsByExport.set(entry.exportName, trailIds);
@@ -70,14 +67,14 @@ const collectDuplicateExportDiagnostics = (
70
67
  };
71
68
 
72
69
  const collectMissingTargetDiagnostics = (
73
- projection: TopoGraphLibraryProjection,
70
+ rendering: TopoGraphLibraryDerived,
74
71
  knownTrailIds: ReadonlySet<string>
75
72
  ): readonly WardenDiagnostic[] =>
76
- projection.exports
73
+ rendering.exports
77
74
  .filter((entry) => !knownTrailIds.has(entry.trailId))
78
75
  .map((entry) => missingTargetDiagnostic(entry.exportName, entry.trailId));
79
76
 
80
- export const libraryProjectionCoherence: TopoAwareWardenRule = {
77
+ export const libraryRenderCoherence: TopoAwareWardenRule = {
81
78
  checkTopo(topo, context) {
82
79
  const graph = resolveGraph(topo, context?.graph);
83
80
  if (!graph.library) {
@@ -94,7 +91,7 @@ export const libraryProjectionCoherence: TopoAwareWardenRule = {
94
91
  ];
95
92
  },
96
93
  description:
97
- 'Ensure resolved library projection exports are collision-free and target existing trails.',
94
+ 'Ensure resolved library rendering exports are collision-free and target existing trails.',
98
95
  name: RULE_NAME,
99
96
  severity: 'error',
100
97
  };
@@ -84,9 +84,10 @@ const concernByRuleName: Partial<Record<string, WardenRuleConcern>> = {
84
84
  'fires-declarations': 'signals',
85
85
  'fork-without-preserved-implementation': 'lifecycle',
86
86
  'governed-symbol-residue': 'lifecycle',
87
+ 'governed-vocabulary-permutation-watch': 'lifecycle',
87
88
  'implementation-returns-result': 'results',
88
89
  'intent-propagation': 'composition',
89
- 'library-projection-coherence': 'meta',
90
+ 'library-render-coherence': 'meta',
90
91
  'marker-schema-unsupported': 'lifecycle',
91
92
  'missing-reconcile': 'resources',
92
93
  'missing-visibility': 'composition',
@@ -316,10 +317,46 @@ const builtinWardenRuleMetadataInput = {
316
317
  'governed-symbol-residue': {
317
318
  ...durableExternal,
318
319
  fix: { class: 'term-rewrite', safety: 'safe' },
320
+ guidance: {
321
+ docs: [
322
+ {
323
+ label: 'Vocabulary transition workflow',
324
+ path: 'docs/releases/v1-vocabulary-transition-workflow.md',
325
+ },
326
+ ],
327
+ steps: [
328
+ 'Run the governed transition through Regrade so committed history proves the primary migration.',
329
+ 'Use manual edits only for review or cleanup after Regrade exhausts the safe slice.',
330
+ 'Keep the transition registry and committed history evidence aligned.',
331
+ ],
332
+ summary:
333
+ 'Require committed Regrade evidence before completing a governed vocabulary migration.',
334
+ },
319
335
  invariant:
320
- 'Active governed vocabulary symbol renames do not leave retired identifiers in source.',
336
+ 'Governed vocabulary transitions carry committed Regrade provenance and do not leave or reintroduce retired identifiers.',
321
337
  tier: 'source-static',
322
338
  },
339
+ 'governed-vocabulary-permutation-watch': {
340
+ guidance: {
341
+ docs: [
342
+ {
343
+ label: 'Vocabulary transition workflow',
344
+ path: 'docs/releases/v1-vocabulary-transition-workflow.md',
345
+ },
346
+ ],
347
+ steps: [
348
+ 'Add the unknown form to the governed vocabulary plan and run an incremental Regrade plan.',
349
+ 'If the form is intentionally unrelated or preserved, classify it so later history suppresses the advisory.',
350
+ ],
351
+ summary:
352
+ 'Classify unknown governed-stem permutations recorded by committed Regrade history.',
353
+ },
354
+ invariant:
355
+ 'Committed Regrade history keeps unknown governed-stem permutations visible until they are classified.',
356
+ lifecycle: { state: 'durable' },
357
+ scope: 'advisory',
358
+ tier: 'project-static',
359
+ },
323
360
  'implementation-returns-result': {
324
361
  ...durableExternal,
325
362
  invariant: 'Implementations return Result values.',
@@ -343,10 +380,10 @@ const builtinWardenRuleMetadataInput = {
343
380
  'layer-field-name-drift': {
344
381
  ...durableExternal,
345
382
  invariant:
346
- 'Layer input field reserved names are shared across surface projections.',
383
+ 'Layer input field reserved names are shared across surface renderings.',
347
384
  tier: 'source-static',
348
385
  },
349
- 'library-projection-coherence': {
386
+ 'library-render-coherence': {
350
387
  ...durableExternal,
351
388
  guidance: {
352
389
  docs: [
@@ -361,20 +398,20 @@ const builtinWardenRuleMetadataInput = {
361
398
  ],
362
399
  steps: [
363
400
  'Rename one source trail or add an explicit library export override before generating a package.',
364
- 'Keep serialized library projection exports attached to existing trail IDs.',
365
- 'Run the generated-package smoke after repairing projection drift.',
401
+ 'Keep serialized derived library exports attached to existing trail IDs.',
402
+ 'Run the generated-package smoke after repairing rendering drift.',
366
403
  ],
367
404
  summary:
368
- 'Keep resolved library projection exports collision-free and attached to one trail contract.',
405
+ 'Keep resolved derived library exports collision-free and attached to one trail contract.',
369
406
  },
370
407
  invariant:
371
- 'Resolved library projection exports are collision-free and target existing trails.',
408
+ 'Resolved derived library exports are collision-free and target existing trails.',
372
409
  tier: 'topo-aware',
373
410
  },
374
411
  'marker-schema-unsupported': {
375
412
  ...durableExternal,
376
413
  invariant:
377
- 'Versioned schemas stay inside the supported marker projection subset.',
414
+ 'Versioned schemas stay inside the supported marker derivation subset.',
378
415
  tier: 'source-static',
379
416
  },
380
417
  'missing-reconcile': {
@@ -503,8 +540,8 @@ const builtinWardenRuleMetadataInput = {
503
540
  'Derived store signals are consumed by matching trail on: consumers.',
504
541
  tier: 'project-static',
505
542
  },
506
- 'owner-projection-parity': {
507
- invariant: 'Framework projections stay aligned with owner exports.',
543
+ 'owner-render-parity': {
544
+ invariant: 'Framework renderings stay aligned with owner exports.',
508
545
  lifecycle: { state: 'durable' },
509
546
  scope: 'internal',
510
547
  tier: 'source-static',
@@ -565,11 +602,11 @@ const builtinWardenRuleMetadataInput = {
565
602
  docs: [trailContractDocs, wardenDocs],
566
603
  relatedRules: ['public-union-output-discriminants'],
567
604
  steps: [
568
- 'Add an explicit output schema to public trails that can be projected onto MCP or HTTP surfaces.',
605
+ 'Add an explicit output schema to public trails that can be rendered onto MCP or HTTP surfaces.',
569
606
  'If the trail is composition-only, mark it visibility: "internal" instead of exposing it by default.',
570
607
  ],
571
608
  summary:
572
- 'Make public surface result contracts explicit before MCP/HTTP projection.',
609
+ 'Make public surface result contracts explicit before MCP/HTTP rendering.',
573
610
  },
574
611
  invariant: 'Public MCP/HTTP surface trails declare output schemas.',
575
612
  tier: 'topo-aware',
@@ -709,7 +746,7 @@ const builtinWardenRuleMetadataInput = {
709
746
  'Record explicit visibility-widening acceptance and stable-description metadata when a trailhead intentionally widens visibility.',
710
747
  ],
711
748
  summary:
712
- 'Keep trailhead maps reviewable before they reach MCP projection.',
749
+ 'Keep trailhead maps reviewable before they reach MCP rendering.',
713
750
  },
714
751
  invariant:
715
752
  'Trailhead maps avoid selector overlap, hidden visibility widening, and drift-prone dynamic selectors.',
@@ -23,11 +23,11 @@ import {
23
23
  import type { AstNode, AstParentContext } from '@ontrails/source';
24
24
  import {
25
25
  collectAllResultHelperNames,
26
+ collectDirectResultAssignments,
26
27
  collectNamespaceHelperImports,
27
28
  collectResultTypeNames,
28
29
  findNearestBindingScope,
29
- isHelperCall,
30
- isResultExpression,
30
+ isResultProducingExpression,
31
31
  trackScopedResultHelperDeclaration,
32
32
  } from './implementation-returns-result.js';
33
33
  import { isTestFile } from './scan.js';
@@ -82,16 +82,6 @@ const getErrorSourceVariable = (node: AstNode | null): string | null => {
82
82
  return identifierName(member?.object);
83
83
  };
84
84
 
85
- const isResultProducingExpression = (
86
- node: AstNode,
87
- helperNames: ReadonlySet<string>,
88
- namespaceHelpers: NamespaceHelperMap,
89
- scopes: readonly ReadonlySet<string>[],
90
- scopedHelpers: ScopedHelperMap
91
- ): boolean =>
92
- isResultExpression(node) ||
93
- isHelperCall(node, helperNames, namespaceHelpers, scopes, scopedHelpers);
94
-
95
85
  type ResultProvenance = Map<ReadonlySet<string>, Set<string>>;
96
86
 
97
87
  const markResultVariable = (
@@ -158,6 +148,7 @@ const trackVariableDeclarator = (
158
148
  isResultProducingExpression(
159
149
  init,
160
150
  helperNames,
151
+ provenance,
161
152
  namespaceHelpers,
162
153
  scopes,
163
154
  scopedHelpers
@@ -175,11 +166,10 @@ const trackAssignmentExpression = (
175
166
  helperNames: ReadonlySet<string>,
176
167
  namespaceHelpers: NamespaceHelperMap,
177
168
  scopedHelpers: ScopedHelperMap,
169
+ directAssignments: ReadonlySet<AstNode>,
178
170
  scopes: readonly ReadonlySet<string>[]
179
171
  ): void => {
180
172
  const left = getNodeLeft(node);
181
- const operator = getNodeOperator(node);
182
- const right = getNodeRight(node);
183
173
  const name = identifierName(left);
184
174
  if (!name) {
185
175
  return;
@@ -188,16 +178,22 @@ const trackAssignmentExpression = (
188
178
  if (!scope) {
189
179
  return;
190
180
  }
191
- if (
192
- operator === '=' &&
181
+ const right = getNodeRight(node);
182
+ const resultRhs =
183
+ getNodeOperator(node) === '=' &&
193
184
  right &&
194
185
  isResultProducingExpression(
195
186
  right,
196
187
  helperNames,
188
+ provenance,
197
189
  namespaceHelpers,
198
190
  scopes,
199
191
  scopedHelpers
200
- )
192
+ );
193
+ if (
194
+ resultRhs &&
195
+ (hasResultProvenance(provenance, scope, name) ||
196
+ directAssignments.has(node))
201
197
  ) {
202
198
  markResultVariable(provenance, scope, name);
203
199
  return;
@@ -258,6 +254,7 @@ const checkFunctionBody = (
258
254
  const scopedHelpers: MutableScopedHelperMap = new Map();
259
255
  const implScope = collectScopeFrameBindings(owner);
260
256
  const initialScopes = implScope.size > 0 ? [implScope] : [];
257
+ const directAssignments = collectDirectResultAssignments(body);
261
258
 
262
259
  walkWithScopes(
263
260
  body,
@@ -287,6 +284,7 @@ const checkFunctionBody = (
287
284
  helperNames,
288
285
  namespaceHelpers,
289
286
  scopedHelpers,
287
+ directAssignments,
290
288
  scopes
291
289
  );
292
290
  return;
@@ -39,7 +39,6 @@ const ALLOWED_PATH_SUFFIXES: readonly string[] = [
39
39
  '/packages/warden/src/rules/retired-vocabulary.ts',
40
40
  '/packages/warden/src/trails/governed-symbol-residue.trail.ts',
41
41
  '/packages/warden/src/trails/no-retired-cross-vocabulary.trail.ts',
42
- '/scripts/vocab-cutover-rewrite.ts',
43
42
  ];
44
43
 
45
44
  interface SafeRewriteMatch {
@@ -18,14 +18,14 @@ import {
18
18
  import type { AstNode } from '@ontrails/source';
19
19
  import type { WardenDiagnostic, WardenRule } from './types.js';
20
20
 
21
- const RULE_NAME = 'owner-projection-parity';
21
+ const RULE_NAME = 'owner-render-parity';
22
22
 
23
- const HTTP_METHOD_PROJECTION_PATH = resolve(
23
+ const HTTP_METHOD_DERIVATION_PATH = resolve(
24
24
  fileURLToPath(new URL('../../../http/src/method.ts', import.meta.url))
25
25
  );
26
26
 
27
27
  const isTargetFile = (filePath: string): boolean =>
28
- resolve(filePath) === HTTP_METHOD_PROJECTION_PATH;
28
+ resolve(filePath) === HTTP_METHOD_DERIVATION_PATH;
29
29
 
30
30
  const unwrapExpression = (node: AstNode | undefined): AstNode | undefined => {
31
31
  let current = node;
@@ -44,13 +44,13 @@ const unwrapExpression = (node: AstNode | undefined): AstNode | undefined => {
44
44
  return current;
45
45
  };
46
46
 
47
- interface ProjectionMap {
47
+ interface IntentKeyMap {
48
48
  readonly keys: ReadonlySet<string>;
49
49
  readonly node: AstNode;
50
50
  }
51
51
 
52
- const findHttpMethodByIntentMap = (ast: AstNode): ProjectionMap | null => {
53
- let found: ProjectionMap | null = null;
52
+ const findHttpMethodByIntentMap = (ast: AstNode): IntentKeyMap | null => {
53
+ let found: IntentKeyMap | null = null;
54
54
 
55
55
  walk(ast, (node) => {
56
56
  if (found || node.type !== 'VariableDeclarator') {
@@ -89,16 +89,16 @@ const findHttpMethodByIntentMap = (ast: AstNode): ProjectionMap | null => {
89
89
  const buildMessage = (missing: string[], extra: string[]): string => {
90
90
  const details = [
91
91
  missing.length > 0 ? `missing owner intents: ${missing.join(', ')}` : '',
92
- extra.length > 0 ? `unknown projection keys: ${extra.join(', ')}` : '',
92
+ extra.length > 0 ? `unknown intentKeyMap keys: ${extra.join(', ')}` : '',
93
93
  ].filter(Boolean);
94
94
 
95
95
  return [
96
- 'owner-projection-parity: httpMethodByIntent must cover the core intentValues owner vocabulary.',
96
+ 'owner-render-parity: httpMethodByIntent must cover the core intentValues owner vocabulary.',
97
97
  ...details,
98
98
  ].join(' ');
99
99
  };
100
100
 
101
- export const ownerProjectionParity: WardenRule = {
101
+ export const ownerRenderParity: WardenRule = {
102
102
  check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
103
103
  if (!isTargetFile(filePath)) {
104
104
  return [];
@@ -109,35 +109,35 @@ export const ownerProjectionParity: WardenRule = {
109
109
  return [];
110
110
  }
111
111
 
112
- const projection = findHttpMethodByIntentMap(ast);
112
+ const intentKeyMap = findHttpMethodByIntentMap(ast);
113
113
  const ownerKeys = new Set<string>(intentValues);
114
- const projectionKeys = projection?.keys ?? new Set<string>();
114
+ const derivedKeys = intentKeyMap?.keys ?? new Set<string>();
115
115
  const missing = [...ownerKeys]
116
- .filter((key) => !projectionKeys.has(key))
116
+ .filter((key) => !derivedKeys.has(key))
117
117
  .toSorted();
118
- const extra = [...projectionKeys]
118
+ const extra = [...derivedKeys]
119
119
  .filter((key) => !ownerKeys.has(key))
120
120
  .toSorted();
121
121
 
122
- if (projection && missing.length === 0 && extra.length === 0) {
122
+ if (intentKeyMap && missing.length === 0 && extra.length === 0) {
123
123
  return [];
124
124
  }
125
125
 
126
- const node = projection?.node ?? ast;
126
+ const node = intentKeyMap?.node ?? ast;
127
127
  return [
128
128
  {
129
129
  filePath,
130
130
  line: offsetToLine(sourceCode, node.start),
131
- message: projection
131
+ message: intentKeyMap
132
132
  ? buildMessage(missing, extra)
133
- : 'owner-projection-parity: expected httpMethodByIntent to project core intentValues.',
133
+ : 'owner-render-parity: expected httpMethodByIntent to render core intentValues.',
134
134
  rule: RULE_NAME,
135
135
  severity: 'error',
136
136
  },
137
137
  ];
138
138
  },
139
139
  description:
140
- 'Require owner-derived projection maps to cover their authoritative owner vocabulary.',
140
+ 'Require owner-derived intentKeyMap maps to cover their authoritative owner vocabulary.',
141
141
  name: RULE_NAME,
142
142
  severity: 'error',
143
143
  };
@@ -20,9 +20,7 @@ import type { WardenPublicWorkspace } from '../workspaces.js';
20
20
 
21
21
  const RULE_NAME = 'public-internal-deep-imports';
22
22
  const ONTRAILS_SPECIFIER_PATTERN = /^(@ontrails\/[^/]+)(?:\/(.+))?$/;
23
- const ROOT_BARREL_INTERNAL_RE_EXPORT_ALLOWLIST = new Set([
24
- '@ontrails/tracing:./internal/dev-state.js',
25
- ]);
23
+ const ROOT_BARREL_INTERNAL_RE_EXPORT_ALLOWLIST = new Set<string>();
26
24
 
27
25
  interface ReExportSite {
28
26
  readonly importSource: string;
@@ -10,7 +10,7 @@ const diagnosticForTrail = (trail: AnyTrail): WardenDiagnostic => ({
10
10
  filePath: TOPO_FILE,
11
11
  line: 1,
12
12
  message:
13
- `Trail "${trail.id}" is visible to public MCP/HTTP surface projection but does not declare an output schema. ` +
13
+ `Trail "${trail.id}" is visible to public MCP/HTTP surface rendering but does not declare an output schema. ` +
14
14
  'Add an explicit output schema, or mark the trail visibility as internal if it is composition-only.',
15
15
  rule: RULE_NAME,
16
16
  severity: 'error',
@@ -23,12 +23,13 @@ import { errorMappingCompleteness } from './error-mapping-completeness.js';
23
23
  import { exampleValid } from './example-valid.js';
24
24
  import { firesDeclarations } from './fires-declarations.js';
25
25
  import { governedSymbolResidue } from './governed-symbol-residue.js';
26
+ import { governedVocabularyPermutationWatch } from './governed-vocabulary-permutation-watch.js';
26
27
  import { implementationReturnsResult } from './implementation-returns-result.js';
27
28
  import { incompleteAccessorForStandardOp } from './incomplete-accessor-for-standard-op.js';
28
29
  import { incompleteCrud } from './incomplete-crud.js';
29
30
  import { intentPropagation } from './intent-propagation.js';
30
31
  import { layerFieldNameDrift } from './layer-field-name-drift.js';
31
- import { libraryProjectionCoherence } from './library-projection-coherence.js';
32
+ import { libraryRenderCoherence } from './library-render-coherence.js';
32
33
  import { missingReconcile } from './missing-reconcile.js';
33
34
  import { missingVisibility } from './missing-visibility.js';
34
35
  import { noDevPermitInSource } from './no-dev-permit-in-source.js';
@@ -45,7 +46,7 @@ import { noThrowInImplementation } from './no-throw-in-implementation.js';
45
46
  import { noTopLevelSurface } from './no-top-level-surface.js';
46
47
  import { onReferencesExist } from './on-references-exist.js';
47
48
  import { orphanedSignal } from './orphaned-signal.js';
48
- import { ownerProjectionParity } from './owner-projection-parity.js';
49
+ import { ownerRenderParity } from './owner-render-parity.js';
49
50
  import { permitGovernance } from './permit-governance.js';
50
51
  import { preferSchemaInference } from './prefer-schema-inference.js';
51
52
  import { publicExportExampleCoverage } from './public-export-example-coverage.js';
@@ -109,13 +110,14 @@ export const registeredRuleNames: readonly string[] = [
109
110
  exampleValid.name,
110
111
  firesDeclarations.name,
111
112
  governedSymbolResidue.name,
113
+ governedVocabularyPermutationWatch.name,
112
114
  forkWithoutPreservedImplementation.name,
113
115
  implementationReturnsResult.name,
114
116
  incompleteAccessorForStandardOp.name,
115
117
  incompleteCrud.name,
116
118
  intentPropagation.name,
117
119
  layerFieldNameDrift.name,
118
- libraryProjectionCoherence.name,
120
+ libraryRenderCoherence.name,
119
121
  markerSchemaUnsupported.name,
120
122
  missingReconcile.name,
121
123
  missingVisibility.name,
@@ -133,7 +135,7 @@ export const registeredRuleNames: readonly string[] = [
133
135
  noTopLevelSurface.name,
134
136
  onReferencesExist.name,
135
137
  orphanedSignal.name,
136
- ownerProjectionParity.name,
138
+ ownerRenderParity.name,
137
139
  pendingForce.name,
138
140
  permitGovernance.name,
139
141
  preferSchemaInference.name,