@ontrails/warden 1.0.0-beta.45 → 1.0.0-beta.47
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 +31 -0
- package/package.json +10 -10
- package/src/cli.ts +97 -62
- package/src/index.ts +1 -1
- package/src/regrade-history.ts +54 -465
- package/src/regrade-receipt.ts +484 -0
- package/src/rules/governed-symbol-residue.ts +4 -0
- package/src/rules/governed-vocabulary-permutation-watch.ts +20 -19
- package/src/rules/index.ts +1 -1
- package/src/rules/public-export-example-coverage.ts +25 -37
- package/src/rules/retired-vocabulary.ts +40 -0
- package/src/rules/types.ts +11 -8
- package/src/trails/schema.ts +11 -8
- package/src/workspaces.ts +71 -6
|
@@ -62,6 +62,7 @@ export const governedVocabularySymbolRenameSchema = z.object({
|
|
|
62
62
|
from: z.string().min(1),
|
|
63
63
|
match: z.enum(governedVocabularySymbolRenameMatchModes).default('exact'),
|
|
64
64
|
reviewDeclarationTypes: z.array(z.string().min(1)).default([]),
|
|
65
|
+
safety: z.enum(['safe', 'review']).optional(),
|
|
65
66
|
to: z.string().min(1),
|
|
66
67
|
});
|
|
67
68
|
|
|
@@ -743,6 +744,16 @@ export const governedVocabularyTransitions =
|
|
|
743
744
|
reason:
|
|
744
745
|
'Preserve transition regression fixtures and the intentional negative resolver assertion after the public route is retired.',
|
|
745
746
|
},
|
|
747
|
+
{
|
|
748
|
+
paths: [
|
|
749
|
+
'apps/trails/src/__tests__/regrade.test.ts',
|
|
750
|
+
'packages/regrade/src/downstream/__tests__/ast-rewrite.test.ts',
|
|
751
|
+
'packages/regrade/src/downstream/__tests__/vocabulary.test.ts',
|
|
752
|
+
],
|
|
753
|
+
pattern: '^@ontrails/warden/ast(?:\\.js|\\.utils|/utils)$',
|
|
754
|
+
reason:
|
|
755
|
+
'Preserve the adjacent package-route variants used by the authored Warden AST transition regression fixtures.',
|
|
756
|
+
},
|
|
746
757
|
],
|
|
747
758
|
reviewForms: [],
|
|
748
759
|
safeRewriteForms: {
|
|
@@ -829,6 +840,9 @@ export const governedVocabularyTransitions =
|
|
|
829
840
|
'cliProjection',
|
|
830
841
|
'cliProjectionSchema',
|
|
831
842
|
'collectLibraryProjection',
|
|
843
|
+
'collectionExtensionProjectionForFileRenames',
|
|
844
|
+
'extensionProjection',
|
|
845
|
+
'filterProjectedTargetExtensions',
|
|
832
846
|
'projectActivationEdge',
|
|
833
847
|
'projectActivationSource',
|
|
834
848
|
'projectActivationSourceDeclaration',
|
|
@@ -860,6 +874,11 @@ export const governedVocabularyTransitions =
|
|
|
860
874
|
'projectTrailVersions',
|
|
861
875
|
'projectVersionDetours',
|
|
862
876
|
'projectVersionRuntimeRefs',
|
|
877
|
+
'projectVocabularyText',
|
|
878
|
+
'projected',
|
|
879
|
+
'projectedEvidence',
|
|
880
|
+
'projectedFileInScopeCount',
|
|
881
|
+
'projectedTargetPaths',
|
|
863
882
|
'SchemaProjector',
|
|
864
883
|
'deriveShippedSurfaceProjectionInventory',
|
|
865
884
|
'deriveTrailCliCommandProjection',
|
|
@@ -1190,6 +1209,15 @@ export const governedVocabularyTransitions =
|
|
|
1190
1209
|
from: 'collectLibraryProjection',
|
|
1191
1210
|
to: 'deriveTopoGraphLibrary',
|
|
1192
1211
|
},
|
|
1212
|
+
{
|
|
1213
|
+
from: 'collectionExtensionProjectionForFileRenames',
|
|
1214
|
+
to: 'deriveCollectionExtensionsForFileRenames',
|
|
1215
|
+
},
|
|
1216
|
+
{ from: 'extensionProjection', to: 'derivedExtensions' },
|
|
1217
|
+
{
|
|
1218
|
+
from: 'filterProjectedTargetExtensions',
|
|
1219
|
+
to: 'filterDerivedTargetExtensions',
|
|
1220
|
+
},
|
|
1193
1221
|
{ from: 'projectActivationEdge', to: 'deriveActivationEdge' },
|
|
1194
1222
|
{ from: 'projectActivationSource', to: 'deriveActivationSource' },
|
|
1195
1223
|
{
|
|
@@ -1233,6 +1261,18 @@ export const governedVocabularyTransitions =
|
|
|
1233
1261
|
from: 'projectVersionRuntimeRefs',
|
|
1234
1262
|
to: 'deriveVersionRuntimeRefs',
|
|
1235
1263
|
},
|
|
1264
|
+
{ from: 'projectVocabularyText', to: 'deriveVocabularyText' },
|
|
1265
|
+
{
|
|
1266
|
+
from: 'projected',
|
|
1267
|
+
safety: 'review',
|
|
1268
|
+
to: 'derived',
|
|
1269
|
+
},
|
|
1270
|
+
{ from: 'projectedEvidence', to: 'derivedEvidence' },
|
|
1271
|
+
{
|
|
1272
|
+
from: 'projectedFileInScopeCount',
|
|
1273
|
+
to: 'derivedFileInScopeCount',
|
|
1274
|
+
},
|
|
1275
|
+
{ from: 'projectedTargetPaths', to: 'derivedTargetPaths' },
|
|
1236
1276
|
{ from: 'SchemaProjector', to: 'SchemaDeriver' },
|
|
1237
1277
|
{
|
|
1238
1278
|
from: 'deriveShippedSurfaceProjectionInventory',
|
package/src/rules/types.ts
CHANGED
|
@@ -386,22 +386,25 @@ export interface ProjectContext {
|
|
|
386
386
|
export interface GovernedVocabularyHistoryEvidence {
|
|
387
387
|
readonly caseSensitive: boolean;
|
|
388
388
|
readonly id: string;
|
|
389
|
-
readonly
|
|
389
|
+
readonly latestFormJudgments: readonly GovernedVocabularyHistoryFormJudgment[];
|
|
390
390
|
readonly path: string;
|
|
391
391
|
readonly provenance?: GovernedVocabularyHistoryProvenance;
|
|
392
392
|
readonly runCount: number;
|
|
393
393
|
readonly transitionId: string;
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
/** One vocabulary-form
|
|
397
|
-
export interface
|
|
396
|
+
/** One compact vocabulary-form judgment preserved in committed Regrade history. */
|
|
397
|
+
export interface GovernedVocabularyHistoryFormJudgment {
|
|
398
398
|
readonly disposition: string;
|
|
399
399
|
readonly form: string;
|
|
400
|
-
readonly
|
|
401
|
-
readonly
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
400
|
+
readonly reason?: string | undefined;
|
|
401
|
+
readonly representative?:
|
|
402
|
+
| {
|
|
403
|
+
readonly line: number;
|
|
404
|
+
readonly path: string;
|
|
405
|
+
}
|
|
406
|
+
| undefined;
|
|
407
|
+
readonly target?: string | undefined;
|
|
405
408
|
}
|
|
406
409
|
|
|
407
410
|
export interface GovernedVocabularyHistoryIssue {
|
package/src/trails/schema.ts
CHANGED
|
@@ -119,21 +119,24 @@ export const authoredMcpSurfaceBindingSetSchema = z.object({
|
|
|
119
119
|
.describe('Trail ids registered in the owning app topo'),
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
const
|
|
122
|
+
const governedVocabularyHistoryFormJudgmentSchema = z.object({
|
|
123
123
|
disposition: z.string(),
|
|
124
124
|
form: z.string(),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
125
|
+
reason: z.string().optional(),
|
|
126
|
+
representative: z
|
|
127
|
+
.object({
|
|
128
|
+
line: z.number().int().positive(),
|
|
129
|
+
path: z.string(),
|
|
130
|
+
})
|
|
131
|
+
.optional(),
|
|
132
|
+
target: z.string().optional(),
|
|
130
133
|
});
|
|
131
134
|
|
|
132
135
|
const governedVocabularyHistoryEvidenceSchema = z.object({
|
|
133
136
|
caseSensitive: z.boolean(),
|
|
134
137
|
id: z.string(),
|
|
135
|
-
|
|
136
|
-
.array(
|
|
138
|
+
latestFormJudgments: z
|
|
139
|
+
.array(governedVocabularyHistoryFormJudgmentSchema)
|
|
137
140
|
.readonly(),
|
|
138
141
|
path: z.string(),
|
|
139
142
|
runCount: z.number().int().nonnegative(),
|
package/src/workspaces.ts
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
* Workspace-manifest discovery for Warden project-aware rules.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { realpathSync } from 'node:fs';
|
|
6
|
-
import { dirname, join, relative, resolve } from 'node:path';
|
|
5
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
6
|
+
import { dirname, join, posix, relative, resolve } from 'node:path';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
listWorkspacePackages,
|
|
10
|
+
listWorkspacePatterns,
|
|
11
|
+
matchesAnyPathGlob,
|
|
12
|
+
} from '@ontrails/core';
|
|
9
13
|
|
|
10
14
|
export interface WardenPublicWorkspace {
|
|
11
15
|
readonly name: string;
|
|
@@ -18,6 +22,8 @@ export interface WardenPublicWorkspace {
|
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export interface WardenWorkspaceCollectionOptions {
|
|
25
|
+
/** Root-relative package manifests observed by the owning source collection. */
|
|
26
|
+
readonly collectedPackageJsonPaths?: ReadonlySet<string> | undefined;
|
|
21
27
|
readonly exclude?: readonly string[];
|
|
22
28
|
}
|
|
23
29
|
|
|
@@ -27,6 +33,7 @@ interface PackageManifest {
|
|
|
27
33
|
readonly files?: unknown;
|
|
28
34
|
readonly name?: unknown;
|
|
29
35
|
readonly private?: unknown;
|
|
36
|
+
readonly workspaces?: unknown;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
const normalizePath = (path: string): string => path.replaceAll('\\', '/');
|
|
@@ -42,6 +49,60 @@ const normalizeRealPath = (path: string): string => {
|
|
|
42
49
|
const rootRelativePath = (rootDir: string, path: string): string =>
|
|
43
50
|
normalizePath(relative(rootDir, path));
|
|
44
51
|
|
|
52
|
+
const readPackageManifest = (path: string): PackageManifest | undefined => {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(readFileSync(path, 'utf8')) as PackageManifest;
|
|
55
|
+
} catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const matchesWorkspacePattern = (
|
|
61
|
+
workspacePath: string,
|
|
62
|
+
pattern: string
|
|
63
|
+
): boolean => {
|
|
64
|
+
const normalizedPattern = normalizePath(pattern).replace(/^\.\//, '');
|
|
65
|
+
return normalizedPattern.endsWith('/*')
|
|
66
|
+
? posix.dirname(workspacePath) === normalizedPattern.slice(0, -2)
|
|
67
|
+
: workspacePath === normalizedPattern;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
interface WorkspaceManifestCandidate {
|
|
71
|
+
readonly manifest: PackageManifest;
|
|
72
|
+
readonly packageJsonPath: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const collectedWorkspaceManifests = (
|
|
76
|
+
rootDir: string,
|
|
77
|
+
packageJsonPaths: ReadonlySet<string>
|
|
78
|
+
): readonly WorkspaceManifestCandidate[] => {
|
|
79
|
+
if (!packageJsonPaths.has('package.json')) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
const rootManifest = readPackageManifest(join(rootDir, 'package.json'));
|
|
83
|
+
const patterns = listWorkspacePatterns(rootManifest);
|
|
84
|
+
|
|
85
|
+
return [...packageJsonPaths]
|
|
86
|
+
.filter((path) => path !== 'package.json' && path.endsWith('/package.json'))
|
|
87
|
+
.toSorted()
|
|
88
|
+
.flatMap((path): readonly WorkspaceManifestCandidate[] => {
|
|
89
|
+
const normalizedPath = normalizePath(path).replace(/^\.\//, '');
|
|
90
|
+
const workspacePath = posix.dirname(normalizedPath);
|
|
91
|
+
if (
|
|
92
|
+
posix.isAbsolute(normalizedPath) ||
|
|
93
|
+
normalizedPath.startsWith('../') ||
|
|
94
|
+
!patterns.some((pattern) =>
|
|
95
|
+
matchesWorkspacePattern(workspacePath, pattern)
|
|
96
|
+
)
|
|
97
|
+
) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const packageJsonPath = resolve(rootDir, normalizedPath);
|
|
101
|
+
const manifest = readPackageManifest(packageJsonPath);
|
|
102
|
+
return manifest ? [{ manifest, packageJsonPath }] : [];
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
45
106
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
46
107
|
Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
47
108
|
|
|
@@ -172,10 +233,14 @@ export const collectPublicWorkspaces = (
|
|
|
172
233
|
): ReadonlyMap<string, WardenPublicWorkspace> => {
|
|
173
234
|
const normalizedRoot = normalizeRealPath(rootDir);
|
|
174
235
|
const workspaces = new Map<string, WardenPublicWorkspace>();
|
|
236
|
+
const candidates = options.collectedPackageJsonPaths
|
|
237
|
+
? collectedWorkspaceManifests(
|
|
238
|
+
normalizedRoot,
|
|
239
|
+
options.collectedPackageJsonPaths
|
|
240
|
+
)
|
|
241
|
+
: listWorkspacePackages<PackageManifest>(normalizedRoot);
|
|
175
242
|
|
|
176
|
-
for (const workspacePackage of
|
|
177
|
-
normalizedRoot
|
|
178
|
-
)) {
|
|
243
|
+
for (const workspacePackage of candidates) {
|
|
179
244
|
const workspace = publicWorkspaceFromManifest(
|
|
180
245
|
workspacePackage.packageJsonPath,
|
|
181
246
|
workspacePackage.manifest
|