@ontrails/warden 1.0.0-beta.42 → 1.0.0-beta.45
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 +69 -0
- package/package.json +15 -10
- package/src/adapter-check.ts +1 -1
- package/src/cli.ts +81 -5
- package/src/index.ts +17 -4
- package/src/regrade-history.ts +599 -0
- package/src/rules/cli-command-route-coherence.ts +6 -6
- package/src/rules/draft-visible-debt.ts +1 -1
- package/src/rules/duplicate-exported-symbol.ts +4 -43
- package/src/rules/governed-symbol-residue.ts +146 -53
- package/src/rules/governed-vocabulary-permutation-watch.ts +76 -0
- package/src/rules/implementation-returns-result.ts +99 -9
- package/src/rules/index.ts +17 -6
- package/src/rules/layer-field-name-drift.ts +2 -2
- package/src/rules/{library-projection-coherence.ts → library-render-coherence.ts} +11 -14
- package/src/rules/metadata.ts +51 -14
- package/src/rules/no-redundant-result-error-wrap.ts +15 -17
- package/src/rules/no-retired-cross-vocabulary.ts +0 -1
- package/src/rules/{owner-projection-parity.ts → owner-render-parity.ts} +18 -18
- package/src/rules/public-internal-deep-imports.ts +1 -3
- package/src/rules/public-output-schema.ts +1 -1
- package/src/rules/registry-names.ts +6 -4
- package/src/rules/retired-vocabulary.ts +775 -41
- package/src/rules/surface-overlay-coherence.ts +1 -1
- package/src/rules/trail-versioning-source.ts +1 -1
- package/src/rules/trailhead-override-divergence.ts +4 -4
- package/src/rules/types.ts +51 -5
- package/src/trails/governed-vocabulary-permutation-watch.trail.ts +16 -0
- package/src/trails/index.ts +3 -2
- package/src/trails/layer-field-name-drift.trail.ts +1 -1
- package/src/trails/{library-projection-coherence.trail.ts → library-render-coherence.trail.ts} +6 -6
- package/src/trails/{owner-projection-parity.trail.ts → owner-render-parity.trail.ts} +4 -4
- package/src/trails/public-output-schema.trail.ts +1 -1
- package/src/trails/run.ts +44 -2
- package/src/trails/schema.ts +41 -0
- package/src/trails/wrap-rule.ts +44 -2
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { deriveTopoGraph } from '@ontrails/topography';
|
|
2
|
-
import type {
|
|
3
|
-
TopoGraph,
|
|
4
|
-
TopoGraphLibraryProjection,
|
|
5
|
-
} from '@ontrails/topography';
|
|
2
|
+
import type { TopoGraph, TopoGraphLibraryDerived } from '@ontrails/topography';
|
|
6
3
|
|
|
7
4
|
import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
|
|
8
5
|
|
|
9
|
-
const RULE_NAME = 'library-
|
|
6
|
+
const RULE_NAME = 'library-render-coherence';
|
|
10
7
|
const TOPO_FILE = '<topo>';
|
|
11
8
|
|
|
12
9
|
const resolveGraph = (
|
|
@@ -26,7 +23,7 @@ const collisionDiagnostic = (
|
|
|
26
23
|
): WardenDiagnostic => ({
|
|
27
24
|
filePath: TOPO_FILE,
|
|
28
25
|
line: 1,
|
|
29
|
-
message: `Library
|
|
26
|
+
message: `Library rendering export collision on "${exportName}": trails ${renderTrailIds(trailIds)} derive the same package export. Rename one trail or add a library export override before materializing the generated package.`,
|
|
30
27
|
rule: RULE_NAME,
|
|
31
28
|
severity: 'error',
|
|
32
29
|
});
|
|
@@ -37,7 +34,7 @@ const missingTargetDiagnostic = (
|
|
|
37
34
|
): WardenDiagnostic => ({
|
|
38
35
|
filePath: TOPO_FILE,
|
|
39
36
|
line: 1,
|
|
40
|
-
message: `Library
|
|
37
|
+
message: `Library rendering export "${exportName}" targets unknown trail "${trailId}". Resolved library exports must stay attached to existing trail contracts.`,
|
|
41
38
|
rule: RULE_NAME,
|
|
42
39
|
severity: 'error',
|
|
43
40
|
});
|
|
@@ -48,16 +45,16 @@ const duplicateExportDiagnostic = (
|
|
|
48
45
|
): WardenDiagnostic => ({
|
|
49
46
|
filePath: TOPO_FILE,
|
|
50
47
|
line: 1,
|
|
51
|
-
message: `Library
|
|
48
|
+
message: `Library rendering contains duplicate export "${exportName}" for trails ${renderTrailIds(trailIds)}. The resolved rendering should record the collision and keep only one emitted export.`,
|
|
52
49
|
rule: RULE_NAME,
|
|
53
50
|
severity: 'error',
|
|
54
51
|
});
|
|
55
52
|
|
|
56
53
|
const collectDuplicateExportDiagnostics = (
|
|
57
|
-
|
|
54
|
+
rendering: TopoGraphLibraryDerived
|
|
58
55
|
): readonly WardenDiagnostic[] => {
|
|
59
56
|
const trailIdsByExport = new Map<string, string[]>();
|
|
60
|
-
for (const entry of
|
|
57
|
+
for (const entry of rendering.exports) {
|
|
61
58
|
const trailIds = trailIdsByExport.get(entry.exportName) ?? [];
|
|
62
59
|
trailIds.push(entry.trailId);
|
|
63
60
|
trailIdsByExport.set(entry.exportName, trailIds);
|
|
@@ -70,14 +67,14 @@ const collectDuplicateExportDiagnostics = (
|
|
|
70
67
|
};
|
|
71
68
|
|
|
72
69
|
const collectMissingTargetDiagnostics = (
|
|
73
|
-
|
|
70
|
+
rendering: TopoGraphLibraryDerived,
|
|
74
71
|
knownTrailIds: ReadonlySet<string>
|
|
75
72
|
): readonly WardenDiagnostic[] =>
|
|
76
|
-
|
|
73
|
+
rendering.exports
|
|
77
74
|
.filter((entry) => !knownTrailIds.has(entry.trailId))
|
|
78
75
|
.map((entry) => missingTargetDiagnostic(entry.exportName, entry.trailId));
|
|
79
76
|
|
|
80
|
-
export const
|
|
77
|
+
export const libraryRenderCoherence: TopoAwareWardenRule = {
|
|
81
78
|
checkTopo(topo, context) {
|
|
82
79
|
const graph = resolveGraph(topo, context?.graph);
|
|
83
80
|
if (!graph.library) {
|
|
@@ -94,7 +91,7 @@ export const libraryProjectionCoherence: TopoAwareWardenRule = {
|
|
|
94
91
|
];
|
|
95
92
|
},
|
|
96
93
|
description:
|
|
97
|
-
'Ensure resolved library
|
|
94
|
+
'Ensure resolved library rendering exports are collision-free and target existing trails.',
|
|
98
95
|
name: RULE_NAME,
|
|
99
96
|
severity: 'error',
|
|
100
97
|
};
|
package/src/rules/metadata.ts
CHANGED
|
@@ -84,9 +84,10 @@ const concernByRuleName: Partial<Record<string, WardenRuleConcern>> = {
|
|
|
84
84
|
'fires-declarations': 'signals',
|
|
85
85
|
'fork-without-preserved-implementation': 'lifecycle',
|
|
86
86
|
'governed-symbol-residue': 'lifecycle',
|
|
87
|
+
'governed-vocabulary-permutation-watch': 'lifecycle',
|
|
87
88
|
'implementation-returns-result': 'results',
|
|
88
89
|
'intent-propagation': 'composition',
|
|
89
|
-
'library-
|
|
90
|
+
'library-render-coherence': 'meta',
|
|
90
91
|
'marker-schema-unsupported': 'lifecycle',
|
|
91
92
|
'missing-reconcile': 'resources',
|
|
92
93
|
'missing-visibility': 'composition',
|
|
@@ -316,10 +317,46 @@ const builtinWardenRuleMetadataInput = {
|
|
|
316
317
|
'governed-symbol-residue': {
|
|
317
318
|
...durableExternal,
|
|
318
319
|
fix: { class: 'term-rewrite', safety: 'safe' },
|
|
320
|
+
guidance: {
|
|
321
|
+
docs: [
|
|
322
|
+
{
|
|
323
|
+
label: 'Vocabulary transition workflow',
|
|
324
|
+
path: 'docs/releases/v1-vocabulary-transition-workflow.md',
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
steps: [
|
|
328
|
+
'Run the governed transition through Regrade so committed history proves the primary migration.',
|
|
329
|
+
'Use manual edits only for review or cleanup after Regrade exhausts the safe slice.',
|
|
330
|
+
'Keep the transition registry and committed history evidence aligned.',
|
|
331
|
+
],
|
|
332
|
+
summary:
|
|
333
|
+
'Require committed Regrade evidence before completing a governed vocabulary migration.',
|
|
334
|
+
},
|
|
319
335
|
invariant:
|
|
320
|
-
'
|
|
336
|
+
'Governed vocabulary transitions carry committed Regrade provenance and do not leave or reintroduce retired identifiers.',
|
|
321
337
|
tier: 'source-static',
|
|
322
338
|
},
|
|
339
|
+
'governed-vocabulary-permutation-watch': {
|
|
340
|
+
guidance: {
|
|
341
|
+
docs: [
|
|
342
|
+
{
|
|
343
|
+
label: 'Vocabulary transition workflow',
|
|
344
|
+
path: 'docs/releases/v1-vocabulary-transition-workflow.md',
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
steps: [
|
|
348
|
+
'Add the unknown form to the governed vocabulary plan and run an incremental Regrade plan.',
|
|
349
|
+
'If the form is intentionally unrelated or preserved, classify it so later history suppresses the advisory.',
|
|
350
|
+
],
|
|
351
|
+
summary:
|
|
352
|
+
'Classify unknown governed-stem permutations recorded by committed Regrade history.',
|
|
353
|
+
},
|
|
354
|
+
invariant:
|
|
355
|
+
'Committed Regrade history keeps unknown governed-stem permutations visible until they are classified.',
|
|
356
|
+
lifecycle: { state: 'durable' },
|
|
357
|
+
scope: 'advisory',
|
|
358
|
+
tier: 'project-static',
|
|
359
|
+
},
|
|
323
360
|
'implementation-returns-result': {
|
|
324
361
|
...durableExternal,
|
|
325
362
|
invariant: 'Implementations return Result values.',
|
|
@@ -343,10 +380,10 @@ const builtinWardenRuleMetadataInput = {
|
|
|
343
380
|
'layer-field-name-drift': {
|
|
344
381
|
...durableExternal,
|
|
345
382
|
invariant:
|
|
346
|
-
'Layer input field reserved names are shared across surface
|
|
383
|
+
'Layer input field reserved names are shared across surface renderings.',
|
|
347
384
|
tier: 'source-static',
|
|
348
385
|
},
|
|
349
|
-
'library-
|
|
386
|
+
'library-render-coherence': {
|
|
350
387
|
...durableExternal,
|
|
351
388
|
guidance: {
|
|
352
389
|
docs: [
|
|
@@ -361,20 +398,20 @@ const builtinWardenRuleMetadataInput = {
|
|
|
361
398
|
],
|
|
362
399
|
steps: [
|
|
363
400
|
'Rename one source trail or add an explicit library export override before generating a package.',
|
|
364
|
-
'Keep serialized library
|
|
365
|
-
'Run the generated-package smoke after repairing
|
|
401
|
+
'Keep serialized derived library exports attached to existing trail IDs.',
|
|
402
|
+
'Run the generated-package smoke after repairing rendering drift.',
|
|
366
403
|
],
|
|
367
404
|
summary:
|
|
368
|
-
'Keep resolved library
|
|
405
|
+
'Keep resolved derived library exports collision-free and attached to one trail contract.',
|
|
369
406
|
},
|
|
370
407
|
invariant:
|
|
371
|
-
'Resolved library
|
|
408
|
+
'Resolved derived library exports are collision-free and target existing trails.',
|
|
372
409
|
tier: 'topo-aware',
|
|
373
410
|
},
|
|
374
411
|
'marker-schema-unsupported': {
|
|
375
412
|
...durableExternal,
|
|
376
413
|
invariant:
|
|
377
|
-
'Versioned schemas stay inside the supported marker
|
|
414
|
+
'Versioned schemas stay inside the supported marker derivation subset.',
|
|
378
415
|
tier: 'source-static',
|
|
379
416
|
},
|
|
380
417
|
'missing-reconcile': {
|
|
@@ -503,8 +540,8 @@ const builtinWardenRuleMetadataInput = {
|
|
|
503
540
|
'Derived store signals are consumed by matching trail on: consumers.',
|
|
504
541
|
tier: 'project-static',
|
|
505
542
|
},
|
|
506
|
-
'owner-
|
|
507
|
-
invariant: 'Framework
|
|
543
|
+
'owner-render-parity': {
|
|
544
|
+
invariant: 'Framework renderings stay aligned with owner exports.',
|
|
508
545
|
lifecycle: { state: 'durable' },
|
|
509
546
|
scope: 'internal',
|
|
510
547
|
tier: 'source-static',
|
|
@@ -565,11 +602,11 @@ const builtinWardenRuleMetadataInput = {
|
|
|
565
602
|
docs: [trailContractDocs, wardenDocs],
|
|
566
603
|
relatedRules: ['public-union-output-discriminants'],
|
|
567
604
|
steps: [
|
|
568
|
-
'Add an explicit output schema to public trails that can be
|
|
605
|
+
'Add an explicit output schema to public trails that can be rendered onto MCP or HTTP surfaces.',
|
|
569
606
|
'If the trail is composition-only, mark it visibility: "internal" instead of exposing it by default.',
|
|
570
607
|
],
|
|
571
608
|
summary:
|
|
572
|
-
'Make public surface result contracts explicit before MCP/HTTP
|
|
609
|
+
'Make public surface result contracts explicit before MCP/HTTP rendering.',
|
|
573
610
|
},
|
|
574
611
|
invariant: 'Public MCP/HTTP surface trails declare output schemas.',
|
|
575
612
|
tier: 'topo-aware',
|
|
@@ -709,7 +746,7 @@ const builtinWardenRuleMetadataInput = {
|
|
|
709
746
|
'Record explicit visibility-widening acceptance and stable-description metadata when a trailhead intentionally widens visibility.',
|
|
710
747
|
],
|
|
711
748
|
summary:
|
|
712
|
-
'Keep trailhead maps reviewable before they reach MCP
|
|
749
|
+
'Keep trailhead maps reviewable before they reach MCP rendering.',
|
|
713
750
|
},
|
|
714
751
|
invariant:
|
|
715
752
|
'Trailhead maps avoid selector overlap, hidden visibility widening, and drift-prone dynamic selectors.',
|
|
@@ -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;
|
|
@@ -39,7 +39,6 @@ const ALLOWED_PATH_SUFFIXES: readonly string[] = [
|
|
|
39
39
|
'/packages/warden/src/rules/retired-vocabulary.ts',
|
|
40
40
|
'/packages/warden/src/trails/governed-symbol-residue.trail.ts',
|
|
41
41
|
'/packages/warden/src/trails/no-retired-cross-vocabulary.trail.ts',
|
|
42
|
-
'/scripts/vocab-cutover-rewrite.ts',
|
|
43
42
|
];
|
|
44
43
|
|
|
45
44
|
interface SafeRewriteMatch {
|
|
@@ -18,14 +18,14 @@ import {
|
|
|
18
18
|
import type { AstNode } from '@ontrails/source';
|
|
19
19
|
import type { WardenDiagnostic, WardenRule } from './types.js';
|
|
20
20
|
|
|
21
|
-
const RULE_NAME = 'owner-
|
|
21
|
+
const RULE_NAME = 'owner-render-parity';
|
|
22
22
|
|
|
23
|
-
const
|
|
23
|
+
const HTTP_METHOD_DERIVATION_PATH = resolve(
|
|
24
24
|
fileURLToPath(new URL('../../../http/src/method.ts', import.meta.url))
|
|
25
25
|
);
|
|
26
26
|
|
|
27
27
|
const isTargetFile = (filePath: string): boolean =>
|
|
28
|
-
resolve(filePath) ===
|
|
28
|
+
resolve(filePath) === HTTP_METHOD_DERIVATION_PATH;
|
|
29
29
|
|
|
30
30
|
const unwrapExpression = (node: AstNode | undefined): AstNode | undefined => {
|
|
31
31
|
let current = node;
|
|
@@ -44,13 +44,13 @@ const unwrapExpression = (node: AstNode | undefined): AstNode | undefined => {
|
|
|
44
44
|
return current;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
interface
|
|
47
|
+
interface IntentKeyMap {
|
|
48
48
|
readonly keys: ReadonlySet<string>;
|
|
49
49
|
readonly node: AstNode;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
const findHttpMethodByIntentMap = (ast: AstNode):
|
|
53
|
-
let found:
|
|
52
|
+
const findHttpMethodByIntentMap = (ast: AstNode): IntentKeyMap | null => {
|
|
53
|
+
let found: IntentKeyMap | null = null;
|
|
54
54
|
|
|
55
55
|
walk(ast, (node) => {
|
|
56
56
|
if (found || node.type !== 'VariableDeclarator') {
|
|
@@ -89,16 +89,16 @@ const findHttpMethodByIntentMap = (ast: AstNode): ProjectionMap | null => {
|
|
|
89
89
|
const buildMessage = (missing: string[], extra: string[]): string => {
|
|
90
90
|
const details = [
|
|
91
91
|
missing.length > 0 ? `missing owner intents: ${missing.join(', ')}` : '',
|
|
92
|
-
extra.length > 0 ? `unknown
|
|
92
|
+
extra.length > 0 ? `unknown intentKeyMap keys: ${extra.join(', ')}` : '',
|
|
93
93
|
].filter(Boolean);
|
|
94
94
|
|
|
95
95
|
return [
|
|
96
|
-
'owner-
|
|
96
|
+
'owner-render-parity: httpMethodByIntent must cover the core intentValues owner vocabulary.',
|
|
97
97
|
...details,
|
|
98
98
|
].join(' ');
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
-
export const
|
|
101
|
+
export const ownerRenderParity: WardenRule = {
|
|
102
102
|
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
103
103
|
if (!isTargetFile(filePath)) {
|
|
104
104
|
return [];
|
|
@@ -109,35 +109,35 @@ export const ownerProjectionParity: WardenRule = {
|
|
|
109
109
|
return [];
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const
|
|
112
|
+
const intentKeyMap = findHttpMethodByIntentMap(ast);
|
|
113
113
|
const ownerKeys = new Set<string>(intentValues);
|
|
114
|
-
const
|
|
114
|
+
const derivedKeys = intentKeyMap?.keys ?? new Set<string>();
|
|
115
115
|
const missing = [...ownerKeys]
|
|
116
|
-
.filter((key) => !
|
|
116
|
+
.filter((key) => !derivedKeys.has(key))
|
|
117
117
|
.toSorted();
|
|
118
|
-
const extra = [...
|
|
118
|
+
const extra = [...derivedKeys]
|
|
119
119
|
.filter((key) => !ownerKeys.has(key))
|
|
120
120
|
.toSorted();
|
|
121
121
|
|
|
122
|
-
if (
|
|
122
|
+
if (intentKeyMap && missing.length === 0 && extra.length === 0) {
|
|
123
123
|
return [];
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
const node =
|
|
126
|
+
const node = intentKeyMap?.node ?? ast;
|
|
127
127
|
return [
|
|
128
128
|
{
|
|
129
129
|
filePath,
|
|
130
130
|
line: offsetToLine(sourceCode, node.start),
|
|
131
|
-
message:
|
|
131
|
+
message: intentKeyMap
|
|
132
132
|
? buildMessage(missing, extra)
|
|
133
|
-
: 'owner-
|
|
133
|
+
: 'owner-render-parity: expected httpMethodByIntent to render core intentValues.',
|
|
134
134
|
rule: RULE_NAME,
|
|
135
135
|
severity: 'error',
|
|
136
136
|
},
|
|
137
137
|
];
|
|
138
138
|
},
|
|
139
139
|
description:
|
|
140
|
-
'Require owner-derived
|
|
140
|
+
'Require owner-derived intentKeyMap maps to cover their authoritative owner vocabulary.',
|
|
141
141
|
name: RULE_NAME,
|
|
142
142
|
severity: 'error',
|
|
143
143
|
};
|
|
@@ -20,9 +20,7 @@ import type { WardenPublicWorkspace } from '../workspaces.js';
|
|
|
20
20
|
|
|
21
21
|
const RULE_NAME = 'public-internal-deep-imports';
|
|
22
22
|
const ONTRAILS_SPECIFIER_PATTERN = /^(@ontrails\/[^/]+)(?:\/(.+))?$/;
|
|
23
|
-
const ROOT_BARREL_INTERNAL_RE_EXPORT_ALLOWLIST = new Set(
|
|
24
|
-
'@ontrails/tracing:./internal/dev-state.js',
|
|
25
|
-
]);
|
|
23
|
+
const ROOT_BARREL_INTERNAL_RE_EXPORT_ALLOWLIST = new Set<string>();
|
|
26
24
|
|
|
27
25
|
interface ReExportSite {
|
|
28
26
|
readonly importSource: string;
|
|
@@ -10,7 +10,7 @@ const diagnosticForTrail = (trail: AnyTrail): WardenDiagnostic => ({
|
|
|
10
10
|
filePath: TOPO_FILE,
|
|
11
11
|
line: 1,
|
|
12
12
|
message:
|
|
13
|
-
`Trail "${trail.id}" is visible to public MCP/HTTP surface
|
|
13
|
+
`Trail "${trail.id}" is visible to public MCP/HTTP surface rendering but does not declare an output schema. ` +
|
|
14
14
|
'Add an explicit output schema, or mark the trail visibility as internal if it is composition-only.',
|
|
15
15
|
rule: RULE_NAME,
|
|
16
16
|
severity: 'error',
|
|
@@ -23,12 +23,13 @@ import { errorMappingCompleteness } from './error-mapping-completeness.js';
|
|
|
23
23
|
import { exampleValid } from './example-valid.js';
|
|
24
24
|
import { firesDeclarations } from './fires-declarations.js';
|
|
25
25
|
import { governedSymbolResidue } from './governed-symbol-residue.js';
|
|
26
|
+
import { governedVocabularyPermutationWatch } from './governed-vocabulary-permutation-watch.js';
|
|
26
27
|
import { implementationReturnsResult } from './implementation-returns-result.js';
|
|
27
28
|
import { incompleteAccessorForStandardOp } from './incomplete-accessor-for-standard-op.js';
|
|
28
29
|
import { incompleteCrud } from './incomplete-crud.js';
|
|
29
30
|
import { intentPropagation } from './intent-propagation.js';
|
|
30
31
|
import { layerFieldNameDrift } from './layer-field-name-drift.js';
|
|
31
|
-
import {
|
|
32
|
+
import { libraryRenderCoherence } from './library-render-coherence.js';
|
|
32
33
|
import { missingReconcile } from './missing-reconcile.js';
|
|
33
34
|
import { missingVisibility } from './missing-visibility.js';
|
|
34
35
|
import { noDevPermitInSource } from './no-dev-permit-in-source.js';
|
|
@@ -45,7 +46,7 @@ import { noThrowInImplementation } from './no-throw-in-implementation.js';
|
|
|
45
46
|
import { noTopLevelSurface } from './no-top-level-surface.js';
|
|
46
47
|
import { onReferencesExist } from './on-references-exist.js';
|
|
47
48
|
import { orphanedSignal } from './orphaned-signal.js';
|
|
48
|
-
import {
|
|
49
|
+
import { ownerRenderParity } from './owner-render-parity.js';
|
|
49
50
|
import { permitGovernance } from './permit-governance.js';
|
|
50
51
|
import { preferSchemaInference } from './prefer-schema-inference.js';
|
|
51
52
|
import { publicExportExampleCoverage } from './public-export-example-coverage.js';
|
|
@@ -109,13 +110,14 @@ export const registeredRuleNames: readonly string[] = [
|
|
|
109
110
|
exampleValid.name,
|
|
110
111
|
firesDeclarations.name,
|
|
111
112
|
governedSymbolResidue.name,
|
|
113
|
+
governedVocabularyPermutationWatch.name,
|
|
112
114
|
forkWithoutPreservedImplementation.name,
|
|
113
115
|
implementationReturnsResult.name,
|
|
114
116
|
incompleteAccessorForStandardOp.name,
|
|
115
117
|
incompleteCrud.name,
|
|
116
118
|
intentPropagation.name,
|
|
117
119
|
layerFieldNameDrift.name,
|
|
118
|
-
|
|
120
|
+
libraryRenderCoherence.name,
|
|
119
121
|
markerSchemaUnsupported.name,
|
|
120
122
|
missingReconcile.name,
|
|
121
123
|
missingVisibility.name,
|
|
@@ -133,7 +135,7 @@ export const registeredRuleNames: readonly string[] = [
|
|
|
133
135
|
noTopLevelSurface.name,
|
|
134
136
|
onReferencesExist.name,
|
|
135
137
|
orphanedSignal.name,
|
|
136
|
-
|
|
138
|
+
ownerRenderParity.name,
|
|
137
139
|
pendingForce.name,
|
|
138
140
|
permitGovernance.name,
|
|
139
141
|
preferSchemaInference.name,
|