@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.
- package/CHANGELOG.md +125 -0
- package/README.md +5 -5
- package/package.json +10 -12
- package/src/cli.ts +49 -44
- package/src/command.ts +2 -2
- package/src/drift.ts +4 -3
- package/src/index.ts +3 -2
- package/src/project-context.ts +3 -3
- package/src/resolve.ts +2 -2
- package/src/rules/captured-kernel.ts +375 -0
- package/src/rules/circular-refs.ts +12 -16
- package/src/rules/cli-command-route-coherence.ts +1 -1
- package/src/rules/composes-declarations.ts +15 -13
- package/src/rules/context-no-surface-types.ts +4 -3
- package/src/rules/dead-internal-trail.ts +3 -3
- package/src/rules/dead-public-trail.ts +3 -3
- package/src/rules/draft-file-marking.ts +4 -9
- package/src/rules/draft-visible-debt.ts +3 -8
- package/src/rules/duplicate-public-contract.ts +50 -5
- package/src/rules/{contour-exists.ts → entity-exists.ts} +67 -64
- package/src/rules/entity-ids.ts +15 -0
- package/src/rules/error-mapping-completeness.ts +2 -2
- package/src/rules/example-valid.ts +84 -84
- package/src/rules/fires-declarations.ts +27 -22
- package/src/rules/governed-symbol-residue.ts +339 -32
- package/src/rules/implementation-returns-result.ts +8 -8
- package/src/rules/incomplete-accessor-for-standard-op.ts +15 -15
- package/src/rules/incomplete-crud.ts +42 -40
- package/src/rules/index.ts +9 -6
- package/src/rules/intent-propagation.ts +3 -5
- package/src/rules/layer-field-name-drift.ts +2 -2
- package/src/rules/library-projection-coherence.ts +2 -2
- package/src/rules/metadata.ts +37 -15
- package/src/rules/missing-reconcile.ts +3 -4
- package/src/rules/missing-visibility.ts +2 -1
- package/src/rules/no-destructured-compose.ts +14 -12
- package/src/rules/no-direct-implementation-call.ts +12 -7
- package/src/rules/no-legacy-cli-alias-export.ts +2 -2
- package/src/rules/no-native-error-result.ts +2 -2
- package/src/rules/no-redundant-result-error-wrap.ts +10 -10
- package/src/rules/no-sync-result-assumption.ts +84 -78
- package/src/rules/no-throw-in-detour-recover.ts +2 -2
- package/src/rules/no-throw-in-implementation.ts +13 -7
- package/src/rules/no-top-level-surface.ts +2 -2
- package/src/rules/on-references-exist.ts +3 -3
- package/src/rules/orphaned-signal.ts +4 -5
- package/src/rules/owner-projection-parity.ts +2 -2
- package/src/rules/public-export-example-coverage.ts +10 -4
- package/src/rules/public-internal-deep-imports.ts +2 -3
- package/src/rules/read-intent-fires.ts +4 -3
- package/src/rules/reference-exists.ts +20 -21
- package/src/rules/registry-names.ts +6 -4
- package/src/rules/resolved-import-boundary.ts +1 -1
- package/src/rules/resource-declarations.ts +21 -17
- package/src/rules/resource-exists.ts +4 -2
- package/src/rules/resource-id-grammar.ts +2 -2
- package/src/rules/resource-mock-coverage.ts +2 -2
- package/src/rules/retired-vocabulary.ts +242 -5
- package/src/rules/source/composition.ts +165 -0
- package/src/rules/source/drafts.ts +164 -0
- package/src/rules/source/entities.ts +618 -0
- package/src/rules/source/pragmas.ts +45 -0
- package/src/rules/source/resources.ts +64 -0
- package/src/rules/source/signals.ts +397 -0
- package/src/rules/source/stores.ts +310 -0
- package/src/rules/static-resource-accessor-preference.ts +15 -11
- package/src/rules/surface-overlay-coherence.ts +1 -1
- package/src/rules/surface-trailhead-coherence.ts +2 -2
- package/src/rules/trail-fork-coaching.ts +27 -18
- package/src/rules/trail-versioning-source.ts +16 -12
- package/src/rules/trail-versioning-topo.ts +2 -2
- package/src/rules/trailhead-override-divergence.ts +2 -2
- package/src/rules/types.ts +7 -7
- package/src/rules/unmaterialized-activation-source.ts +1 -0
- package/src/rules/unreachable-detour-shadowing.ts +2 -2
- package/src/rules/valid-describe-refs.ts +2 -2
- package/src/rules/warden-export-symmetry.ts +3 -3
- package/src/rules/warden-rules-use-ast.ts +12 -17
- package/src/trails/activation-orphan.trail.ts +5 -5
- package/src/trails/captured-kernel.trail.ts +108 -0
- package/src/trails/circular-refs.trail.ts +6 -6
- package/src/trails/cli-command-route-coherence.trail.ts +2 -2
- package/src/trails/composes-declarations.trail.ts +1 -1
- package/src/trails/context-no-surface-types.trail.ts +1 -1
- package/src/trails/dead-internal-trail.trail.ts +2 -2
- package/src/trails/dead-public-trail.trail.ts +1 -1
- package/src/trails/deprecation-without-guidance.trail.ts +1 -1
- package/src/trails/duplicate-public-contract.trail.ts +3 -3
- package/src/trails/entity-exists.trail.ts +21 -0
- package/src/trails/example-valid.trail.ts +3 -3
- package/src/trails/fires-declarations.trail.ts +1 -1
- package/src/trails/{fork-without-preserved-blaze.trail.ts → fork-without-preserved-implementation.trail.ts} +4 -4
- package/src/trails/implementation-returns-result.trail.ts +1 -1
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +4 -4
- package/src/trails/index.ts +3 -2
- package/src/trails/intent-propagation.trail.ts +2 -2
- package/src/trails/library-projection-coherence.trail.ts +2 -2
- package/src/trails/marker-schema-unsupported.trail.ts +1 -1
- package/src/trails/missing-visibility.trail.ts +1 -1
- package/src/trails/no-destructured-compose.trail.ts +5 -5
- package/src/trails/no-direct-implementation-call.trail.ts +1 -1
- package/src/trails/no-redundant-result-error-wrap.trail.ts +2 -2
- package/src/trails/no-sync-result-assumption.trail.ts +2 -2
- package/src/trails/no-throw-in-detour-recover.trail.ts +1 -1
- package/src/trails/no-throw-in-implementation.trail.ts +1 -1
- package/src/trails/on-references-exist.trail.ts +1 -1
- package/src/trails/pending-force.trail.ts +1 -1
- package/src/trails/permit-governance.trail.ts +2 -2
- package/src/trails/prefer-schema-inference.trail.ts +1 -1
- package/src/trails/public-output-schema.trail.ts +2 -2
- package/src/trails/public-union-output-discriminants.trail.ts +1 -1
- package/src/trails/read-intent-fires.trail.ts +1 -1
- package/src/trails/reference-exists.trail.ts +5 -5
- package/src/trails/resource-declarations.trail.ts +1 -1
- package/src/trails/resource-exists.trail.ts +1 -1
- package/src/trails/run.ts +4 -6
- package/src/trails/scheduled-destroy-intent.trail.ts +2 -2
- package/src/trails/schema.ts +9 -9
- package/src/trails/signal-graph-coaching.trail.ts +2 -2
- package/src/trails/static-resource-accessor-preference.trail.ts +1 -1
- package/src/trails/surface-overlay-coherence.trail.ts +1 -1
- package/src/trails/trail-fork-coaching.trail.ts +1 -1
- package/src/trails/unmaterialized-activation-source.trail.ts +5 -5
- package/src/trails/valid-detour-contract.trail.ts +1 -1
- package/src/trails/version-gap.trail.ts +1 -1
- package/src/trails/version-pinned-compose.trail.ts +1 -1
- package/src/trails/version-without-examples.trail.ts +1 -1
- package/src/trails/warden-rules-use-ast.trail.ts +2 -2
- package/src/trails/webhook-route-collision.trail.ts +2 -2
- package/src/trails/wrap-rule.ts +17 -17
- package/src/ast.ts +0 -137
- package/src/rules/ast.ts +0 -4143
- package/src/rules/contour-ids.ts +0 -15
- 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 '
|
|
17
|
-
import type { AstNode
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
} from '
|
|
7
|
-
import
|
|
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
|
-
|
|
16
|
+
sourceEntity: string,
|
|
18
17
|
field: string,
|
|
19
|
-
|
|
18
|
+
targetEntity: string,
|
|
20
19
|
filePath: string,
|
|
21
20
|
line: number
|
|
22
21
|
): WardenDiagnostic => ({
|
|
23
22
|
filePath,
|
|
24
23
|
line,
|
|
25
|
-
message: `
|
|
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
|
|
29
|
+
const checkEntityReferences = (
|
|
31
30
|
ast: AstNode,
|
|
32
31
|
sourceCode: string,
|
|
33
32
|
filePath: string,
|
|
34
|
-
|
|
33
|
+
knownEntityIds: ReadonlySet<string>
|
|
35
34
|
): readonly WardenDiagnostic[] => {
|
|
36
35
|
if (isTestFile(filePath)) {
|
|
37
36
|
return [];
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
return
|
|
39
|
+
return collectEntityReferenceSites(ast, knownEntityIds).flatMap(
|
|
41
40
|
(reference) => {
|
|
42
|
-
if (
|
|
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
|
|
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
|
|
68
|
+
return checkEntityReferences(
|
|
70
69
|
ast,
|
|
71
70
|
sourceCode,
|
|
72
71
|
filePath,
|
|
73
|
-
|
|
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
|
|
87
|
-
return
|
|
85
|
+
const localEntityIds = collectEntityDefinitionIds(ast);
|
|
86
|
+
return checkEntityReferences(
|
|
88
87
|
ast,
|
|
89
88
|
sourceCode,
|
|
90
89
|
filePath,
|
|
91
|
-
|
|
90
|
+
mergeKnownEntityIds(localEntityIds, context.knownEntityIds)
|
|
92
91
|
);
|
|
93
92
|
},
|
|
94
93
|
description:
|
|
95
|
-
'Ensure every
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
+
forkWithoutPreservedImplementation.name,
|
|
111
113
|
implementationReturnsResult.name,
|
|
112
114
|
incompleteAccessorForStandardOp.name,
|
|
113
115
|
incompleteCrud.name,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Validates that resource access matches the declared `resources` array.
|
|
3
3
|
*
|
|
4
|
-
* Statically analyzes trail `
|
|
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 '
|
|
31
|
-
import type { AstNode } from '
|
|
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
|
|
131
|
-
const extractContextParamNode = (
|
|
132
|
-
|
|
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
|
|
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 = (
|
|
151
|
-
|
|
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
|
|
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
|
|
202
|
-
* No seeded defaults: if the
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
16
|
-
import type { AstNode } from '
|
|
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 '
|
|
9
|
-
import type { AstNode } from '
|
|
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 '
|
|
26
|
-
import type { AstNode } from '
|
|
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: '
|
|
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: '
|
|
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
|
-
{
|
|
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: {
|