@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 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.45",
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.45",
36
- "@ontrails/cli": "^1.0.0-beta.45",
37
- "@ontrails/config": "^1.0.0-beta.45",
38
- "@ontrails/permits": "^1.0.0-beta.45",
39
- "@ontrails/source": "^1.0.0-beta.45",
40
- "@ontrails/store": "^1.0.0-beta.45",
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"
45
+ "@ontrails/testing": "^1.0.0-beta.47"
46
46
  },
47
47
  "peerDependencies": {
48
- "@ontrails/core": "^1.0.0-beta.45",
49
- "@ontrails/topography": "^1.0.0-beta.45"
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 { findTrailDefinitions, parse } from '@ontrails/source';
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
- dir: string,
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 match of matches) {
255
- if (isWardenSourceScanTarget(match)) {
256
- files.push(`${dir}/${match}`);
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 = (dir: string): readonly string[] =>
263
- collectFilesMatching(dir, '**/*.ts');
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 = (dir: string): readonly string[] => {
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 match of matches) {
323
- if (isWardenDevPermitTestScanTarget(match)) {
324
- files.push(`${dir}/${match}`);
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 = (dir: string): readonly string[] => [
331
- ...collectFilesMatching(dir, '**/*.sh', true),
332
- ...collectFilesMatching(dir, '**/*.bash', true),
333
- ...collectFilesMatching(dir, '**/*.zsh', true),
334
- ...collectFilesMatching(dir, '**/*.yml', true),
335
- ...collectFilesMatching(dir, '**/*.yaml', true),
336
- ...collectFilesMatching(dir, 'plugin/**/*.json', true),
337
- ...collectFilesMatching(dir, 'plugin/**/*.jsonc', true),
338
- ...collectFilesMatching(dir, 'plugin/**/*.toml', true),
339
- ...collectFilesMatching(dir, 'plugin/**/*.cjs', true),
340
- ...collectFilesMatching(dir, 'plugin/**/*.mjs', true),
341
- ...collectFilesMatching(dir, 'scripts/**/*.json', true),
342
- ...collectFilesMatching(dir, 'scripts/**/*.jsonc', true),
343
- ...collectFilesMatching(dir, 'scripts/**/*.toml', true),
344
- ...collectFilesMatching(dir, 'scripts/**/*.cjs', true),
345
- ...collectFilesMatching(dir, 'scripts/**/*.mjs', true),
346
- ...collectFilesMatching(dir, '**/package.json', true),
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 = (dir: string): readonly string[] => {
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 match of matches) {
381
- if (isWardenSourceScanTarget(match) && isDocumentationScanTarget(match)) {
382
- files.push(`${dir}/${match}`);
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
- const loadSourceFiles = async (
696
- rootDir: string
697
- ): Promise<readonly SourceFile[]> => {
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(rootDir)) {
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(rootDir)) {
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(rootDir)) {
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(rootDir)) {
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(await loadSourceFiles(rootDir), drafts),
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
@@ -11,7 +11,7 @@
11
11
  export type {
12
12
  BuiltinWardenRuleName,
13
13
  GovernedVocabularyHistoryEvidence,
14
- GovernedVocabularyHistoryFormObservation,
14
+ GovernedVocabularyHistoryFormJudgment,
15
15
  GovernedVocabularyHistoryIssue,
16
16
  ProjectAwareWardenRule,
17
17
  ProjectContext,