@ontrails/warden 1.0.0-beta.43 → 1.0.0-beta.46
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 +35 -0
- package/package.json +15 -10
- 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/implementation-returns-result.ts +99 -9
- package/src/rules/index.ts +1 -1
- package/src/rules/no-redundant-result-error-wrap.ts +15 -17
- package/src/rules/public-export-example-coverage.ts +25 -37
- package/src/rules/retired-vocabulary.ts +30 -0
- package/src/rules/types.ts +11 -8
- package/src/trails/schema.ts +11 -8
|
@@ -25,8 +25,11 @@ import {
|
|
|
25
25
|
getNodeImported,
|
|
26
26
|
getNodeInit,
|
|
27
27
|
getNodeLocal,
|
|
28
|
+
getNodeLeft,
|
|
28
29
|
getNodeName,
|
|
30
|
+
getNodeOperator,
|
|
29
31
|
getNodeReturnType,
|
|
32
|
+
getNodeRight,
|
|
30
33
|
getNodeSource,
|
|
31
34
|
getNodeTypeAnnotation,
|
|
32
35
|
getNodeValue,
|
|
@@ -97,12 +100,15 @@ export type ScopedHelperMap = ReadonlyMap<
|
|
|
97
100
|
|
|
98
101
|
export type MutableScopedHelperMap = Map<ReadonlySet<string>, Set<string>>;
|
|
99
102
|
|
|
100
|
-
type ScopedResultVariableMap = ReadonlyMap<
|
|
103
|
+
export type ScopedResultVariableMap = ReadonlyMap<
|
|
101
104
|
ReadonlySet<string>,
|
|
102
105
|
ReadonlySet<string>
|
|
103
106
|
>;
|
|
104
107
|
|
|
105
|
-
type MutableScopedResultVariableMap = Map<
|
|
108
|
+
export type MutableScopedResultVariableMap = Map<
|
|
109
|
+
ReadonlySet<string>,
|
|
110
|
+
Set<string>
|
|
111
|
+
>;
|
|
106
112
|
|
|
107
113
|
export const findNearestBindingScope = (
|
|
108
114
|
name: string,
|
|
@@ -224,8 +230,8 @@ const unwrapReturnExpression = (node: AstNode): AstNode => {
|
|
|
224
230
|
return current;
|
|
225
231
|
};
|
|
226
232
|
|
|
227
|
-
/** Check
|
|
228
|
-
const
|
|
233
|
+
/** Check whether an expression has visible Result-producing provenance. */
|
|
234
|
+
export const isResultProducingExpression = (
|
|
229
235
|
argument: AstNode,
|
|
230
236
|
helperNames: ReadonlySet<string>,
|
|
231
237
|
resultVars: ScopedResultVariableMap,
|
|
@@ -240,7 +246,7 @@ const isAllowedReturnArgument = (
|
|
|
240
246
|
return (
|
|
241
247
|
consequent !== undefined &&
|
|
242
248
|
alternate !== undefined &&
|
|
243
|
-
|
|
249
|
+
isResultProducingExpression(
|
|
244
250
|
consequent,
|
|
245
251
|
helperNames,
|
|
246
252
|
resultVars,
|
|
@@ -248,7 +254,7 @@ const isAllowedReturnArgument = (
|
|
|
248
254
|
scopes,
|
|
249
255
|
scopedHelpers
|
|
250
256
|
) &&
|
|
251
|
-
|
|
257
|
+
isResultProducingExpression(
|
|
252
258
|
alternate,
|
|
253
259
|
helperNames,
|
|
254
260
|
resultVars,
|
|
@@ -369,6 +375,14 @@ const addScopedResultVariable = (
|
|
|
369
375
|
resultVars.set(scope, new Set([name]));
|
|
370
376
|
};
|
|
371
377
|
|
|
378
|
+
const clearScopedResultVariable = (
|
|
379
|
+
resultVars: MutableScopedResultVariableMap,
|
|
380
|
+
scope: ReadonlySet<string>,
|
|
381
|
+
name: string
|
|
382
|
+
): void => {
|
|
383
|
+
resultVars.get(scope)?.delete(name);
|
|
384
|
+
};
|
|
385
|
+
|
|
372
386
|
/** Record `const helper = (): Result<...> => ...` declarations for the current lexical scope. */
|
|
373
387
|
export const trackScopedResultHelperDeclaration = (
|
|
374
388
|
node: AstNode,
|
|
@@ -440,8 +454,14 @@ const trackResultVariable = (
|
|
|
440
454
|
}
|
|
441
455
|
if (
|
|
442
456
|
hasResultVariableAnnotation(id, sourceCode, resultTypeNames) ||
|
|
443
|
-
|
|
444
|
-
|
|
457
|
+
isResultProducingExpression(
|
|
458
|
+
init,
|
|
459
|
+
helperNames,
|
|
460
|
+
resultVars,
|
|
461
|
+
namespaceHelpers,
|
|
462
|
+
scopes,
|
|
463
|
+
scopedHelpers
|
|
464
|
+
)
|
|
445
465
|
) {
|
|
446
466
|
const bindingScope = findNearestBindingScope(name, scopes);
|
|
447
467
|
if (bindingScope) {
|
|
@@ -451,6 +471,63 @@ const trackResultVariable = (
|
|
|
451
471
|
}
|
|
452
472
|
};
|
|
453
473
|
|
|
474
|
+
export const collectDirectResultAssignments = (
|
|
475
|
+
body: AstNode
|
|
476
|
+
): ReadonlySet<AstNode> => {
|
|
477
|
+
const assignments = new Set<AstNode>();
|
|
478
|
+
for (const statement of getNodeBodyStatements(body)) {
|
|
479
|
+
if (statement.type !== 'ExpressionStatement') {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const expression = getNodeExpression(statement);
|
|
483
|
+
if (expression?.type === 'AssignmentExpression') {
|
|
484
|
+
assignments.add(expression);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return assignments;
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
const trackResultAssignment = (
|
|
491
|
+
node: AstNode,
|
|
492
|
+
resultVars: MutableScopedResultVariableMap,
|
|
493
|
+
helperNames: ReadonlySet<string>,
|
|
494
|
+
namespaceHelpers: NamespaceHelperMap,
|
|
495
|
+
directAssignments: ReadonlySet<AstNode>,
|
|
496
|
+
scopes: readonly ReadonlySet<string>[],
|
|
497
|
+
scopedHelpers: ScopedHelperMap
|
|
498
|
+
): void => {
|
|
499
|
+
const left = getNodeLeft(node);
|
|
500
|
+
const name = identifierName(left);
|
|
501
|
+
if (!name) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const bindingScope = findNearestBindingScope(name, scopes);
|
|
505
|
+
if (!bindingScope) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
const right = getNodeRight(node);
|
|
509
|
+
const resultRhs =
|
|
510
|
+
getNodeOperator(node) === '=' &&
|
|
511
|
+
right &&
|
|
512
|
+
isResultProducingExpression(
|
|
513
|
+
right,
|
|
514
|
+
helperNames,
|
|
515
|
+
resultVars,
|
|
516
|
+
namespaceHelpers,
|
|
517
|
+
scopes,
|
|
518
|
+
scopedHelpers
|
|
519
|
+
);
|
|
520
|
+
if (
|
|
521
|
+
resultRhs &&
|
|
522
|
+
(isScopedResultVariableBinding(name, scopes, resultVars) ||
|
|
523
|
+
directAssignments.has(node))
|
|
524
|
+
) {
|
|
525
|
+
addScopedResultVariable(resultVars, bindingScope, name);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
clearScopedResultVariable(resultVars, bindingScope, name);
|
|
529
|
+
};
|
|
530
|
+
|
|
454
531
|
// ---------------------------------------------------------------------------
|
|
455
532
|
// Return statement checking
|
|
456
533
|
// ---------------------------------------------------------------------------
|
|
@@ -470,6 +547,7 @@ const checkReturnStatements = (
|
|
|
470
547
|
const resultVars: MutableScopedResultVariableMap = new Map();
|
|
471
548
|
const scopedHelpers: MutableScopedHelperMap = new Map();
|
|
472
549
|
const initialScopes = implScope.size > 0 ? [implScope] : [];
|
|
550
|
+
const directAssignments = collectDirectResultAssignments(blockBody);
|
|
473
551
|
|
|
474
552
|
walkWithScopes(
|
|
475
553
|
blockBody,
|
|
@@ -494,6 +572,18 @@ const checkReturnStatements = (
|
|
|
494
572
|
);
|
|
495
573
|
}
|
|
496
574
|
|
|
575
|
+
if (node.type === 'AssignmentExpression') {
|
|
576
|
+
trackResultAssignment(
|
|
577
|
+
node,
|
|
578
|
+
resultVars,
|
|
579
|
+
helperNames,
|
|
580
|
+
namespaceHelpers,
|
|
581
|
+
directAssignments,
|
|
582
|
+
currentScopes,
|
|
583
|
+
scopedHelpers
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
|
|
497
587
|
if (node.type !== 'ReturnStatement') {
|
|
498
588
|
return;
|
|
499
589
|
}
|
|
@@ -505,7 +595,7 @@ const checkReturnStatements = (
|
|
|
505
595
|
}
|
|
506
596
|
|
|
507
597
|
if (
|
|
508
|
-
|
|
598
|
+
isResultProducingExpression(
|
|
509
599
|
argument,
|
|
510
600
|
helperNames,
|
|
511
601
|
resultVars,
|
package/src/rules/index.ts
CHANGED
|
@@ -81,7 +81,7 @@ import { webhookRouteCollision } from './webhook-route-collision.js';
|
|
|
81
81
|
|
|
82
82
|
export type {
|
|
83
83
|
GovernedVocabularyHistoryEvidence,
|
|
84
|
-
|
|
84
|
+
GovernedVocabularyHistoryFormJudgment,
|
|
85
85
|
GovernedVocabularyHistoryIssue,
|
|
86
86
|
WardenFix,
|
|
87
87
|
WardenFixCapability,
|
|
@@ -23,11 +23,11 @@ import {
|
|
|
23
23
|
import type { AstNode, AstParentContext } from '@ontrails/source';
|
|
24
24
|
import {
|
|
25
25
|
collectAllResultHelperNames,
|
|
26
|
+
collectDirectResultAssignments,
|
|
26
27
|
collectNamespaceHelperImports,
|
|
27
28
|
collectResultTypeNames,
|
|
28
29
|
findNearestBindingScope,
|
|
29
|
-
|
|
30
|
-
isResultExpression,
|
|
30
|
+
isResultProducingExpression,
|
|
31
31
|
trackScopedResultHelperDeclaration,
|
|
32
32
|
} from './implementation-returns-result.js';
|
|
33
33
|
import { isTestFile } from './scan.js';
|
|
@@ -82,16 +82,6 @@ const getErrorSourceVariable = (node: AstNode | null): string | null => {
|
|
|
82
82
|
return identifierName(member?.object);
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
const isResultProducingExpression = (
|
|
86
|
-
node: AstNode,
|
|
87
|
-
helperNames: ReadonlySet<string>,
|
|
88
|
-
namespaceHelpers: NamespaceHelperMap,
|
|
89
|
-
scopes: readonly ReadonlySet<string>[],
|
|
90
|
-
scopedHelpers: ScopedHelperMap
|
|
91
|
-
): boolean =>
|
|
92
|
-
isResultExpression(node) ||
|
|
93
|
-
isHelperCall(node, helperNames, namespaceHelpers, scopes, scopedHelpers);
|
|
94
|
-
|
|
95
85
|
type ResultProvenance = Map<ReadonlySet<string>, Set<string>>;
|
|
96
86
|
|
|
97
87
|
const markResultVariable = (
|
|
@@ -158,6 +148,7 @@ const trackVariableDeclarator = (
|
|
|
158
148
|
isResultProducingExpression(
|
|
159
149
|
init,
|
|
160
150
|
helperNames,
|
|
151
|
+
provenance,
|
|
161
152
|
namespaceHelpers,
|
|
162
153
|
scopes,
|
|
163
154
|
scopedHelpers
|
|
@@ -175,11 +166,10 @@ const trackAssignmentExpression = (
|
|
|
175
166
|
helperNames: ReadonlySet<string>,
|
|
176
167
|
namespaceHelpers: NamespaceHelperMap,
|
|
177
168
|
scopedHelpers: ScopedHelperMap,
|
|
169
|
+
directAssignments: ReadonlySet<AstNode>,
|
|
178
170
|
scopes: readonly ReadonlySet<string>[]
|
|
179
171
|
): void => {
|
|
180
172
|
const left = getNodeLeft(node);
|
|
181
|
-
const operator = getNodeOperator(node);
|
|
182
|
-
const right = getNodeRight(node);
|
|
183
173
|
const name = identifierName(left);
|
|
184
174
|
if (!name) {
|
|
185
175
|
return;
|
|
@@ -188,16 +178,22 @@ const trackAssignmentExpression = (
|
|
|
188
178
|
if (!scope) {
|
|
189
179
|
return;
|
|
190
180
|
}
|
|
191
|
-
|
|
192
|
-
|
|
181
|
+
const right = getNodeRight(node);
|
|
182
|
+
const resultRhs =
|
|
183
|
+
getNodeOperator(node) === '=' &&
|
|
193
184
|
right &&
|
|
194
185
|
isResultProducingExpression(
|
|
195
186
|
right,
|
|
196
187
|
helperNames,
|
|
188
|
+
provenance,
|
|
197
189
|
namespaceHelpers,
|
|
198
190
|
scopes,
|
|
199
191
|
scopedHelpers
|
|
200
|
-
)
|
|
192
|
+
);
|
|
193
|
+
if (
|
|
194
|
+
resultRhs &&
|
|
195
|
+
(hasResultProvenance(provenance, scope, name) ||
|
|
196
|
+
directAssignments.has(node))
|
|
201
197
|
) {
|
|
202
198
|
markResultVariable(provenance, scope, name);
|
|
203
199
|
return;
|
|
@@ -258,6 +254,7 @@ const checkFunctionBody = (
|
|
|
258
254
|
const scopedHelpers: MutableScopedHelperMap = new Map();
|
|
259
255
|
const implScope = collectScopeFrameBindings(owner);
|
|
260
256
|
const initialScopes = implScope.size > 0 ? [implScope] : [];
|
|
257
|
+
const directAssignments = collectDirectResultAssignments(body);
|
|
261
258
|
|
|
262
259
|
walkWithScopes(
|
|
263
260
|
body,
|
|
@@ -287,6 +284,7 @@ const checkFunctionBody = (
|
|
|
287
284
|
helperNames,
|
|
288
285
|
namespaceHelpers,
|
|
289
286
|
scopedHelpers,
|
|
287
|
+
directAssignments,
|
|
290
288
|
scopes
|
|
291
289
|
);
|
|
292
290
|
return;
|
|
@@ -44,8 +44,9 @@ import {
|
|
|
44
44
|
getNodeValue,
|
|
45
45
|
offsetToLine,
|
|
46
46
|
parse,
|
|
47
|
+
parseWithDiagnostics,
|
|
47
48
|
} from '@ontrails/source';
|
|
48
|
-
import type { AstNode } from '@ontrails/source';
|
|
49
|
+
import type { AstNode, SourceComment } from '@ontrails/source';
|
|
49
50
|
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
50
51
|
|
|
51
52
|
const RULE_NAME = 'public-export-example-coverage';
|
|
@@ -379,44 +380,19 @@ const declarationNameMatches = (
|
|
|
379
380
|
return false;
|
|
380
381
|
};
|
|
381
382
|
|
|
382
|
-
/**
|
|
383
|
-
* Comment texts found in an inter-statement trivia gap. The gap between two
|
|
384
|
-
* top-level statements contains only whitespace and comments, so a small
|
|
385
|
-
* line/block comment scan recovers the same comment set TypeScript's
|
|
386
|
-
* `getLeadingCommentRanges` returns for the statement's full start.
|
|
387
|
-
*/
|
|
388
|
-
const collectCommentTexts = (gapText: string): readonly string[] => {
|
|
389
|
-
const comments: string[] = [];
|
|
390
|
-
let index = 0;
|
|
391
|
-
while (index < gapText.length - 1) {
|
|
392
|
-
if (gapText[index] === '/' && gapText[index + 1] === '/') {
|
|
393
|
-
const lineEnd = gapText.indexOf('\n', index);
|
|
394
|
-
const stop = lineEnd === -1 ? gapText.length : lineEnd;
|
|
395
|
-
comments.push(gapText.slice(index, stop));
|
|
396
|
-
index = stop + 1;
|
|
397
|
-
} else if (gapText[index] === '/' && gapText[index + 1] === '*') {
|
|
398
|
-
const blockEnd = gapText.indexOf('*/', index + 2);
|
|
399
|
-
const stop = blockEnd === -1 ? gapText.length : blockEnd + 2;
|
|
400
|
-
comments.push(gapText.slice(index, stop));
|
|
401
|
-
index = stop;
|
|
402
|
-
} else {
|
|
403
|
-
index += 1;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return comments;
|
|
407
|
-
};
|
|
408
|
-
|
|
409
383
|
const EXAMPLE_TAG_PATTERN = /@example\b/;
|
|
384
|
+
const LINE_TERMINATOR_PATTERN = /[\n\r\u2028\u2029]/u;
|
|
410
385
|
|
|
411
386
|
/**
|
|
412
387
|
* True when the exported declaration named `importedName` in `sourceText`
|
|
413
|
-
* carries a leading comment containing `@example`.
|
|
414
|
-
*
|
|
415
|
-
*
|
|
388
|
+
* carries a leading comment containing `@example`. Parser-native comments in
|
|
389
|
+
* the trivia gap between the preceding top-level statement's end (or file
|
|
390
|
+
* start) and the matching export statement are considered leading.
|
|
416
391
|
*/
|
|
417
392
|
const hasLeadingExampleForExport = (
|
|
418
393
|
sourceText: string,
|
|
419
394
|
ast: AstNode,
|
|
395
|
+
comments: readonly SourceComment[],
|
|
420
396
|
importedName: string
|
|
421
397
|
): boolean => {
|
|
422
398
|
const body = programBody(ast);
|
|
@@ -429,9 +405,16 @@ const hasLeadingExampleForExport = (
|
|
|
429
405
|
continue;
|
|
430
406
|
}
|
|
431
407
|
const previous = body[statementIndex - 1];
|
|
432
|
-
const
|
|
433
|
-
return
|
|
434
|
-
|
|
408
|
+
const gapStart = previous?.end ?? 0;
|
|
409
|
+
return comments.some(
|
|
410
|
+
(comment) =>
|
|
411
|
+
gapStart <= comment.start &&
|
|
412
|
+
comment.end <= statement.start &&
|
|
413
|
+
(previous === undefined ||
|
|
414
|
+
LINE_TERMINATOR_PATTERN.test(
|
|
415
|
+
sourceText.slice(gapStart, comment.start)
|
|
416
|
+
)) &&
|
|
417
|
+
EXAMPLE_TAG_PATTERN.test(comment.value)
|
|
435
418
|
);
|
|
436
419
|
}
|
|
437
420
|
return false;
|
|
@@ -468,8 +451,8 @@ const coverageDiagnosticsForSpecifier = (
|
|
|
468
451
|
),
|
|
469
452
|
];
|
|
470
453
|
}
|
|
471
|
-
const
|
|
472
|
-
if (!
|
|
454
|
+
const parsedSource = parseWithDiagnostics(sourcePath, sourceText);
|
|
455
|
+
if (!parsedSource.ast || parsedSource.diagnostics.length > 0) {
|
|
473
456
|
return [
|
|
474
457
|
diagnostic(
|
|
475
458
|
sourceCode,
|
|
@@ -481,7 +464,12 @@ const coverageDiagnosticsForSpecifier = (
|
|
|
481
464
|
];
|
|
482
465
|
}
|
|
483
466
|
if (
|
|
484
|
-
hasLeadingExampleForExport(
|
|
467
|
+
hasLeadingExampleForExport(
|
|
468
|
+
sourceText,
|
|
469
|
+
parsedSource.ast,
|
|
470
|
+
parsedSource.comments,
|
|
471
|
+
specifier.importedName
|
|
472
|
+
)
|
|
485
473
|
) {
|
|
486
474
|
return [];
|
|
487
475
|
}
|
|
@@ -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
|
|
|
@@ -829,6 +830,9 @@ export const governedVocabularyTransitions =
|
|
|
829
830
|
'cliProjection',
|
|
830
831
|
'cliProjectionSchema',
|
|
831
832
|
'collectLibraryProjection',
|
|
833
|
+
'collectionExtensionProjectionForFileRenames',
|
|
834
|
+
'extensionProjection',
|
|
835
|
+
'filterProjectedTargetExtensions',
|
|
832
836
|
'projectActivationEdge',
|
|
833
837
|
'projectActivationSource',
|
|
834
838
|
'projectActivationSourceDeclaration',
|
|
@@ -860,6 +864,11 @@ export const governedVocabularyTransitions =
|
|
|
860
864
|
'projectTrailVersions',
|
|
861
865
|
'projectVersionDetours',
|
|
862
866
|
'projectVersionRuntimeRefs',
|
|
867
|
+
'projectVocabularyText',
|
|
868
|
+
'projected',
|
|
869
|
+
'projectedEvidence',
|
|
870
|
+
'projectedFileInScopeCount',
|
|
871
|
+
'projectedTargetPaths',
|
|
863
872
|
'SchemaProjector',
|
|
864
873
|
'deriveShippedSurfaceProjectionInventory',
|
|
865
874
|
'deriveTrailCliCommandProjection',
|
|
@@ -1190,6 +1199,15 @@ export const governedVocabularyTransitions =
|
|
|
1190
1199
|
from: 'collectLibraryProjection',
|
|
1191
1200
|
to: 'deriveTopoGraphLibrary',
|
|
1192
1201
|
},
|
|
1202
|
+
{
|
|
1203
|
+
from: 'collectionExtensionProjectionForFileRenames',
|
|
1204
|
+
to: 'deriveCollectionExtensionsForFileRenames',
|
|
1205
|
+
},
|
|
1206
|
+
{ from: 'extensionProjection', to: 'derivedExtensions' },
|
|
1207
|
+
{
|
|
1208
|
+
from: 'filterProjectedTargetExtensions',
|
|
1209
|
+
to: 'filterDerivedTargetExtensions',
|
|
1210
|
+
},
|
|
1193
1211
|
{ from: 'projectActivationEdge', to: 'deriveActivationEdge' },
|
|
1194
1212
|
{ from: 'projectActivationSource', to: 'deriveActivationSource' },
|
|
1195
1213
|
{
|
|
@@ -1233,6 +1251,18 @@ export const governedVocabularyTransitions =
|
|
|
1233
1251
|
from: 'projectVersionRuntimeRefs',
|
|
1234
1252
|
to: 'deriveVersionRuntimeRefs',
|
|
1235
1253
|
},
|
|
1254
|
+
{ from: 'projectVocabularyText', to: 'deriveVocabularyText' },
|
|
1255
|
+
{
|
|
1256
|
+
from: 'projected',
|
|
1257
|
+
safety: 'review',
|
|
1258
|
+
to: 'derived',
|
|
1259
|
+
},
|
|
1260
|
+
{ from: 'projectedEvidence', to: 'derivedEvidence' },
|
|
1261
|
+
{
|
|
1262
|
+
from: 'projectedFileInScopeCount',
|
|
1263
|
+
to: 'derivedFileInScopeCount',
|
|
1264
|
+
},
|
|
1265
|
+
{ from: 'projectedTargetPaths', to: 'derivedTargetPaths' },
|
|
1236
1266
|
{ from: 'SchemaProjector', to: 'SchemaDeriver' },
|
|
1237
1267
|
{
|
|
1238
1268
|
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(),
|