@ontrails/warden 1.0.0-beta.4 → 1.0.0-beta.41
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 +835 -13
- package/README.md +133 -26
- package/bin/warden.ts +51 -0
- package/package.json +27 -6
- package/src/adapter-check.ts +136 -0
- package/src/cli.ts +1570 -105
- package/src/command.ts +986 -0
- package/src/config.ts +193 -0
- package/src/draft.ts +22 -0
- package/src/drift.ts +233 -23
- package/src/fix.ts +126 -0
- package/src/formatters.ts +78 -13
- package/src/guide.ts +245 -0
- package/src/index.ts +247 -15
- package/src/project-context.ts +446 -0
- package/src/project-rules.ts +290 -0
- package/src/resolve.ts +531 -0
- package/src/rules/activation-orphan.ts +97 -0
- package/src/rules/captured-kernel.ts +375 -0
- package/src/rules/circular-refs.ts +150 -0
- package/src/rules/cli-command-route-coherence.ts +177 -0
- package/src/rules/composes-declarations.ts +839 -0
- package/src/rules/context-no-surface-types.ts +79 -15
- package/src/rules/dead-internal-trail.ts +161 -0
- package/src/rules/dead-public-trail.ts +258 -0
- package/src/rules/draft-file-marking.ts +155 -0
- package/src/rules/draft-visible-debt.ts +82 -0
- package/src/rules/duplicate-exported-symbol.ts +211 -0
- package/src/rules/duplicate-public-contract.ts +137 -0
- package/src/rules/entity-exists.ts +254 -0
- package/src/rules/entity-ids.ts +15 -0
- package/src/rules/error-mapping-completeness.ts +290 -0
- package/src/rules/example-valid.ts +395 -0
- package/src/rules/fires-declarations.ts +740 -0
- package/src/rules/governed-symbol-residue.ts +438 -0
- package/src/rules/implementation-returns-result.ts +1409 -166
- package/src/rules/incomplete-accessor-for-standard-op.ts +272 -0
- package/src/rules/incomplete-crud.ts +583 -0
- package/src/rules/index.ts +277 -10
- package/src/rules/intent-propagation.ts +125 -0
- package/src/rules/layer-field-name-drift.ts +102 -0
- package/src/rules/library-projection-coherence.ts +100 -0
- package/src/rules/metadata.ts +871 -0
- package/src/rules/missing-reconcile.ts +97 -0
- package/src/rules/missing-visibility.ts +111 -0
- package/src/rules/no-destructured-compose.ts +196 -0
- package/src/rules/no-dev-permit-in-source.ts +99 -0
- package/src/rules/no-direct-implementation-call.ts +12 -7
- package/src/rules/no-legacy-cli-alias-export.ts +247 -0
- package/src/rules/no-legacy-layer-imports.ts +211 -0
- package/src/rules/no-native-error-result.ts +118 -0
- package/src/rules/no-redundant-result-error-wrap.ts +384 -0
- package/src/rules/no-retired-cross-vocabulary.ts +204 -0
- package/src/rules/no-sync-result-assumption.ts +1141 -98
- package/src/rules/no-throw-in-detour-recover.ts +225 -0
- package/src/rules/no-throw-in-implementation.ts +15 -8
- package/src/rules/no-top-level-surface.ts +371 -0
- package/src/rules/on-references-exist.ts +194 -0
- package/src/rules/orphaned-signal.ts +149 -0
- package/src/rules/owner-projection-parity.ts +143 -0
- package/src/rules/permit-governance.ts +25 -0
- package/src/rules/public-export-example-coverage.ts +573 -0
- package/src/rules/public-internal-deep-imports.ts +456 -0
- package/src/rules/public-output-schema.ts +29 -0
- package/src/rules/public-union-output-discriminants.ts +150 -0
- package/src/rules/read-intent-fires.ts +188 -0
- package/src/rules/reference-exists.ts +97 -0
- package/src/rules/registry-names.ts +167 -0
- package/src/rules/resolved-import-boundary.ts +146 -0
- package/src/rules/resource-declarations.ts +697 -0
- package/src/rules/resource-exists.ts +181 -0
- package/src/rules/resource-id-grammar.ts +65 -0
- package/src/rules/resource-mock-coverage.ts +115 -0
- package/src/rules/retired-vocabulary.ts +633 -0
- package/src/rules/scan.ts +38 -25
- package/src/rules/scheduled-destroy-intent.ts +44 -0
- package/src/rules/signal-graph-coaching.ts +220 -0
- package/src/rules/source/composition.ts +165 -0
- package/src/rules/source/drafts.ts +164 -0
- package/src/rules/source/entities.ts +618 -0
- package/src/rules/source/pragmas.ts +45 -0
- package/src/rules/source/resources.ts +64 -0
- package/src/rules/source/signals.ts +397 -0
- package/src/rules/source/stores.ts +310 -0
- package/src/rules/specs.ts +9 -5
- package/src/rules/static-resource-accessor-preference.ts +653 -0
- package/src/rules/surface-overlay-coherence.ts +262 -0
- package/src/rules/surface-trailhead-coherence.ts +366 -0
- package/src/rules/trail-fork-coaching.ts +625 -0
- package/src/rules/trail-versioning-source.ts +1076 -0
- package/src/rules/trail-versioning-topo.ts +172 -0
- package/src/rules/trailhead-override-divergence.ts +356 -0
- package/src/rules/types.ts +354 -8
- package/src/rules/unmaterialized-activation-source.ts +85 -0
- package/src/rules/unreachable-detour-shadowing.ts +339 -0
- package/src/rules/valid-describe-refs.ts +162 -32
- package/src/rules/valid-detour-contract.ts +78 -0
- package/src/rules/warden-export-symmetry.ts +540 -0
- package/src/rules/warden-rules-use-ast.ts +1109 -0
- package/src/rules/webhook-route-collision.ts +306 -0
- package/src/trails/activation-orphan.trail.ts +84 -0
- package/src/trails/captured-kernel.trail.ts +108 -0
- package/src/trails/circular-refs.trail.ts +29 -0
- package/src/trails/cli-command-route-coherence.trail.ts +47 -0
- package/src/trails/composes-declarations.trail.ts +22 -0
- package/src/trails/context-no-surface-types.trail.ts +21 -0
- package/src/trails/dead-internal-trail.trail.ts +26 -0
- package/src/trails/dead-public-trail.trail.ts +31 -0
- package/src/trails/deprecation-without-guidance.trail.ts +21 -0
- package/src/trails/draft-file-marking.trail.ts +16 -0
- package/src/trails/draft-visible-debt.trail.ts +16 -0
- package/src/trails/duplicate-exported-symbol.trail.ts +48 -0
- package/src/trails/duplicate-public-contract.trail.ts +47 -0
- package/src/trails/entity-exists.trail.ts +21 -0
- package/src/trails/error-mapping-completeness.trail.ts +30 -0
- package/src/trails/example-valid.trail.ts +25 -0
- package/src/trails/fires-declarations.trail.ts +23 -0
- package/src/trails/fork-without-preserved-implementation.trail.ts +31 -0
- package/src/trails/governed-symbol-residue.trail.ts +24 -0
- package/src/trails/implementation-returns-result.trail.ts +20 -0
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
- package/src/trails/incomplete-crud.trail.ts +39 -0
- package/src/trails/index.ts +89 -0
- package/src/trails/intent-propagation.trail.ts +30 -0
- package/src/trails/layer-field-name-drift.trail.ts +39 -0
- package/src/trails/library-projection-coherence.trail.ts +43 -0
- package/src/trails/marker-schema-unsupported.trail.ts +23 -0
- package/src/trails/missing-reconcile.trail.ts +33 -0
- package/src/trails/missing-visibility.trail.ts +22 -0
- package/src/trails/no-destructured-compose.trail.ts +44 -0
- package/src/trails/no-dev-permit-in-source.trail.ts +16 -0
- package/src/trails/no-direct-implementation-call.trail.ts +16 -0
- package/src/trails/no-legacy-cli-alias-export.trail.ts +41 -0
- package/src/trails/no-legacy-layer-imports.trail.ts +41 -0
- package/src/trails/no-native-error-result.trail.ts +18 -0
- package/src/trails/no-redundant-result-error-wrap.trail.ts +55 -0
- package/src/trails/no-retired-cross-vocabulary.trail.ts +42 -0
- package/src/trails/no-sync-result-assumption.trail.ts +19 -0
- package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
- package/src/trails/no-throw-in-implementation.trail.ts +20 -0
- package/src/trails/no-top-level-surface.trail.ts +43 -0
- package/src/trails/on-references-exist.trail.ts +21 -0
- package/src/trails/orphaned-signal.trail.ts +36 -0
- package/src/trails/owner-projection-parity.trail.ts +26 -0
- package/src/trails/pending-force.trail.ts +21 -0
- package/src/trails/permit-governance.trail.ts +51 -0
- package/src/trails/prefer-schema-inference.trail.ts +21 -0
- package/src/trails/public-export-example-coverage.trail.ts +16 -0
- package/src/trails/public-internal-deep-imports.trail.ts +94 -0
- package/src/trails/public-output-schema.trail.ts +55 -0
- package/src/trails/public-union-output-discriminants.trail.ts +33 -0
- package/src/trails/read-intent-fires.trail.ts +20 -0
- package/src/trails/reference-exists.trail.ts +25 -0
- package/src/trails/resolved-import-boundary.trail.ts +109 -0
- package/src/trails/resource-declarations.trail.ts +25 -0
- package/src/trails/resource-exists.trail.ts +27 -0
- package/src/trails/resource-id-grammar.trail.ts +39 -0
- package/src/trails/resource-mock-coverage.trail.ts +40 -0
- package/src/trails/run.ts +160 -0
- package/src/trails/scheduled-destroy-intent.trail.ts +56 -0
- package/src/trails/schema.ts +237 -0
- package/src/trails/signal-graph-coaching.trail.ts +77 -0
- package/src/trails/static-resource-accessor-preference.trail.ts +25 -0
- package/src/trails/surface-overlay-coherence.trail.ts +24 -0
- package/src/trails/surface-trailhead-coherence.trail.ts +25 -0
- package/src/trails/topo.ts +6 -0
- package/src/trails/trail-fork-coaching.trail.ts +42 -0
- package/src/trails/trailhead-override-divergence.trail.ts +47 -0
- package/src/trails/unmaterialized-activation-source.trail.ts +72 -0
- package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
- package/src/trails/valid-describe-refs.trail.ts +18 -0
- package/src/trails/valid-detour-contract.trail.ts +71 -0
- package/src/trails/version-gap.trail.ts +35 -0
- package/src/trails/version-pinned-compose.trail.ts +23 -0
- package/src/trails/version-without-examples.trail.ts +38 -0
- package/src/trails/warden-export-symmetry.trail.ts +16 -0
- package/src/trails/warden-rules-use-ast.trail.ts +64 -0
- package/src/trails/webhook-route-collision.trail.ts +50 -0
- package/src/trails/wrap-rule.ts +224 -0
- package/src/workspaces.ts +199 -0
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/dist/cli.d.ts +0 -46
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -218
- package/dist/cli.js.map +0 -1
- package/dist/drift.d.ts +0 -26
- package/dist/drift.d.ts.map +0 -1
- package/dist/drift.js +0 -27
- package/dist/drift.js.map +0 -1
- package/dist/formatters.d.ts +0 -29
- package/dist/formatters.d.ts.map +0 -1
- package/dist/formatters.js +0 -87
- package/dist/formatters.js.map +0 -1
- package/dist/index.d.ts +0 -26
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -26
- package/dist/index.js.map +0 -1
- package/dist/rules/ast.d.ts +0 -41
- package/dist/rules/ast.d.ts.map +0 -1
- package/dist/rules/ast.js +0 -161
- package/dist/rules/ast.js.map +0 -1
- package/dist/rules/context-no-surface-types.d.ts +0 -12
- package/dist/rules/context-no-surface-types.d.ts.map +0 -1
- package/dist/rules/context-no-surface-types.js +0 -96
- package/dist/rules/context-no-surface-types.js.map +0 -1
- package/dist/rules/implementation-returns-result.d.ts +0 -13
- package/dist/rules/implementation-returns-result.d.ts.map +0 -1
- package/dist/rules/implementation-returns-result.js +0 -277
- package/dist/rules/implementation-returns-result.js.map +0 -1
- package/dist/rules/index.d.ts +0 -15
- package/dist/rules/index.d.ts.map +0 -1
- package/dist/rules/index.js +0 -34
- package/dist/rules/index.js.map +0 -1
- package/dist/rules/no-direct-impl-in-route.d.ts +0 -12
- package/dist/rules/no-direct-impl-in-route.d.ts.map +0 -1
- package/dist/rules/no-direct-impl-in-route.js +0 -47
- package/dist/rules/no-direct-impl-in-route.js.map +0 -1
- package/dist/rules/no-direct-implementation-call.d.ts +0 -12
- package/dist/rules/no-direct-implementation-call.d.ts.map +0 -1
- package/dist/rules/no-direct-implementation-call.js +0 -39
- package/dist/rules/no-direct-implementation-call.js.map +0 -1
- package/dist/rules/no-sync-result-assumption.d.ts +0 -6
- package/dist/rules/no-sync-result-assumption.d.ts.map +0 -1
- package/dist/rules/no-sync-result-assumption.js +0 -98
- package/dist/rules/no-sync-result-assumption.js.map +0 -1
- package/dist/rules/no-throw-in-detour-target.d.ts +0 -12
- package/dist/rules/no-throw-in-detour-target.d.ts.map +0 -1
- package/dist/rules/no-throw-in-detour-target.js +0 -87
- package/dist/rules/no-throw-in-detour-target.js.map +0 -1
- package/dist/rules/no-throw-in-implementation.d.ts +0 -9
- package/dist/rules/no-throw-in-implementation.d.ts.map +0 -1
- package/dist/rules/no-throw-in-implementation.js +0 -34
- package/dist/rules/no-throw-in-implementation.js.map +0 -1
- package/dist/rules/prefer-schema-inference.d.ts +0 -7
- package/dist/rules/prefer-schema-inference.d.ts.map +0 -1
- package/dist/rules/prefer-schema-inference.js +0 -86
- package/dist/rules/prefer-schema-inference.js.map +0 -1
- package/dist/rules/scan.d.ts +0 -8
- package/dist/rules/scan.d.ts.map +0 -1
- package/dist/rules/scan.js +0 -32
- package/dist/rules/scan.js.map +0 -1
- package/dist/rules/specs.d.ts +0 -29
- package/dist/rules/specs.d.ts.map +0 -1
- package/dist/rules/specs.js +0 -192
- package/dist/rules/specs.js.map +0 -1
- package/dist/rules/structure.d.ts +0 -13
- package/dist/rules/structure.d.ts.map +0 -1
- package/dist/rules/structure.js +0 -142
- package/dist/rules/structure.js.map +0 -1
- package/dist/rules/types.d.ts +0 -52
- package/dist/rules/types.d.ts.map +0 -1
- package/dist/rules/types.js +0 -2
- package/dist/rules/types.js.map +0 -1
- package/dist/rules/valid-describe-refs.d.ts +0 -7
- package/dist/rules/valid-describe-refs.d.ts.map +0 -1
- package/dist/rules/valid-describe-refs.js +0 -51
- package/dist/rules/valid-describe-refs.js.map +0 -1
- package/dist/rules/valid-detour-refs.d.ts +0 -6
- package/dist/rules/valid-detour-refs.d.ts.map +0 -1
- package/dist/rules/valid-detour-refs.js +0 -116
- package/dist/rules/valid-detour-refs.js.map +0 -1
- package/src/__tests__/cli.test.ts +0 -198
- package/src/__tests__/drift.test.ts +0 -74
- package/src/__tests__/formatters.test.ts +0 -157
- package/src/__tests__/implementation-returns-result.test.ts +0 -129
- package/src/__tests__/no-direct-implementation-call.test.ts +0 -83
- package/src/__tests__/no-sync-result-assumption.test.ts +0 -85
- package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
- package/src/__tests__/prefer-schema-inference.test.ts +0 -84
- package/src/__tests__/rules.test.ts +0 -227
- package/src/__tests__/valid-describe-refs.test.ts +0 -60
- package/src/rules/ast.ts +0 -213
- package/src/rules/no-direct-impl-in-route.ts +0 -81
- package/src/rules/no-throw-in-detour-target.ts +0 -150
- package/src/rules/valid-detour-refs.ts +0 -187
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/src/rules/types.ts
CHANGED
|
@@ -1,14 +1,230 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DiagnosticSeverity,
|
|
3
|
+
Intent,
|
|
4
|
+
RuleDiagnosticBase,
|
|
5
|
+
ScanTargets,
|
|
6
|
+
SurfaceBindings,
|
|
7
|
+
Topo,
|
|
8
|
+
} from '@ontrails/core';
|
|
9
|
+
import type { TopoGraph } from '@ontrails/topography';
|
|
10
|
+
|
|
11
|
+
import type { WardenDepth } from '../config.js';
|
|
12
|
+
import type { WardenImportResolution } from '../resolve.js';
|
|
13
|
+
import type { WardenPublicWorkspace } from '../workspaces.js';
|
|
14
|
+
|
|
1
15
|
/**
|
|
2
16
|
* Severity level for warden diagnostics.
|
|
3
17
|
*/
|
|
4
|
-
export type WardenSeverity =
|
|
18
|
+
export type WardenSeverity = DiagnosticSeverity;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Execution tier for a Warden rule.
|
|
22
|
+
*
|
|
23
|
+
* Tier names describe the narrowest runtime shape that can answer the rule's
|
|
24
|
+
* question. They do not change ownership: source-static rules can still be
|
|
25
|
+
* durable public Warden doctrine.
|
|
26
|
+
*/
|
|
27
|
+
export type WardenRuleTier =
|
|
28
|
+
| 'advisory'
|
|
29
|
+
| 'drift'
|
|
30
|
+
| 'project-static'
|
|
31
|
+
| 'source-static'
|
|
32
|
+
| 'topo-aware';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Context where a Warden rule applies.
|
|
36
|
+
*/
|
|
37
|
+
export type WardenRuleScope =
|
|
38
|
+
| 'advisory'
|
|
39
|
+
| 'extension'
|
|
40
|
+
| 'external'
|
|
41
|
+
| 'internal'
|
|
42
|
+
| 'repo-local'
|
|
43
|
+
| 'temporary';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Lifecycle state for a Warden rule.
|
|
47
|
+
*/
|
|
48
|
+
export type WardenRuleLifecycleState = 'deprecated' | 'durable' | 'temporary';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Queryable concern dimension for Warden rule metadata.
|
|
52
|
+
*/
|
|
53
|
+
export type WardenRuleConcern =
|
|
54
|
+
| 'composition'
|
|
55
|
+
| 'general'
|
|
56
|
+
| 'lifecycle'
|
|
57
|
+
| 'meta'
|
|
58
|
+
| 'permits'
|
|
59
|
+
| 'resources'
|
|
60
|
+
| 'results'
|
|
61
|
+
| 'signals';
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Lifecycle metadata for a Warden rule.
|
|
65
|
+
*/
|
|
66
|
+
export interface WardenRuleLifecycle {
|
|
67
|
+
/** Current lifecycle state. */
|
|
68
|
+
readonly state: WardenRuleLifecycleState;
|
|
69
|
+
/** Required for temporary or deprecated rules. */
|
|
70
|
+
readonly retireWhen?: string | undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Documentation or reference target for Warden remediation guidance.
|
|
75
|
+
*/
|
|
76
|
+
export interface WardenGuidanceLink {
|
|
77
|
+
/** Human-readable link label. */
|
|
78
|
+
readonly label: string;
|
|
79
|
+
/** Repository-relative documentation path, when the target is in-tree. */
|
|
80
|
+
readonly path?: string | undefined;
|
|
81
|
+
/** External documentation URL, when the target is outside the repo. */
|
|
82
|
+
readonly url?: string | undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Structured remediation guidance that can be rendered for humans or projected
|
|
87
|
+
* into agent-facing manifests without scraping diagnostic prose.
|
|
88
|
+
*/
|
|
89
|
+
export interface WardenGuidance {
|
|
90
|
+
/** Concise next step for the finding or rule. */
|
|
91
|
+
readonly summary: string;
|
|
92
|
+
/** Ordered remediation steps, when the rule benefits from more detail. */
|
|
93
|
+
readonly steps?: readonly string[] | undefined;
|
|
94
|
+
/** Reference docs that explain the invariant. */
|
|
95
|
+
readonly docs?: readonly WardenGuidanceLink[] | undefined;
|
|
96
|
+
/** Example commands. These are guidance examples, not autofix contracts. */
|
|
97
|
+
readonly commands?: readonly string[] | undefined;
|
|
98
|
+
/** Related rule identifiers that help agents navigate nearby doctrine. */
|
|
99
|
+
readonly relatedRules?: readonly string[] | undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Transform class a fix belongs to.
|
|
104
|
+
*
|
|
105
|
+
* Names the kind of mechanical change so agents, the guide, and downstream
|
|
106
|
+
* regrades can route by class. `term-rewrite` is the durable name for retired
|
|
107
|
+
* vocabulary renames (`vocab-cutover` is historical wording only).
|
|
108
|
+
* `export-restructure` names structural export rewrites — the finding is not
|
|
109
|
+
* a rename but a reshaping of a module export (for example wrapping a legacy
|
|
110
|
+
* CLI alias map into a `surfaceOverlay({ cli })` entry inside
|
|
111
|
+
* `trailsOverlays`), so downstream Regrade routes it to a structural
|
|
112
|
+
* transform class instead of a span replacement.
|
|
113
|
+
*/
|
|
114
|
+
export type WardenFixClass = 'export-restructure' | 'term-rewrite';
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* How safe a fix is to apply without human review.
|
|
118
|
+
*
|
|
119
|
+
* - `safe`: a deterministic, scope-correct source edit `warden --fix` may apply
|
|
120
|
+
* automatically.
|
|
121
|
+
* - `review`: the change is understood but needs human judgement (ambiguous
|
|
122
|
+
* span, removal with no mechanical replacement, semantic follow-up). The
|
|
123
|
+
* finding stays reported; `warden --fix` never applies it.
|
|
124
|
+
*/
|
|
125
|
+
export type WardenFixSafety = 'review' | 'safe';
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A concrete, half-open source edit `[start, end)` replaced by `replacement`.
|
|
129
|
+
*
|
|
130
|
+
* Offsets are JavaScript string indices into the exact analyzed source text,
|
|
131
|
+
* matching `String.prototype.slice()` and the `offsetToLine` helper the rules
|
|
132
|
+
* already use. Carrying an explicit span (not just a line) lets `warden --fix`
|
|
133
|
+
* apply edits deterministically and
|
|
134
|
+
* last-to-first without re-parsing.
|
|
135
|
+
*/
|
|
136
|
+
export interface WardenFixEdit {
|
|
137
|
+
/** Inclusive start offset into the source. */
|
|
138
|
+
readonly start: number;
|
|
139
|
+
/** Exclusive end offset into the source. */
|
|
140
|
+
readonly end: number;
|
|
141
|
+
/** Text that replaces the `[start, end)` span. */
|
|
142
|
+
readonly replacement: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Source targets a fix class can inspect when projected into downstream tools.
|
|
147
|
+
*
|
|
148
|
+
* Warden itself decides which committed files it scans. This metadata is for
|
|
149
|
+
* consumers such as Regrade that need to derive a narrower collection before
|
|
150
|
+
* invoking the rule against an explicit downstream root.
|
|
151
|
+
*/
|
|
152
|
+
export type WardenFixScanTargets = ScanTargets & {
|
|
153
|
+
/**
|
|
154
|
+
* @deprecated Compatibility bridge for Warden-backed Regrade classes that
|
|
155
|
+
* predate PathScope. Prefer Regrade collection scope for new callers.
|
|
156
|
+
*/
|
|
157
|
+
readonly ignoredDirectories?: readonly string[];
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Per-finding fix metadata attached to a diagnostic.
|
|
162
|
+
*
|
|
163
|
+
* Authored on the diagnostic at construction because only the rule that matched
|
|
164
|
+
* knows the concrete span. `warden --fix` applies `edits` only when
|
|
165
|
+
* `safety` is `safe`; `review` fixes stay reported with their guidance so a
|
|
166
|
+
* human (or a downstream regrade) can resolve them.
|
|
167
|
+
*/
|
|
168
|
+
export interface WardenFix {
|
|
169
|
+
/** Transform class this fix belongs to. */
|
|
170
|
+
readonly class: WardenFixClass;
|
|
171
|
+
/** Whether `warden --fix` may apply this automatically. */
|
|
172
|
+
readonly safety: WardenFixSafety;
|
|
173
|
+
/** Source edits to apply, when `safety` is `safe`. Empty for review-only. */
|
|
174
|
+
readonly edits?: readonly WardenFixEdit[] | undefined;
|
|
175
|
+
/** Why the fix is needed and what it changes, for humans and migration notes. */
|
|
176
|
+
readonly reason: string;
|
|
177
|
+
/** Optional pointer to a fixture or example demonstrating the fix. */
|
|
178
|
+
readonly fixture?: string | undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Per-rule fix capability, projected into the guide/manifest.
|
|
183
|
+
*
|
|
184
|
+
* Declares that a rule can emit {@link WardenFix} metadata and the default
|
|
185
|
+
* safety for its fixes, so `warden --help`, the guide, and agent surfaces can
|
|
186
|
+
* advertise fix availability without a finding in hand. Concrete edits still
|
|
187
|
+
* live on each diagnostic's {@link WardenFix}.
|
|
188
|
+
*/
|
|
189
|
+
export interface WardenFixCapability {
|
|
190
|
+
/** Transform class the rule's fixes belong to. */
|
|
191
|
+
readonly class: WardenFixClass;
|
|
192
|
+
/** Default safety for fixes this rule emits. */
|
|
193
|
+
readonly safety: WardenFixSafety;
|
|
194
|
+
/** Downstream scan targets for tools that project this fix capability. */
|
|
195
|
+
readonly scanTargets?: WardenFixScanTargets | undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Stable metadata used to classify Warden rules before dispatch filtering.
|
|
200
|
+
*/
|
|
201
|
+
export interface WardenRuleMetadata {
|
|
202
|
+
/** Cumulative Warden depth where this rule first becomes relevant. */
|
|
203
|
+
readonly depth: WardenDepth;
|
|
204
|
+
/** Queryable rule concern for agent-facing surfaces. */
|
|
205
|
+
readonly concern: WardenRuleConcern;
|
|
206
|
+
/** One-line invariant the rule protects. */
|
|
207
|
+
readonly invariant: string;
|
|
208
|
+
/** Rule lifecycle. */
|
|
209
|
+
readonly lifecycle: WardenRuleLifecycle;
|
|
210
|
+
/** Where the rule applies. */
|
|
211
|
+
readonly scope: WardenRuleScope;
|
|
212
|
+
/** Narrowest Warden tier that can answer the rule. */
|
|
213
|
+
readonly tier: WardenRuleTier;
|
|
214
|
+
/** Structured remediation guidance for diagnostics emitted by this rule. */
|
|
215
|
+
readonly guidance?: WardenGuidance | undefined;
|
|
216
|
+
/** Declares that this rule can emit fix metadata, for guide projection. */
|
|
217
|
+
readonly fix?: WardenFixCapability | undefined;
|
|
218
|
+
}
|
|
5
219
|
|
|
6
220
|
/**
|
|
7
221
|
* A single diagnostic reported by a warden rule.
|
|
8
222
|
*/
|
|
9
|
-
export interface WardenDiagnostic {
|
|
223
|
+
export interface WardenDiagnostic extends RuleDiagnosticBase {
|
|
10
224
|
/** Rule identifier, e.g. "no-throw-in-implementation" */
|
|
11
225
|
readonly rule: string;
|
|
226
|
+
/** Optional rule-local diagnostic code for checks with multiple stable findings. */
|
|
227
|
+
readonly code?: string | undefined;
|
|
12
228
|
/** Severity level */
|
|
13
229
|
readonly severity: WardenSeverity;
|
|
14
230
|
/** Human-readable message describing the violation */
|
|
@@ -17,13 +233,43 @@ export interface WardenDiagnostic {
|
|
|
17
233
|
readonly line: number;
|
|
18
234
|
/** File path that was analyzed */
|
|
19
235
|
readonly filePath: string;
|
|
236
|
+
/** Topo/app identity for diagnostics emitted during multi-topo runs. */
|
|
237
|
+
readonly topoName?: string | undefined;
|
|
238
|
+
/** Optional finding-level guidance. Defaults from rule metadata when absent. */
|
|
239
|
+
readonly guidance?: WardenGuidance | undefined;
|
|
240
|
+
/** Optional structured fix for this finding. `warden --fix` applies safe edits. */
|
|
241
|
+
readonly fix?: WardenFix | undefined;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Public exported symbol observed from a first-party package export target. */
|
|
245
|
+
export interface WardenExportedSymbolDefinition {
|
|
246
|
+
/** Exported binding name. */
|
|
247
|
+
readonly name: string;
|
|
248
|
+
/** Export declaration kind that introduced the binding. */
|
|
249
|
+
readonly kind:
|
|
250
|
+
| 'class'
|
|
251
|
+
| 'const'
|
|
252
|
+
| 'enum'
|
|
253
|
+
| 'export'
|
|
254
|
+
| 'function'
|
|
255
|
+
| 'interface'
|
|
256
|
+
| 'type';
|
|
257
|
+
/** Absolute or runner-provided file path containing the definition. */
|
|
258
|
+
readonly filePath: string;
|
|
259
|
+
/** 1-based source line where the exported definition starts. */
|
|
260
|
+
readonly line: number;
|
|
261
|
+
/** First-party workspace package that owns the definition. */
|
|
262
|
+
readonly workspaceName: string;
|
|
263
|
+
/** Filesystem root of the owning workspace. */
|
|
264
|
+
readonly workspaceRoot: string;
|
|
20
265
|
}
|
|
21
266
|
|
|
22
267
|
/**
|
|
23
|
-
* A warden rule
|
|
268
|
+
* A warden rule analyzes one source file and returns diagnostics.
|
|
24
269
|
*
|
|
25
|
-
* Rules
|
|
26
|
-
*
|
|
270
|
+
* Rules should prefer structured AST helpers when they inspect TypeScript
|
|
271
|
+
* syntax. Simple string checks remain acceptable when the authored rule is
|
|
272
|
+
* explicitly about text that is not parseable syntax, such as generated output.
|
|
27
273
|
*/
|
|
28
274
|
export interface WardenRule {
|
|
29
275
|
/** Unique rule identifier */
|
|
@@ -32,6 +278,8 @@ export interface WardenRule {
|
|
|
32
278
|
readonly severity: WardenSeverity;
|
|
33
279
|
/** Human-readable description of what the rule enforces */
|
|
34
280
|
readonly description: string;
|
|
281
|
+
/** Optional inline classification. Built-ins are classified by registry. */
|
|
282
|
+
readonly metadata?: WardenRuleMetadata | undefined;
|
|
35
283
|
/** Run the rule against source code and return any diagnostics */
|
|
36
284
|
readonly check: (
|
|
37
285
|
sourceCode: string,
|
|
@@ -40,13 +288,84 @@ export interface WardenRule {
|
|
|
40
288
|
}
|
|
41
289
|
|
|
42
290
|
/**
|
|
43
|
-
*
|
|
291
|
+
* One app's authored `surfaces` overlay `mcp` bindings plus the trail ids
|
|
292
|
+
* that scope which source files the bindings can be compared against.
|
|
293
|
+
*/
|
|
294
|
+
export interface AuthoredMcpSurfaceBindingSet {
|
|
295
|
+
/** Stable app/topo label the bindings were authored for. */
|
|
296
|
+
readonly appName: string;
|
|
297
|
+
/** The `mcp` bindings from the app's `surfaces` overlay. */
|
|
298
|
+
readonly bindings: SurfaceBindings;
|
|
299
|
+
/** Trail ids registered in the owning app's topo. */
|
|
300
|
+
readonly trailIds: readonly string[];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Options for compose-file rules that need knowledge of all trail IDs in a project.
|
|
44
305
|
*/
|
|
45
306
|
export interface ProjectContext {
|
|
307
|
+
/**
|
|
308
|
+
* Per-app authored `mcp` surface bindings, resolved from the project's
|
|
309
|
+
* topo targets (the serialized graph overlays when available, else the
|
|
310
|
+
* app-module overlay registrations). Each set carries the owning app's
|
|
311
|
+
* trail ids so source rules can attribute a call-site surface option to
|
|
312
|
+
* the app whose overlay authored the default before comparing.
|
|
313
|
+
*/
|
|
314
|
+
readonly authoredMcpSurfaceBindingSets?:
|
|
315
|
+
| readonly AuthoredMcpSurfaceBindingSet[]
|
|
316
|
+
| undefined;
|
|
317
|
+
/** All known entity names in the project. */
|
|
318
|
+
readonly knownEntityIds?: ReadonlySet<string>;
|
|
319
|
+
/** Store table IDs used with the CRUD factory across the project. */
|
|
320
|
+
readonly crudTableIds?: ReadonlySet<string>;
|
|
46
321
|
/** All known trail IDs in the project */
|
|
47
322
|
readonly knownTrailIds: ReadonlySet<string>;
|
|
48
|
-
/**
|
|
49
|
-
readonly
|
|
323
|
+
/** Trail IDs registered in configured app topo targets. */
|
|
324
|
+
readonly topoTrailIds?: ReadonlySet<string>;
|
|
325
|
+
/** Declared entity references keyed by source entity name. */
|
|
326
|
+
readonly entityReferencesByName?: ReadonlyMap<string, readonly string[]>;
|
|
327
|
+
/** All known resource IDs in the project */
|
|
328
|
+
readonly knownResourceIds?: ReadonlySet<string>;
|
|
329
|
+
/** All known signal IDs in the project */
|
|
330
|
+
readonly knownSignalIds?: ReadonlySet<string>;
|
|
331
|
+
/** All trail IDs referenced by declared composes arrays across the project. */
|
|
332
|
+
readonly composeTargetTrailIds?: ReadonlySet<string>;
|
|
333
|
+
/** Signal IDs referenced by trail `on` arrays across the project. */
|
|
334
|
+
readonly onTargetSignalIds?: ReadonlySet<string>;
|
|
335
|
+
/** Store table IDs used with reconcile trails across the project. */
|
|
336
|
+
readonly reconcileTableIds?: ReadonlySet<string>;
|
|
337
|
+
/** Resolved import facts keyed by importer file path across the project. */
|
|
338
|
+
readonly importResolutionsByFile?: ReadonlyMap<
|
|
339
|
+
string,
|
|
340
|
+
readonly WardenImportResolution[]
|
|
341
|
+
>;
|
|
342
|
+
/** Resolved docs/specifier facts keyed by documentation file path. */
|
|
343
|
+
readonly documentedImportResolutionsByFile?: ReadonlyMap<
|
|
344
|
+
string,
|
|
345
|
+
readonly WardenImportResolution[]
|
|
346
|
+
>;
|
|
347
|
+
/** Non-private published @ontrails workspaces discovered from the root manifest. */
|
|
348
|
+
readonly publicWorkspaces?: ReadonlyMap<string, WardenPublicWorkspace>;
|
|
349
|
+
/** Public package export definitions grouped by exported name. */
|
|
350
|
+
readonly exportedSymbolDefinitionsByName?: ReadonlyMap<
|
|
351
|
+
string,
|
|
352
|
+
readonly WardenExportedSymbolDefinition[]
|
|
353
|
+
>;
|
|
354
|
+
/** Normalized trail intents by trail ID across the project. */
|
|
355
|
+
readonly trailIntentsById?: ReadonlyMap<string, Intent>;
|
|
356
|
+
/**
|
|
357
|
+
* CRUD operation coverage per entity aggregated across the project.
|
|
358
|
+
*
|
|
359
|
+
* Keys are stable entity IDs (authored entity names, `imported:<local>`
|
|
360
|
+
* sentinels for entities imported from another module, or store-table IDs
|
|
361
|
+
* produced by `deriveStoreTableId`). Values are the set of CRUD operations
|
|
362
|
+
* (`create`, `read`, `update`, `delete`, `list`) observed for that entity.
|
|
363
|
+
*
|
|
364
|
+
* Enables compose-file completeness evaluation so one-file-per-operation
|
|
365
|
+
* layouts (e.g. separate `create.ts`, `read.ts`) do not trip file-scoped
|
|
366
|
+
* coverage warnings.
|
|
367
|
+
*/
|
|
368
|
+
readonly crudCoverageByEntity?: ReadonlyMap<string, ReadonlySet<string>>;
|
|
50
369
|
}
|
|
51
370
|
|
|
52
371
|
/**
|
|
@@ -60,3 +379,30 @@ export interface ProjectAwareWardenRule extends WardenRule {
|
|
|
60
379
|
context: ProjectContext
|
|
61
380
|
) => readonly WardenDiagnostic[];
|
|
62
381
|
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* A topo-aware warden rule inspects the compiled runtime trail graph —
|
|
385
|
+
* actual `Trail` objects with resolved types, accessor shapes, detour
|
|
386
|
+
* declarations, `pattern` field values, and other runtime-only data that
|
|
387
|
+
* is unavailable to AST-based rules.
|
|
388
|
+
*
|
|
389
|
+
* Unlike `WardenRule` and `ProjectAwareWardenRule`, which analyze source
|
|
390
|
+
* code on a per-file basis, a `TopoAwareWardenRule` runs once per topo
|
|
391
|
+
* and returns diagnostics spanning the whole graph. A rule file must
|
|
392
|
+
* implement exactly one of the three rule kinds.
|
|
393
|
+
*/
|
|
394
|
+
export interface TopoAwareWardenRule {
|
|
395
|
+
/** Unique rule identifier */
|
|
396
|
+
readonly name: string;
|
|
397
|
+
/** Default severity */
|
|
398
|
+
readonly severity: WardenSeverity;
|
|
399
|
+
/** Human-readable description of what the rule enforces */
|
|
400
|
+
readonly description: string;
|
|
401
|
+
/** Optional inline classification. Built-ins are classified by registry. */
|
|
402
|
+
readonly metadata?: WardenRuleMetadata | undefined;
|
|
403
|
+
/** Run the rule against the resolved topo and return any diagnostics */
|
|
404
|
+
readonly checkTopo: (
|
|
405
|
+
topo: Topo,
|
|
406
|
+
context?: { readonly graph?: TopoGraph | undefined } | undefined
|
|
407
|
+
) => readonly WardenDiagnostic[] | Promise<readonly WardenDiagnostic[]>;
|
|
408
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ActivationSourceKind, Topo } from '@ontrails/core';
|
|
2
|
+
|
|
3
|
+
import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
|
|
4
|
+
|
|
5
|
+
const RULE_NAME = 'unmaterialized-activation-source';
|
|
6
|
+
const TOPO_FILE = '<topo>';
|
|
7
|
+
|
|
8
|
+
const MATERIALIZED_SOURCE_KINDS = new Set<ActivationSourceKind>([
|
|
9
|
+
'queue',
|
|
10
|
+
'schedule',
|
|
11
|
+
'signal',
|
|
12
|
+
'webhook',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const PENDING_SOURCE_KINDS = new Set<ActivationSourceKind>();
|
|
16
|
+
|
|
17
|
+
interface SourceConsumers {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly kind: ActivationSourceKind;
|
|
20
|
+
readonly key: string;
|
|
21
|
+
readonly trailIds: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sourceKey = (kind: ActivationSourceKind, id: string): string =>
|
|
25
|
+
`${kind}:${id}`;
|
|
26
|
+
|
|
27
|
+
const sortedUnique = (values: Iterable<string>): readonly string[] =>
|
|
28
|
+
[...new Set(values)].toSorted();
|
|
29
|
+
|
|
30
|
+
const collectSourceConsumers = (topo: Topo): readonly SourceConsumers[] => {
|
|
31
|
+
const consumersBySource = new Map<
|
|
32
|
+
string,
|
|
33
|
+
{
|
|
34
|
+
readonly id: string;
|
|
35
|
+
readonly kind: ActivationSourceKind;
|
|
36
|
+
readonly trailIds: Set<string>;
|
|
37
|
+
}
|
|
38
|
+
>();
|
|
39
|
+
|
|
40
|
+
for (const trail of topo.list()) {
|
|
41
|
+
for (const activation of trail.activationSources) {
|
|
42
|
+
const key = sourceKey(activation.source.kind, activation.source.id);
|
|
43
|
+
const current =
|
|
44
|
+
consumersBySource.get(key) ??
|
|
45
|
+
({
|
|
46
|
+
id: activation.source.id,
|
|
47
|
+
kind: activation.source.kind,
|
|
48
|
+
trailIds: new Set<string>(),
|
|
49
|
+
} as const);
|
|
50
|
+
current.trailIds.add(trail.id);
|
|
51
|
+
consumersBySource.set(key, current);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return [...consumersBySource.entries()]
|
|
56
|
+
.map(([key, source]) => ({
|
|
57
|
+
id: source.id,
|
|
58
|
+
key,
|
|
59
|
+
kind: source.kind,
|
|
60
|
+
trailIds: sortedUnique(source.trailIds),
|
|
61
|
+
}))
|
|
62
|
+
.toSorted((a, b) => a.key.localeCompare(b.key));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const isUnmaterialized = (kind: ActivationSourceKind): boolean =>
|
|
66
|
+
PENDING_SOURCE_KINDS.has(kind) && !MATERIALIZED_SOURCE_KINDS.has(kind);
|
|
67
|
+
|
|
68
|
+
const buildDiagnostic = (source: SourceConsumers): WardenDiagnostic => ({
|
|
69
|
+
filePath: TOPO_FILE,
|
|
70
|
+
line: 1,
|
|
71
|
+
message: `Activation source "${source.id}" of kind "${source.kind}" activates trail${source.trailIds.length === 1 ? '' : 's'} ${source.trailIds.map((id) => `"${id}"`).join(', ')} but no built-in materializer is available in this stack. Add the materializer before relying on runtime delivery, or defer the source declaration until the materializer lands.`,
|
|
72
|
+
rule: RULE_NAME,
|
|
73
|
+
severity: 'warn',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
export const unmaterializedActivationSource: TopoAwareWardenRule = {
|
|
77
|
+
checkTopo: (topo) =>
|
|
78
|
+
collectSourceConsumers(topo)
|
|
79
|
+
.filter((source) => isUnmaterialized(source.kind))
|
|
80
|
+
.map((source) => buildDiagnostic(source)),
|
|
81
|
+
description:
|
|
82
|
+
'Warn when declared activation sources do not have an available runtime materializer.',
|
|
83
|
+
name: RULE_NAME,
|
|
84
|
+
severity: 'warn',
|
|
85
|
+
};
|