@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# @ontrails/warden
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.47
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`ea2f296`](https://github.com/outfitter-dev/trails/commit/ea2f296e8119cf79bd03aeca3f0aa804f2fbcdd6): Classify adjacent package-route variants in the authored Warden AST regression
|
|
8
|
+
fixtures as explicit preserves so the completed transition remains auditable.
|
|
9
|
+
- [`90d394c`](https://github.com/outfitter-dev/trails/commit/90d394c005fdf6b898ba7052d0b56755af0f4954): Derive nested worktree, repository, and submodule collection boundaries in the
|
|
10
|
+
shared Source walker. Regrade and Warden now observe one directly targeted
|
|
11
|
+
working tree per run, and Regrade audit summaries expose boundary skip counts.
|
|
12
|
+
|
|
13
|
+
## 1.0.0-beta.46
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- [`fb0ba0a`](https://github.com/outfitter-dev/trails/commit/fb0ba0ab706bbdce470123e9a6fb2ef9f1822806): Convert the eight committed governed Regrade histories to canonical v3 receipts and remove the temporary schema-v2 compatibility path after migration.
|
|
18
|
+
- [`9a8b6e4`](https://github.com/outfitter-dev/trails/commit/9a8b6e4af394c76c11e6d0007e0f5f94d0be2cb3): Persist Regrade lifecycle runs as canonical v3 receipts with exact Git blob evidence and authored field provenance, and validate their compact classified-form projection independently in Warden.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [`54d259b`](https://github.com/outfitter-dev/trails/commit/54d259be81fb6c41d85be48a6cb2100c746a7126): Expose parser-native comment spans from `parseWithDiagnostics` so source-aware
|
|
23
|
+
tooling can distinguish exact JavaScript and TypeScript comment trivia without
|
|
24
|
+
reimplementing a lexer.
|
|
25
|
+
|
|
26
|
+
Use the shared spans in Warden's public-example rule while keeping leading
|
|
27
|
+
comment ownership fail-closed across JavaScript line terminators.
|
|
28
|
+
|
|
29
|
+
- [`768cc79`](https://github.com/outfitter-dev/trails/commit/768cc79ca10947b8808b376e281e1a81131b4acc): Close missed projection vocabulary residue in Regrade internals and public
|
|
30
|
+
error-rendering guidance, and keep lifecycle-ambiguous governed identifiers in
|
|
31
|
+
the Warden review inventory instead of assigning them an unsafe automatic
|
|
32
|
+
target.
|
|
33
|
+
|
|
3
34
|
## 1.0.0-beta.45
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/warden",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.47",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/outfitter-dev/trails.git",
|
|
@@ -32,20 +32,20 @@
|
|
|
32
32
|
"clean": "rm -rf dist *.tsbuildinfo"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ontrails/adapter-kit": "^1.0.0-beta.
|
|
36
|
-
"@ontrails/cli": "^1.0.0-beta.
|
|
37
|
-
"@ontrails/config": "^1.0.0-beta.
|
|
38
|
-
"@ontrails/permits": "^1.0.0-beta.
|
|
39
|
-
"@ontrails/source": "^1.0.0-beta.
|
|
40
|
-
"@ontrails/store": "^1.0.0-beta.
|
|
35
|
+
"@ontrails/adapter-kit": "^1.0.0-beta.47",
|
|
36
|
+
"@ontrails/cli": "^1.0.0-beta.47",
|
|
37
|
+
"@ontrails/config": "^1.0.0-beta.47",
|
|
38
|
+
"@ontrails/permits": "^1.0.0-beta.47",
|
|
39
|
+
"@ontrails/source": "^1.0.0-beta.47",
|
|
40
|
+
"@ontrails/store": "^1.0.0-beta.47",
|
|
41
41
|
"oxc-resolver": "11.19.1",
|
|
42
42
|
"zod": "^4.3.5"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
45
|
+
"@ontrails/testing": "^1.0.0-beta.47"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
49
|
-
"@ontrails/topography": "^1.0.0-beta.
|
|
48
|
+
"@ontrails/core": "^1.0.0-beta.47",
|
|
49
|
+
"@ontrails/topography": "^1.0.0-beta.47"
|
|
50
50
|
}
|
|
51
51
|
}
|
package/src/cli.ts
CHANGED
|
@@ -63,7 +63,12 @@ import {
|
|
|
63
63
|
collectCrudTableIds as collectCrudTableIdsFromAst,
|
|
64
64
|
collectReconcileTableIds as collectReconcileTableIdsFromAst,
|
|
65
65
|
} from './rules/source/stores.js';
|
|
66
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
collectSourceTree,
|
|
68
|
+
findTrailDefinitions,
|
|
69
|
+
parse,
|
|
70
|
+
} from '@ontrails/source';
|
|
71
|
+
import type { SourceTreeCollection } from '@ontrails/source';
|
|
67
72
|
import { collectFileCrudCoverage } from './rules/incomplete-crud.js';
|
|
68
73
|
import { wardenRules, wardenTopoRules } from './rules/index.js';
|
|
69
74
|
import { getWardenRuleMetadata } from './rules/metadata.js';
|
|
@@ -238,29 +243,28 @@ export interface WardenReport {
|
|
|
238
243
|
* surfaces that should not contribute most committed-source diagnostics.
|
|
239
244
|
*/
|
|
240
245
|
const collectFilesMatching = (
|
|
241
|
-
|
|
246
|
+
collection: NonNullable<ReturnType<typeof collectSourceTree>>,
|
|
242
247
|
pattern: string,
|
|
243
248
|
dot = false
|
|
244
249
|
): readonly string[] => {
|
|
245
250
|
const glob = new Bun.Glob(pattern);
|
|
246
|
-
let matches: IterableIterator<string>;
|
|
247
|
-
try {
|
|
248
|
-
matches = glob.scanSync({ cwd: dir, dot, onlyFiles: true });
|
|
249
|
-
} catch {
|
|
250
|
-
return [];
|
|
251
|
-
}
|
|
252
|
-
|
|
253
251
|
const files: string[] = [];
|
|
254
|
-
for (const
|
|
255
|
-
if (
|
|
256
|
-
|
|
252
|
+
for (const file of collection.files) {
|
|
253
|
+
if (
|
|
254
|
+
(dot ||
|
|
255
|
+
!file.path.split('/').some((segment) => segment.startsWith('.'))) &&
|
|
256
|
+
glob.match(file.path) &&
|
|
257
|
+
isWardenSourceScanTarget(file.path)
|
|
258
|
+
) {
|
|
259
|
+
files.push(file.absolutePath);
|
|
257
260
|
}
|
|
258
261
|
}
|
|
259
262
|
return files;
|
|
260
263
|
};
|
|
261
264
|
|
|
262
|
-
const collectTsFiles = (
|
|
263
|
-
|
|
265
|
+
const collectTsFiles = (
|
|
266
|
+
collection: NonNullable<ReturnType<typeof collectSourceTree>>
|
|
267
|
+
): readonly string[] => collectFilesMatching(collection, '**/*.ts');
|
|
264
268
|
|
|
265
269
|
const draftModeIncludesFile = (
|
|
266
270
|
filePath: string,
|
|
@@ -309,41 +313,42 @@ const filterSourceFilesByScope = (
|
|
|
309
313
|
|
|
310
314
|
const EMPTY_WARDEN_SCOPE: WardenScope = { exclude: [] };
|
|
311
315
|
|
|
312
|
-
const collectDevPermitTestFiles = (
|
|
316
|
+
const collectDevPermitTestFiles = (
|
|
317
|
+
collection: NonNullable<ReturnType<typeof collectSourceTree>>
|
|
318
|
+
): readonly string[] => {
|
|
313
319
|
const glob = new Bun.Glob('**/*.ts');
|
|
314
|
-
let matches: IterableIterator<string>;
|
|
315
|
-
try {
|
|
316
|
-
matches = glob.scanSync({ cwd: dir, onlyFiles: true });
|
|
317
|
-
} catch {
|
|
318
|
-
return [];
|
|
319
|
-
}
|
|
320
|
-
|
|
321
320
|
const files: string[] = [];
|
|
322
|
-
for (const
|
|
323
|
-
if (
|
|
324
|
-
|
|
321
|
+
for (const file of collection.files) {
|
|
322
|
+
if (
|
|
323
|
+
!file.path.split('/').some((segment) => segment.startsWith('.')) &&
|
|
324
|
+
glob.match(file.path) &&
|
|
325
|
+
isWardenDevPermitTestScanTarget(file.path)
|
|
326
|
+
) {
|
|
327
|
+
files.push(file.absolutePath);
|
|
325
328
|
}
|
|
326
329
|
}
|
|
327
330
|
return files;
|
|
328
331
|
};
|
|
329
332
|
|
|
330
|
-
const collectTextScanFiles = (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
...collectFilesMatching(
|
|
334
|
-
...collectFilesMatching(
|
|
335
|
-
...collectFilesMatching(
|
|
336
|
-
...collectFilesMatching(
|
|
337
|
-
...collectFilesMatching(
|
|
338
|
-
...collectFilesMatching(
|
|
339
|
-
...collectFilesMatching(
|
|
340
|
-
...collectFilesMatching(
|
|
341
|
-
...collectFilesMatching(
|
|
342
|
-
...collectFilesMatching(
|
|
343
|
-
...collectFilesMatching(
|
|
344
|
-
...collectFilesMatching(
|
|
345
|
-
...collectFilesMatching(
|
|
346
|
-
...collectFilesMatching(
|
|
333
|
+
const collectTextScanFiles = (
|
|
334
|
+
collection: NonNullable<ReturnType<typeof collectSourceTree>>
|
|
335
|
+
): readonly string[] => [
|
|
336
|
+
...collectFilesMatching(collection, '**/*.sh', true),
|
|
337
|
+
...collectFilesMatching(collection, '**/*.bash', true),
|
|
338
|
+
...collectFilesMatching(collection, '**/*.zsh', true),
|
|
339
|
+
...collectFilesMatching(collection, '**/*.yml', true),
|
|
340
|
+
...collectFilesMatching(collection, '**/*.yaml', true),
|
|
341
|
+
...collectFilesMatching(collection, 'plugin/**/*.json', true),
|
|
342
|
+
...collectFilesMatching(collection, 'plugin/**/*.jsonc', true),
|
|
343
|
+
...collectFilesMatching(collection, 'plugin/**/*.toml', true),
|
|
344
|
+
...collectFilesMatching(collection, 'plugin/**/*.cjs', true),
|
|
345
|
+
...collectFilesMatching(collection, 'plugin/**/*.mjs', true),
|
|
346
|
+
...collectFilesMatching(collection, 'scripts/**/*.json', true),
|
|
347
|
+
...collectFilesMatching(collection, 'scripts/**/*.jsonc', true),
|
|
348
|
+
...collectFilesMatching(collection, 'scripts/**/*.toml', true),
|
|
349
|
+
...collectFilesMatching(collection, 'scripts/**/*.cjs', true),
|
|
350
|
+
...collectFilesMatching(collection, 'scripts/**/*.mjs', true),
|
|
351
|
+
...collectFilesMatching(collection, '**/package.json', true),
|
|
347
352
|
];
|
|
348
353
|
|
|
349
354
|
const isDocumentationScanTarget = (match: string): boolean => {
|
|
@@ -367,19 +372,19 @@ const isDocumentationScanTarget = (match: string): boolean => {
|
|
|
367
372
|
);
|
|
368
373
|
};
|
|
369
374
|
|
|
370
|
-
const collectDocumentationFiles = (
|
|
375
|
+
const collectDocumentationFiles = (
|
|
376
|
+
collection: NonNullable<ReturnType<typeof collectSourceTree>>
|
|
377
|
+
): readonly string[] => {
|
|
371
378
|
const glob = new Bun.Glob('**/*.md');
|
|
372
|
-
let matches: IterableIterator<string>;
|
|
373
|
-
try {
|
|
374
|
-
matches = glob.scanSync({ cwd: dir, onlyFiles: true });
|
|
375
|
-
} catch {
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
|
|
379
379
|
const files: string[] = [];
|
|
380
|
-
for (const
|
|
381
|
-
if (
|
|
382
|
-
|
|
380
|
+
for (const file of collection.files) {
|
|
381
|
+
if (
|
|
382
|
+
!file.path.split('/').some((segment) => segment.startsWith('.')) &&
|
|
383
|
+
glob.match(file.path) &&
|
|
384
|
+
isWardenSourceScanTarget(file.path) &&
|
|
385
|
+
isDocumentationScanTarget(file.path)
|
|
386
|
+
) {
|
|
387
|
+
files.push(file.absolutePath);
|
|
383
388
|
}
|
|
384
389
|
}
|
|
385
390
|
return files;
|
|
@@ -692,12 +697,34 @@ const collectReconcileTableIds = (
|
|
|
692
697
|
}
|
|
693
698
|
};
|
|
694
699
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
700
|
+
interface LoadedSourceFiles {
|
|
701
|
+
readonly collection: SourceTreeCollection | null;
|
|
702
|
+
readonly sourceFiles: readonly SourceFile[];
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
const loadSourceFiles = async (rootDir: string): Promise<LoadedSourceFiles> => {
|
|
698
706
|
const sourceFiles: SourceFile[] = [];
|
|
707
|
+
const collection = collectSourceTree(rootDir, {
|
|
708
|
+
classify: (entry) => {
|
|
709
|
+
if (
|
|
710
|
+
entry.kind === 'directory' &&
|
|
711
|
+
['.git', 'dist', 'node_modules'].includes(entry.name)
|
|
712
|
+
) {
|
|
713
|
+
return { action: 'skip', reason: 'ignored-directory' };
|
|
714
|
+
}
|
|
715
|
+
if (entry.kind === 'directory') {
|
|
716
|
+
return { action: 'recurse' };
|
|
717
|
+
}
|
|
718
|
+
return entry.kind === 'file'
|
|
719
|
+
? { action: 'collect' }
|
|
720
|
+
: { action: 'skip', reason: 'unsupported-entry' };
|
|
721
|
+
},
|
|
722
|
+
});
|
|
723
|
+
if (!collection) {
|
|
724
|
+
return { collection, sourceFiles };
|
|
725
|
+
}
|
|
699
726
|
|
|
700
|
-
for (const filePath of collectTsFiles(
|
|
727
|
+
for (const filePath of collectTsFiles(collection)) {
|
|
701
728
|
try {
|
|
702
729
|
sourceFiles.push({
|
|
703
730
|
filePath,
|
|
@@ -709,7 +736,7 @@ const loadSourceFiles = async (
|
|
|
709
736
|
}
|
|
710
737
|
}
|
|
711
738
|
|
|
712
|
-
for (const filePath of collectTextScanFiles(
|
|
739
|
+
for (const filePath of collectTextScanFiles(collection)) {
|
|
713
740
|
try {
|
|
714
741
|
sourceFiles.push({
|
|
715
742
|
filePath,
|
|
@@ -721,7 +748,7 @@ const loadSourceFiles = async (
|
|
|
721
748
|
}
|
|
722
749
|
}
|
|
723
750
|
|
|
724
|
-
for (const filePath of collectDocumentationFiles(
|
|
751
|
+
for (const filePath of collectDocumentationFiles(collection)) {
|
|
725
752
|
try {
|
|
726
753
|
sourceFiles.push({
|
|
727
754
|
filePath,
|
|
@@ -733,7 +760,7 @@ const loadSourceFiles = async (
|
|
|
733
760
|
}
|
|
734
761
|
}
|
|
735
762
|
|
|
736
|
-
for (const filePath of collectDevPermitTestFiles(
|
|
763
|
+
for (const filePath of collectDevPermitTestFiles(collection)) {
|
|
737
764
|
try {
|
|
738
765
|
sourceFiles.push({
|
|
739
766
|
filePath,
|
|
@@ -745,7 +772,7 @@ const loadSourceFiles = async (
|
|
|
745
772
|
}
|
|
746
773
|
}
|
|
747
774
|
|
|
748
|
-
return sourceFiles;
|
|
775
|
+
return { collection, sourceFiles };
|
|
749
776
|
};
|
|
750
777
|
|
|
751
778
|
const collectTopoKnownIds = (
|
|
@@ -983,6 +1010,7 @@ const collectAuthoredMcpSurfaceBindingSets = (
|
|
|
983
1010
|
|
|
984
1011
|
const buildProjectContext = (
|
|
985
1012
|
sourceFiles: readonly SourceFile[],
|
|
1013
|
+
collection: SourceTreeCollection | null,
|
|
986
1014
|
rootDir: string,
|
|
987
1015
|
appTopos: readonly Topo[] = [],
|
|
988
1016
|
scope: WardenScope = EMPTY_WARDEN_SCOPE,
|
|
@@ -1003,6 +1031,11 @@ const buildProjectContext = (
|
|
|
1003
1031
|
(sourceFile) => sourceFile.kind === 'documentation'
|
|
1004
1032
|
);
|
|
1005
1033
|
context.publicWorkspaces = collectPublicWorkspaces(rootDir, {
|
|
1034
|
+
collectedPackageJsonPaths: new Set(
|
|
1035
|
+
collection?.files
|
|
1036
|
+
.filter((file) => file.path.endsWith('package.json'))
|
|
1037
|
+
.map((file) => file.path)
|
|
1038
|
+
),
|
|
1006
1039
|
exclude: scope.exclude,
|
|
1007
1040
|
});
|
|
1008
1041
|
context.governedVocabularyHistoryRequired =
|
|
@@ -1366,13 +1399,15 @@ const lintFiles = async (
|
|
|
1366
1399
|
};
|
|
1367
1400
|
}
|
|
1368
1401
|
|
|
1402
|
+
const loaded = await loadSourceFiles(rootDir);
|
|
1369
1403
|
const sourceFiles = filterSourceFilesByScope(
|
|
1370
|
-
filterSourceFilesByDraftMode(
|
|
1404
|
+
filterSourceFilesByDraftMode(loaded.sourceFiles, drafts),
|
|
1371
1405
|
rootDir,
|
|
1372
1406
|
scope
|
|
1373
1407
|
);
|
|
1374
1408
|
const context = buildProjectContext(
|
|
1375
1409
|
sourceFiles,
|
|
1410
|
+
loaded.collection,
|
|
1376
1411
|
rootDir,
|
|
1377
1412
|
topoTargets.map((target) => target.topo),
|
|
1378
1413
|
scope,
|
package/src/index.ts
CHANGED