@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.
@@ -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',
@@ -386,22 +386,25 @@ export interface ProjectContext {
386
386
  export interface GovernedVocabularyHistoryEvidence {
387
387
  readonly caseSensitive: boolean;
388
388
  readonly id: string;
389
- readonly latestFormObservations: readonly GovernedVocabularyHistoryFormObservation[];
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 observation preserved in committed Regrade history. */
397
- export interface GovernedVocabularyHistoryFormObservation {
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 line: number;
401
- readonly path: string;
402
- readonly reason: string;
403
- readonly scopeTier?: 'in-scope' | 'policy-classified' | undefined;
404
- readonly verdict: 'applied' | 'deferred' | 'modified' | 'skipped';
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 {
@@ -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 governedVocabularyHistoryObservationSchema = z.object({
122
+ const governedVocabularyHistoryFormJudgmentSchema = z.object({
123
123
  disposition: z.string(),
124
124
  form: z.string(),
125
- line: z.number().int().positive(),
126
- path: z.string(),
127
- reason: z.string(),
128
- scopeTier: z.enum(['in-scope', 'policy-classified']).optional(),
129
- verdict: z.enum(['applied', 'deferred', 'modified', 'skipped']),
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
- latestFormObservations: z
136
- .array(governedVocabularyHistoryObservationSchema)
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 { listWorkspacePackages, matchesAnyPathGlob } from '@ontrails/core';
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 listWorkspacePackages<PackageManifest>(
177
- normalizedRoot
178
- )) {
243
+ for (const workspacePackage of candidates) {
179
244
  const workspace = publicWorkspaceFromManifest(
180
245
  workspacePackage.packageJsonPath,
181
246
  workspacePackage.manifest