@ontrails/warden 0.2.0

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.
Files changed (188) hide show
  1. package/CHANGELOG.md +1556 -0
  2. package/README.md +200 -0
  3. package/bin/warden.ts +51 -0
  4. package/package.json +51 -0
  5. package/src/adapter-check.ts +136 -0
  6. package/src/cli.ts +2159 -0
  7. package/src/command.ts +1167 -0
  8. package/src/config.ts +193 -0
  9. package/src/draft.ts +22 -0
  10. package/src/drift.ts +260 -0
  11. package/src/fix.ts +126 -0
  12. package/src/formatters.ts +178 -0
  13. package/src/guide.ts +245 -0
  14. package/src/index.ts +295 -0
  15. package/src/project-context.ts +446 -0
  16. package/src/project-rules.ts +290 -0
  17. package/src/regrade-history.ts +188 -0
  18. package/src/regrade-receipt.ts +484 -0
  19. package/src/resolve.ts +531 -0
  20. package/src/rules/activation-orphan.ts +97 -0
  21. package/src/rules/captured-kernel.ts +375 -0
  22. package/src/rules/circular-refs.ts +150 -0
  23. package/src/rules/cli-command-route-coherence.ts +177 -0
  24. package/src/rules/composes-declarations.ts +839 -0
  25. package/src/rules/context-no-surface-types.ts +214 -0
  26. package/src/rules/dead-internal-trail.ts +161 -0
  27. package/src/rules/dead-public-trail.ts +258 -0
  28. package/src/rules/draft-file-marking.ts +155 -0
  29. package/src/rules/draft-visible-debt.ts +83 -0
  30. package/src/rules/duplicate-exported-symbol.ts +172 -0
  31. package/src/rules/duplicate-public-contract.ts +137 -0
  32. package/src/rules/entity-exists.ts +254 -0
  33. package/src/rules/entity-ids.ts +15 -0
  34. package/src/rules/error-mapping-completeness.ts +290 -0
  35. package/src/rules/example-valid.ts +395 -0
  36. package/src/rules/fires-declarations.ts +740 -0
  37. package/src/rules/governed-symbol-residue.ts +535 -0
  38. package/src/rules/governed-vocabulary-permutation-watch.ts +77 -0
  39. package/src/rules/implementation-returns-result.ts +1733 -0
  40. package/src/rules/incomplete-accessor-for-standard-op.ts +272 -0
  41. package/src/rules/incomplete-crud.ts +583 -0
  42. package/src/rules/index.ts +328 -0
  43. package/src/rules/intent-propagation.ts +125 -0
  44. package/src/rules/layer-field-name-drift.ts +102 -0
  45. package/src/rules/library-render-coherence.ts +97 -0
  46. package/src/rules/metadata.ts +929 -0
  47. package/src/rules/missing-reconcile.ts +97 -0
  48. package/src/rules/missing-visibility.ts +111 -0
  49. package/src/rules/no-destructured-compose.ts +196 -0
  50. package/src/rules/no-dev-permit-in-source.ts +99 -0
  51. package/src/rules/no-direct-implementation-call.ts +52 -0
  52. package/src/rules/no-legacy-cli-alias-export.ts +247 -0
  53. package/src/rules/no-legacy-layer-imports.ts +211 -0
  54. package/src/rules/no-native-error-result.ts +118 -0
  55. package/src/rules/no-redundant-result-error-wrap.ts +382 -0
  56. package/src/rules/no-retired-cross-vocabulary.ts +203 -0
  57. package/src/rules/no-sync-result-assumption.ts +1199 -0
  58. package/src/rules/no-throw-in-detour-recover.ts +225 -0
  59. package/src/rules/no-throw-in-implementation.ts +48 -0
  60. package/src/rules/no-top-level-surface.ts +371 -0
  61. package/src/rules/on-references-exist.ts +194 -0
  62. package/src/rules/orphaned-signal.ts +149 -0
  63. package/src/rules/owner-render-parity.ts +143 -0
  64. package/src/rules/permit-governance.ts +25 -0
  65. package/src/rules/prefer-schema-inference.ts +141 -0
  66. package/src/rules/public-export-example-coverage.ts +561 -0
  67. package/src/rules/public-internal-deep-imports.ts +454 -0
  68. package/src/rules/public-output-schema.ts +29 -0
  69. package/src/rules/public-union-output-discriminants.ts +150 -0
  70. package/src/rules/read-intent-fires.ts +188 -0
  71. package/src/rules/reference-exists.ts +97 -0
  72. package/src/rules/registry-names.ts +171 -0
  73. package/src/rules/resolved-import-boundary.ts +146 -0
  74. package/src/rules/resource-declarations.ts +697 -0
  75. package/src/rules/resource-exists.ts +181 -0
  76. package/src/rules/resource-id-grammar.ts +65 -0
  77. package/src/rules/resource-mock-coverage.ts +115 -0
  78. package/src/rules/retired-vocabulary.ts +1407 -0
  79. package/src/rules/scan.ts +59 -0
  80. package/src/rules/scheduled-destroy-intent.ts +44 -0
  81. package/src/rules/signal-graph-coaching.ts +220 -0
  82. package/src/rules/source/composition.ts +165 -0
  83. package/src/rules/source/drafts.ts +164 -0
  84. package/src/rules/source/entities.ts +618 -0
  85. package/src/rules/source/pragmas.ts +45 -0
  86. package/src/rules/source/resources.ts +64 -0
  87. package/src/rules/source/signals.ts +397 -0
  88. package/src/rules/source/stores.ts +310 -0
  89. package/src/rules/specs.ts +388 -0
  90. package/src/rules/static-resource-accessor-preference.ts +654 -0
  91. package/src/rules/structure.ts +234 -0
  92. package/src/rules/surface-overlay-coherence.ts +262 -0
  93. package/src/rules/surface-trailhead-coherence.ts +366 -0
  94. package/src/rules/trail-fork-coaching.ts +625 -0
  95. package/src/rules/trail-versioning-source.ts +1076 -0
  96. package/src/rules/trail-versioning-topo.ts +172 -0
  97. package/src/rules/trailhead-override-divergence.ts +356 -0
  98. package/src/rules/types.ts +464 -0
  99. package/src/rules/unmaterialized-activation-source.ts +85 -0
  100. package/src/rules/unreachable-detour-shadowing.ts +343 -0
  101. package/src/rules/valid-describe-refs.ts +224 -0
  102. package/src/rules/valid-detour-contract.ts +78 -0
  103. package/src/rules/warden-export-symmetry.ts +540 -0
  104. package/src/rules/warden-rules-use-ast.ts +1109 -0
  105. package/src/rules/webhook-route-collision.ts +306 -0
  106. package/src/rules/workspace-lock-ownership.ts +34 -0
  107. package/src/trails/activation-orphan.trail.ts +84 -0
  108. package/src/trails/captured-kernel.trail.ts +108 -0
  109. package/src/trails/circular-refs.trail.ts +29 -0
  110. package/src/trails/cli-command-route-coherence.trail.ts +47 -0
  111. package/src/trails/composes-declarations.trail.ts +22 -0
  112. package/src/trails/context-no-surface-types.trail.ts +21 -0
  113. package/src/trails/dead-internal-trail.trail.ts +26 -0
  114. package/src/trails/dead-public-trail.trail.ts +31 -0
  115. package/src/trails/deprecation-without-guidance.trail.ts +21 -0
  116. package/src/trails/draft-file-marking.trail.ts +16 -0
  117. package/src/trails/draft-visible-debt.trail.ts +16 -0
  118. package/src/trails/duplicate-exported-symbol.trail.ts +48 -0
  119. package/src/trails/duplicate-public-contract.trail.ts +47 -0
  120. package/src/trails/entity-exists.trail.ts +21 -0
  121. package/src/trails/error-mapping-completeness.trail.ts +30 -0
  122. package/src/trails/example-valid.trail.ts +25 -0
  123. package/src/trails/fires-declarations.trail.ts +23 -0
  124. package/src/trails/fork-without-preserved-implementation.trail.ts +31 -0
  125. package/src/trails/governed-symbol-residue.trail.ts +24 -0
  126. package/src/trails/governed-vocabulary-permutation-watch.trail.ts +16 -0
  127. package/src/trails/implementation-returns-result.trail.ts +20 -0
  128. package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
  129. package/src/trails/incomplete-crud.trail.ts +39 -0
  130. package/src/trails/index.ts +91 -0
  131. package/src/trails/intent-propagation.trail.ts +30 -0
  132. package/src/trails/layer-field-name-drift.trail.ts +39 -0
  133. package/src/trails/library-render-coherence.trail.ts +43 -0
  134. package/src/trails/marker-schema-unsupported.trail.ts +23 -0
  135. package/src/trails/missing-reconcile.trail.ts +33 -0
  136. package/src/trails/missing-visibility.trail.ts +22 -0
  137. package/src/trails/no-destructured-compose.trail.ts +44 -0
  138. package/src/trails/no-dev-permit-in-source.trail.ts +16 -0
  139. package/src/trails/no-direct-implementation-call.trail.ts +16 -0
  140. package/src/trails/no-legacy-cli-alias-export.trail.ts +41 -0
  141. package/src/trails/no-legacy-layer-imports.trail.ts +41 -0
  142. package/src/trails/no-native-error-result.trail.ts +18 -0
  143. package/src/trails/no-redundant-result-error-wrap.trail.ts +55 -0
  144. package/src/trails/no-retired-cross-vocabulary.trail.ts +42 -0
  145. package/src/trails/no-sync-result-assumption.trail.ts +19 -0
  146. package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
  147. package/src/trails/no-throw-in-implementation.trail.ts +20 -0
  148. package/src/trails/no-top-level-surface.trail.ts +43 -0
  149. package/src/trails/on-references-exist.trail.ts +21 -0
  150. package/src/trails/orphaned-signal.trail.ts +36 -0
  151. package/src/trails/owner-render-parity.trail.ts +26 -0
  152. package/src/trails/pending-force.trail.ts +21 -0
  153. package/src/trails/permit-governance.trail.ts +51 -0
  154. package/src/trails/prefer-schema-inference.trail.ts +21 -0
  155. package/src/trails/public-export-example-coverage.trail.ts +16 -0
  156. package/src/trails/public-internal-deep-imports.trail.ts +94 -0
  157. package/src/trails/public-output-schema.trail.ts +55 -0
  158. package/src/trails/public-union-output-discriminants.trail.ts +33 -0
  159. package/src/trails/read-intent-fires.trail.ts +20 -0
  160. package/src/trails/reference-exists.trail.ts +25 -0
  161. package/src/trails/resolved-import-boundary.trail.ts +109 -0
  162. package/src/trails/resource-declarations.trail.ts +25 -0
  163. package/src/trails/resource-exists.trail.ts +27 -0
  164. package/src/trails/resource-id-grammar.trail.ts +39 -0
  165. package/src/trails/resource-mock-coverage.trail.ts +40 -0
  166. package/src/trails/run.ts +205 -0
  167. package/src/trails/scheduled-destroy-intent.trail.ts +56 -0
  168. package/src/trails/schema.ts +295 -0
  169. package/src/trails/signal-graph-coaching.trail.ts +77 -0
  170. package/src/trails/static-resource-accessor-preference.trail.ts +25 -0
  171. package/src/trails/surface-overlay-coherence.trail.ts +24 -0
  172. package/src/trails/surface-trailhead-coherence.trail.ts +25 -0
  173. package/src/trails/topo.ts +6 -0
  174. package/src/trails/trail-fork-coaching.trail.ts +42 -0
  175. package/src/trails/trailhead-override-divergence.trail.ts +47 -0
  176. package/src/trails/unmaterialized-activation-source.trail.ts +72 -0
  177. package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
  178. package/src/trails/valid-describe-refs.trail.ts +18 -0
  179. package/src/trails/valid-detour-contract.trail.ts +71 -0
  180. package/src/trails/version-gap.trail.ts +35 -0
  181. package/src/trails/version-pinned-compose.trail.ts +23 -0
  182. package/src/trails/version-without-examples.trail.ts +38 -0
  183. package/src/trails/warden-export-symmetry.trail.ts +16 -0
  184. package/src/trails/warden-rules-use-ast.trail.ts +64 -0
  185. package/src/trails/webhook-route-collision.trail.ts +50 -0
  186. package/src/trails/workspace-lock-ownership.trail.ts +16 -0
  187. package/src/trails/wrap-rule.ts +269 -0
  188. package/src/workspaces.ts +264 -0
@@ -0,0 +1,328 @@
1
+ import { activationOrphan } from './activation-orphan.js';
2
+ import { capturedKernel } from './captured-kernel.js';
3
+ import { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
4
+ import { circularRefs } from './circular-refs.js';
5
+ import { entityExists } from './entity-exists.js';
6
+ import { contextNoSurfaceTypes } from './context-no-surface-types.js';
7
+ import { composesDeclarations } from './composes-declarations.js';
8
+ import { deadInternalTrail } from './dead-internal-trail.js';
9
+ import { deadPublicTrail } from './dead-public-trail.js';
10
+ import { draftFileMarking } from './draft-file-marking.js';
11
+ import { draftVisibleDebt } from './draft-visible-debt.js';
12
+ import { duplicateExportedSymbol } from './duplicate-exported-symbol.js';
13
+ import { duplicatePublicContract } from './duplicate-public-contract.js';
14
+ import { errorMappingCompleteness } from './error-mapping-completeness.js';
15
+ import { exampleValid } from './example-valid.js';
16
+ import { firesDeclarations } from './fires-declarations.js';
17
+ import { governedSymbolResidue } from './governed-symbol-residue.js';
18
+ import { governedVocabularyPermutationWatch } from './governed-vocabulary-permutation-watch.js';
19
+ import { implementationReturnsResult } from './implementation-returns-result.js';
20
+ import { incompleteAccessorForStandardOp } from './incomplete-accessor-for-standard-op.js';
21
+ import { incompleteCrud } from './incomplete-crud.js';
22
+ import { intentPropagation } from './intent-propagation.js';
23
+ import { layerFieldNameDrift } from './layer-field-name-drift.js';
24
+ import { libraryRenderCoherence } from './library-render-coherence.js';
25
+ import { missingVisibility } from './missing-visibility.js';
26
+ import { missingReconcile } from './missing-reconcile.js';
27
+ import { noDevPermitInSource } from './no-dev-permit-in-source.js';
28
+ import { noDestructuredCompose } from './no-destructured-compose.js';
29
+ import { noLegacyCliAliasExport } from './no-legacy-cli-alias-export.js';
30
+ import { noLegacyLayerImports } from './no-legacy-layer-imports.js';
31
+ import { noDirectImplementationCall } from './no-direct-implementation-call.js';
32
+ import { noNativeErrorResult } from './no-native-error-result.js';
33
+ import { noRedundantResultErrorWrap } from './no-redundant-result-error-wrap.js';
34
+ import { noRetiredCrossVocabulary } from './no-retired-cross-vocabulary.js';
35
+ import { noSyncResultAssumption } from './no-sync-result-assumption.js';
36
+ import { noThrowInDetourRecover } from './no-throw-in-detour-recover.js';
37
+ import { noThrowInImplementation } from './no-throw-in-implementation.js';
38
+ import { noTopLevelSurface } from './no-top-level-surface.js';
39
+ import { onReferencesExist } from './on-references-exist.js';
40
+ import { orphanedSignal } from './orphaned-signal.js';
41
+ import { ownerRenderParity } from './owner-render-parity.js';
42
+ import { permitGovernance } from './permit-governance.js';
43
+ import { preferSchemaInference } from './prefer-schema-inference.js';
44
+ import { publicExportExampleCoverage } from './public-export-example-coverage.js';
45
+ import { publicInternalDeepImports } from './public-internal-deep-imports.js';
46
+ import { publicOutputSchema } from './public-output-schema.js';
47
+ import { publicUnionOutputDiscriminants } from './public-union-output-discriminants.js';
48
+ import { readIntentFires } from './read-intent-fires.js';
49
+ import { referenceExists } from './reference-exists.js';
50
+ import { resolvedImportBoundary } from './resolved-import-boundary.js';
51
+ import { resourceDeclarations } from './resource-declarations.js';
52
+ import { resourceExists } from './resource-exists.js';
53
+ import { resourceIdGrammar } from './resource-id-grammar.js';
54
+ import { resourceMockCoverage } from './resource-mock-coverage.js';
55
+ import { scheduledDestroyIntent } from './scheduled-destroy-intent.js';
56
+ import { signalGraphCoaching } from './signal-graph-coaching.js';
57
+ import { staticResourceAccessorPreference } from './static-resource-accessor-preference.js';
58
+ import { surfaceOverlayCoherence } from './surface-overlay-coherence.js';
59
+ import { surfaceTrailheadCoherence } from './surface-trailhead-coherence.js';
60
+ import { trailForkCoaching } from './trail-fork-coaching.js';
61
+ import { trailheadOverrideDivergence } from './trailhead-override-divergence.js';
62
+ import {
63
+ forkWithoutPreservedImplementation,
64
+ markerSchemaUnsupported,
65
+ versionPinnedCompose,
66
+ } from './trail-versioning-source.js';
67
+ import {
68
+ deprecationWithoutGuidance,
69
+ pendingForce,
70
+ versionGap,
71
+ versionWithoutExamples,
72
+ } from './trail-versioning-topo.js';
73
+ import type { TopoAwareWardenRule, WardenRule } from './types.js';
74
+ import { unmaterializedActivationSource } from './unmaterialized-activation-source.js';
75
+ import { unreachableDetourShadowing } from './unreachable-detour-shadowing.js';
76
+ import { validDetourContract } from './valid-detour-contract.js';
77
+ import { validDescribeRefs } from './valid-describe-refs.js';
78
+ import { wardenExportSymmetry } from './warden-export-symmetry.js';
79
+ import { wardenRulesUseAst } from './warden-rules-use-ast.js';
80
+ import { webhookRouteCollision } from './webhook-route-collision.js';
81
+ import { workspaceLockOwnership } from './workspace-lock-ownership.js';
82
+
83
+ export type {
84
+ GovernedVocabularyHistoryEvidence,
85
+ GovernedVocabularyHistoryFormJudgment,
86
+ GovernedVocabularyHistoryIssue,
87
+ WardenFix,
88
+ WardenFixCapability,
89
+ WardenFixClass,
90
+ WardenFixEdit,
91
+ WardenFixScanTargets,
92
+ WardenFixSafety,
93
+ WardenGuidance,
94
+ WardenGuidanceLink,
95
+ WardenExportedSymbolDefinition,
96
+ WardenRuleLifecycle,
97
+ WardenRuleLifecycleState,
98
+ WardenRuleConcern,
99
+ WardenRuleMetadata,
100
+ WardenRuleScope,
101
+ ProjectAwareWardenRule,
102
+ ProjectContext,
103
+ TopoAwareWardenRule,
104
+ WardenDiagnostic,
105
+ WardenRule,
106
+ WardenRuleTier,
107
+ WardenSeverity,
108
+ } from './types.js';
109
+
110
+ export {
111
+ formatGovernedVocabularyTransitionGuide,
112
+ getGovernedVocabularyTransition,
113
+ governedVocabularyLiteralRenameSchema,
114
+ governedVocabularyFileRenameSchema,
115
+ governedVocabularyHistoryProvenanceSchema,
116
+ governedVocabularyPreserveRuleSchema,
117
+ governedVocabularyProvenancePolicySchema,
118
+ governedVocabularyRegistrySchema,
119
+ governedVocabularyScopeSchema,
120
+ governedVocabularySymbolRenameSchema,
121
+ governedVocabularyTargetSchema,
122
+ governedVocabularyTransitionSchema,
123
+ governedVocabularyTransitionStatuses,
124
+ governedVocabularyTransitions,
125
+ listGovernedVocabularyTransitions,
126
+ requireGovernedVocabularyTransition,
127
+ } from './retired-vocabulary.js';
128
+ export type {
129
+ GovernedVocabularyLiteralRename,
130
+ GovernedVocabularyHistoryProvenance,
131
+ GovernedVocabularyPreserveRule,
132
+ GovernedVocabularyProvenancePolicy,
133
+ GovernedVocabularyScope,
134
+ GovernedVocabularySymbolRename,
135
+ GovernedVocabularyTarget,
136
+ GovernedVocabularyTransition,
137
+ GovernedVocabularyTransitionInput,
138
+ } from './retired-vocabulary.js';
139
+ export {
140
+ builtinWardenRuleMetadata,
141
+ getWardenRuleMetadata,
142
+ listWardenRuleMetadata,
143
+ wardenFixClasses,
144
+ wardenFixSafeties,
145
+ wardenRuleConcerns,
146
+ wardenRuleLifecycleStates,
147
+ wardenRuleScopes,
148
+ wardenRuleTiers,
149
+ } from './metadata.js';
150
+ export type { BuiltinWardenRuleName } from './metadata.js';
151
+
152
+ export { activationOrphan } from './activation-orphan.js';
153
+ export { capturedKernel } from './captured-kernel.js';
154
+ export { cliCommandRouteCoherence } from './cli-command-route-coherence.js';
155
+ export { noThrowInImplementation } from './no-throw-in-implementation.js';
156
+ export { circularRefs } from './circular-refs.js';
157
+ export { entityExists } from './entity-exists.js';
158
+ export { contextNoSurfaceTypes } from './context-no-surface-types.js';
159
+ export { composesDeclarations } from './composes-declarations.js';
160
+ export { deadInternalTrail } from './dead-internal-trail.js';
161
+ export { deadPublicTrail } from './dead-public-trail.js';
162
+ export { draftFileMarking } from './draft-file-marking.js';
163
+ export { draftVisibleDebt } from './draft-visible-debt.js';
164
+ export { duplicateExportedSymbol } from './duplicate-exported-symbol.js';
165
+ export { duplicatePublicContract } from './duplicate-public-contract.js';
166
+ export { errorMappingCompleteness } from './error-mapping-completeness.js';
167
+ export { exampleValid } from './example-valid.js';
168
+ export { firesDeclarations } from './fires-declarations.js';
169
+ export { governedSymbolResidue } from './governed-symbol-residue.js';
170
+ export { governedVocabularyPermutationWatch } from './governed-vocabulary-permutation-watch.js';
171
+ export { incompleteAccessorForStandardOp } from './incomplete-accessor-for-standard-op.js';
172
+ export { incompleteCrud } from './incomplete-crud.js';
173
+ export { intentPropagation } from './intent-propagation.js';
174
+ export { libraryRenderCoherence } from './library-render-coherence.js';
175
+ export { layerFieldNameDrift } from './layer-field-name-drift.js';
176
+ export { missingVisibility } from './missing-visibility.js';
177
+ export { missingReconcile } from './missing-reconcile.js';
178
+ export { onReferencesExist } from './on-references-exist.js';
179
+ export { noDevPermitInSource } from './no-dev-permit-in-source.js';
180
+ export { noDestructuredCompose } from './no-destructured-compose.js';
181
+ export { noLegacyCliAliasExport } from './no-legacy-cli-alias-export.js';
182
+ export { noLegacyLayerImports } from './no-legacy-layer-imports.js';
183
+ export { noDirectImplementationCall } from './no-direct-implementation-call.js';
184
+ export { noNativeErrorResult } from './no-native-error-result.js';
185
+ export { noRedundantResultErrorWrap } from './no-redundant-result-error-wrap.js';
186
+ export { noRetiredCrossVocabulary } from './no-retired-cross-vocabulary.js';
187
+ export { noSyncResultAssumption } from './no-sync-result-assumption.js';
188
+ export { implementationReturnsResult } from './implementation-returns-result.js';
189
+ export { noThrowInDetourRecover } from './no-throw-in-detour-recover.js';
190
+ export { noTopLevelSurface } from './no-top-level-surface.js';
191
+ export { orphanedSignal } from './orphaned-signal.js';
192
+ export { ownerRenderParity } from './owner-render-parity.js';
193
+ export { permitGovernance } from './permit-governance.js';
194
+ export { preferSchemaInference } from './prefer-schema-inference.js';
195
+ export { publicInternalDeepImports } from './public-internal-deep-imports.js';
196
+ export { publicOutputSchema } from './public-output-schema.js';
197
+ export { publicUnionOutputDiscriminants } from './public-union-output-discriminants.js';
198
+ export { readIntentFires } from './read-intent-fires.js';
199
+ export { referenceExists } from './reference-exists.js';
200
+ export { resolvedImportBoundary } from './resolved-import-boundary.js';
201
+ export { resourceDeclarations } from './resource-declarations.js';
202
+ export { resourceExists } from './resource-exists.js';
203
+ export { resourceIdGrammar } from './resource-id-grammar.js';
204
+ export { resourceMockCoverage } from './resource-mock-coverage.js';
205
+ export { scheduledDestroyIntent } from './scheduled-destroy-intent.js';
206
+ export { signalGraphCoaching } from './signal-graph-coaching.js';
207
+ export { staticResourceAccessorPreference } from './static-resource-accessor-preference.js';
208
+ export { surfaceOverlayCoherence } from './surface-overlay-coherence.js';
209
+ export { surfaceTrailheadCoherence } from './surface-trailhead-coherence.js';
210
+ export { trailForkCoaching } from './trail-fork-coaching.js';
211
+ export { trailheadOverrideDivergence } from './trailhead-override-divergence.js';
212
+ export {
213
+ forkWithoutPreservedImplementation,
214
+ markerSchemaUnsupported,
215
+ versionPinnedCompose,
216
+ } from './trail-versioning-source.js';
217
+ export {
218
+ deprecationWithoutGuidance,
219
+ pendingForce,
220
+ versionGap,
221
+ versionWithoutExamples,
222
+ } from './trail-versioning-topo.js';
223
+ export { unmaterializedActivationSource } from './unmaterialized-activation-source.js';
224
+ export { unreachableDetourShadowing } from './unreachable-detour-shadowing.js';
225
+ export { validDetourContract } from './valid-detour-contract.js';
226
+ export { validDescribeRefs } from './valid-describe-refs.js';
227
+ export { webhookRouteCollision } from './webhook-route-collision.js';
228
+ export { workspaceLockOwnership } from './workspace-lock-ownership.js';
229
+
230
+ /** All built-in warden rules, keyed by rule name. */
231
+ export const wardenRules: ReadonlyMap<string, WardenRule> = new Map<
232
+ string,
233
+ WardenRule
234
+ >([
235
+ [noThrowInImplementation.name, noThrowInImplementation],
236
+ [capturedKernel.name, capturedKernel],
237
+ [circularRefs.name, circularRefs],
238
+ [entityExists.name, entityExists],
239
+ [contextNoSurfaceTypes.name, contextNoSurfaceTypes],
240
+ [composesDeclarations.name, composesDeclarations],
241
+ [deadInternalTrail.name, deadInternalTrail],
242
+ [deadPublicTrail.name, deadPublicTrail],
243
+ [draftFileMarking.name, draftFileMarking],
244
+ [draftVisibleDebt.name, draftVisibleDebt],
245
+ [duplicateExportedSymbol.name, duplicateExportedSymbol],
246
+ [errorMappingCompleteness.name, errorMappingCompleteness],
247
+ [exampleValid.name, exampleValid],
248
+ [firesDeclarations.name, firesDeclarations],
249
+ [governedSymbolResidue.name, governedSymbolResidue],
250
+ [governedVocabularyPermutationWatch.name, governedVocabularyPermutationWatch],
251
+ [incompleteCrud.name, incompleteCrud],
252
+ [intentPropagation.name, intentPropagation],
253
+ [layerFieldNameDrift.name, layerFieldNameDrift],
254
+ [missingVisibility.name, missingVisibility],
255
+ [missingReconcile.name, missingReconcile],
256
+ [onReferencesExist.name, onReferencesExist],
257
+ [orphanedSignal.name, orphanedSignal],
258
+ [ownerRenderParity.name, ownerRenderParity],
259
+ [publicExportExampleCoverage.name, publicExportExampleCoverage],
260
+ [publicInternalDeepImports.name, publicInternalDeepImports],
261
+ [resourceDeclarations.name, resourceDeclarations],
262
+ [readIntentFires.name, readIntentFires],
263
+ [referenceExists.name, referenceExists],
264
+ [resolvedImportBoundary.name, resolvedImportBoundary],
265
+ [resourceIdGrammar.name, resourceIdGrammar],
266
+ [resourceExists.name, resourceExists],
267
+ [resourceMockCoverage.name, resourceMockCoverage],
268
+ [preferSchemaInference.name, preferSchemaInference],
269
+ [staticResourceAccessorPreference.name, staticResourceAccessorPreference],
270
+ [surfaceTrailheadCoherence.name, surfaceTrailheadCoherence],
271
+ [trailForkCoaching.name, trailForkCoaching],
272
+ [trailheadOverrideDivergence.name, trailheadOverrideDivergence],
273
+ [validDescribeRefs.name, validDescribeRefs],
274
+ [noDevPermitInSource.name, noDevPermitInSource],
275
+ [noDestructuredCompose.name, noDestructuredCompose],
276
+ [noDirectImplementationCall.name, noDirectImplementationCall],
277
+ [noLegacyCliAliasExport.name, noLegacyCliAliasExport],
278
+ [noLegacyLayerImports.name, noLegacyLayerImports],
279
+ [noNativeErrorResult.name, noNativeErrorResult],
280
+ [noRedundantResultErrorWrap.name, noRedundantResultErrorWrap],
281
+ [noRetiredCrossVocabulary.name, noRetiredCrossVocabulary],
282
+ [noSyncResultAssumption.name, noSyncResultAssumption],
283
+ [implementationReturnsResult.name, implementationReturnsResult],
284
+ [noThrowInDetourRecover.name, noThrowInDetourRecover],
285
+ [noTopLevelSurface.name, noTopLevelSurface],
286
+ [unreachableDetourShadowing.name, unreachableDetourShadowing],
287
+ [wardenExportSymmetry.name, wardenExportSymmetry],
288
+ [wardenRulesUseAst.name, wardenRulesUseAst],
289
+ [forkWithoutPreservedImplementation.name, forkWithoutPreservedImplementation],
290
+ [markerSchemaUnsupported.name, markerSchemaUnsupported],
291
+ [versionPinnedCompose.name, versionPinnedCompose],
292
+ [workspaceLockOwnership.name, workspaceLockOwnership],
293
+ ]);
294
+
295
+ /**
296
+ * Built-in topo-aware warden rules, keyed by rule name.
297
+ *
298
+ * These rules inspect the compiled runtime trail graph once per topo,
299
+ * rather than scanning source files. Kept in a separate registry because
300
+ * their dispatch shape differs from `WardenRule` / `ProjectAwareWardenRule`.
301
+ *
302
+ * @remarks
303
+ * Topo-aware rules only fire when the warden runtime is invoked with a
304
+ * resolved `Topo` (see `WardenOptions.topo`). Runs without a topo — e.g.
305
+ * pure source-directory lints — silently skip this registry. Rules
306
+ * registered here must tolerate non-execution when no topo is available.
307
+ */
308
+ export const wardenTopoRules: ReadonlyMap<string, TopoAwareWardenRule> =
309
+ new Map<string, TopoAwareWardenRule>([
310
+ [activationOrphan.name, activationOrphan],
311
+ [cliCommandRouteCoherence.name, cliCommandRouteCoherence],
312
+ [duplicatePublicContract.name, duplicatePublicContract],
313
+ [incompleteAccessorForStandardOp.name, incompleteAccessorForStandardOp],
314
+ [libraryRenderCoherence.name, libraryRenderCoherence],
315
+ [permitGovernance.name, permitGovernance],
316
+ [publicOutputSchema.name, publicOutputSchema],
317
+ [publicUnionOutputDiscriminants.name, publicUnionOutputDiscriminants],
318
+ [scheduledDestroyIntent.name, scheduledDestroyIntent],
319
+ [signalGraphCoaching.name, signalGraphCoaching],
320
+ [surfaceOverlayCoherence.name, surfaceOverlayCoherence],
321
+ [unmaterializedActivationSource.name, unmaterializedActivationSource],
322
+ [validDetourContract.name, validDetourContract],
323
+ [deprecationWithoutGuidance.name, deprecationWithoutGuidance],
324
+ [pendingForce.name, pendingForce],
325
+ [versionGap.name, versionGap],
326
+ [versionWithoutExamples.name, versionWithoutExamples],
327
+ [webhookRouteCollision.name, webhookRouteCollision],
328
+ ]);
@@ -0,0 +1,125 @@
1
+ import type { Intent } from '@ontrails/core';
2
+ import {
3
+ collectNamedTrailIds,
4
+ collectTrailIntentsById,
5
+ extractDefinitionComposeTargetIds,
6
+ } from './source/composition.js';
7
+ import { findTrailDefinitions, offsetToLine, parse } from '@ontrails/source';
8
+ import type { AstNode } from '@ontrails/source';
9
+ import { isTestFile } from './scan.js';
10
+ import type {
11
+ ProjectAwareWardenRule,
12
+ ProjectContext,
13
+ WardenDiagnostic,
14
+ } from './types.js';
15
+
16
+ const buildIntentPropagationDiagnostic = (
17
+ trailId: string,
18
+ targetTrailId: string,
19
+ targetIntent: Exclude<Intent, 'read'>,
20
+ filePath: string,
21
+ line: number
22
+ ): WardenDiagnostic => ({
23
+ filePath,
24
+ line,
25
+ message: `Trail "${trailId}" declares intent: 'read' but composes "${targetTrailId}" with intent: '${targetIntent}'. Read trails must not compose write or destroy side effects. Raise this trail's intent to the strongest intent among everything it composes (at least '${targetIntent}') so surfaces and permits see the real side effects, or stop composing "${targetTrailId}" if it should stay read-only.`,
26
+ rule: 'intent-propagation',
27
+ severity: 'warn',
28
+ });
29
+
30
+ const buildDiagnosticsForComposeTargets = (
31
+ trailId: string,
32
+ targetTrailIds: readonly string[],
33
+ filePath: string,
34
+ line: number,
35
+ trailIntentsById: ReadonlyMap<string, Intent>
36
+ ): readonly WardenDiagnostic[] =>
37
+ targetTrailIds.flatMap((targetTrailId) => {
38
+ const targetIntent = trailIntentsById.get(targetTrailId);
39
+ if (!targetIntent || targetIntent === 'read') {
40
+ return [];
41
+ }
42
+
43
+ return [
44
+ buildIntentPropagationDiagnostic(
45
+ trailId,
46
+ targetTrailId,
47
+ targetIntent,
48
+ filePath,
49
+ line
50
+ ),
51
+ ];
52
+ });
53
+
54
+ const buildDiagnosticsForTrail = (
55
+ def: ReturnType<typeof findTrailDefinitions>[number],
56
+ sourceCode: string,
57
+ filePath: string,
58
+ namedTrailIds: ReadonlyMap<string, string>,
59
+ trailIntentsById: ReadonlyMap<string, Intent>
60
+ ): readonly WardenDiagnostic[] => {
61
+ if (def.kind !== 'trail' || trailIntentsById.get(def.id) !== 'read') {
62
+ return [];
63
+ }
64
+
65
+ return buildDiagnosticsForComposeTargets(
66
+ def.id,
67
+ extractDefinitionComposeTargetIds(def.config, sourceCode, namedTrailIds),
68
+ filePath,
69
+ offsetToLine(sourceCode, def.start),
70
+ trailIntentsById
71
+ );
72
+ };
73
+
74
+ const checkIntentPropagation = (
75
+ ast: AstNode | null,
76
+ sourceCode: string,
77
+ filePath: string,
78
+ trailIntentsById: ReadonlyMap<string, Intent>
79
+ ): readonly WardenDiagnostic[] => {
80
+ if (isTestFile(filePath) || !ast) {
81
+ return [];
82
+ }
83
+
84
+ const namedTrailIds = collectNamedTrailIds(ast);
85
+ return findTrailDefinitions(ast).flatMap((def) =>
86
+ buildDiagnosticsForTrail(
87
+ def,
88
+ sourceCode,
89
+ filePath,
90
+ namedTrailIds,
91
+ trailIntentsById
92
+ )
93
+ );
94
+ };
95
+
96
+ export const intentPropagation: ProjectAwareWardenRule = {
97
+ check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
98
+ const ast = parse(filePath, sourceCode);
99
+ return checkIntentPropagation(
100
+ ast,
101
+ sourceCode,
102
+ filePath,
103
+ ast ? collectTrailIntentsById(ast) : new Map<string, Intent>()
104
+ );
105
+ },
106
+ checkWithContext(
107
+ sourceCode: string,
108
+ filePath: string,
109
+ context: ProjectContext
110
+ ): readonly WardenDiagnostic[] {
111
+ const ast = parse(filePath, sourceCode);
112
+ const localTrailIntentsById = ast
113
+ ? collectTrailIntentsById(ast)
114
+ : new Map<string, Intent>();
115
+ const trailIntentsById = new Map(localTrailIntentsById);
116
+ for (const [trailId, intent] of context.trailIntentsById ?? []) {
117
+ trailIntentsById.set(trailId, intent);
118
+ }
119
+ return checkIntentPropagation(ast, sourceCode, filePath, trailIntentsById);
120
+ },
121
+ description:
122
+ "Warn when a trail declaring intent: 'read' composes a trail whose normalized intent is write or destroy.",
123
+ name: 'intent-propagation',
124
+ severity: 'warn',
125
+ };
@@ -0,0 +1,102 @@
1
+ import { sep } from 'node:path';
2
+
3
+ import {
4
+ getNodeCallee,
5
+ getNodeId,
6
+ getNodeInit,
7
+ identifierName,
8
+ offsetToLine,
9
+ parse,
10
+ walk,
11
+ } from '@ontrails/source';
12
+ import type { AstNode } from '@ontrails/source';
13
+ import type { WardenDiagnostic, WardenRule } from './types.js';
14
+
15
+ const RULE_NAME = 'layer-field-name-drift';
16
+
17
+ const LEGACY_RESERVED_SET_NAMES = new Set(['META_FLAG_CANDIDATES']);
18
+
19
+ const normalizePath = (filePath: string): string =>
20
+ filePath.split(sep).join('/');
21
+
22
+ const isSurfaceSourceFile = (filePath: string): boolean => {
23
+ const normalized = normalizePath(filePath);
24
+ return /(^|\/)packages\/(cli|http|mcp)\/src\//.test(normalized);
25
+ };
26
+
27
+ const isTestFile = (filePath: string): boolean => {
28
+ const normalized = normalizePath(filePath);
29
+ return (
30
+ normalized.includes('/__tests__/') || /\.test\.[cm]?tsx?$/.test(normalized)
31
+ );
32
+ };
33
+
34
+ const looksLikeLayerReservedNameSet = (name: string): boolean => {
35
+ const lower = name.toLowerCase();
36
+ return (
37
+ LEGACY_RESERVED_SET_NAMES.has(name) ||
38
+ (lower.includes('layer') &&
39
+ lower.includes('reserved') &&
40
+ (lower.includes('name') || lower.includes('flag')))
41
+ );
42
+ };
43
+
44
+ const isSetConstruction = (node: AstNode | undefined): boolean => {
45
+ if (node?.type !== 'NewExpression') {
46
+ return false;
47
+ }
48
+ const callee = getNodeCallee(node);
49
+ return identifierName(callee) === 'Set';
50
+ };
51
+
52
+ const isLocalReservedSetInitializer = (node: AstNode | undefined): boolean =>
53
+ node?.type === 'ArrayExpression' || isSetConstruction(node);
54
+
55
+ const buildDiagnostic = (
56
+ sourceCode: string,
57
+ filePath: string,
58
+ node: AstNode,
59
+ name: string
60
+ ): WardenDiagnostic => ({
61
+ filePath,
62
+ line: offsetToLine(sourceCode, node.start),
63
+ message: `layer-field-name-drift: surface-local reserved name set "${name}" can make layer input fields render differently across surfaces. Import LAYER_FIELD_RESERVED_NAMES from @ontrails/core instead.`,
64
+ rule: RULE_NAME,
65
+ severity: 'error',
66
+ });
67
+
68
+ export const layerFieldNameDrift: WardenRule = {
69
+ check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
70
+ if (!isSurfaceSourceFile(filePath) || isTestFile(filePath)) {
71
+ return [];
72
+ }
73
+
74
+ const ast = parse(filePath, sourceCode);
75
+ if (!ast) {
76
+ return [];
77
+ }
78
+
79
+ const diagnostics: WardenDiagnostic[] = [];
80
+ walk(ast, (node) => {
81
+ if (node.type !== 'VariableDeclarator') {
82
+ return;
83
+ }
84
+ const id = getNodeId(node);
85
+ const init = getNodeInit(node);
86
+ const name = identifierName(id);
87
+ if (
88
+ name &&
89
+ looksLikeLayerReservedNameSet(name) &&
90
+ isLocalReservedSetInitializer(init)
91
+ ) {
92
+ diagnostics.push(buildDiagnostic(sourceCode, filePath, node, name));
93
+ }
94
+ });
95
+
96
+ return diagnostics;
97
+ },
98
+ description:
99
+ 'Prevent surface-local reserved-name sets from drifting layer input field rendering across surfaces.',
100
+ name: RULE_NAME,
101
+ severity: 'error',
102
+ };
@@ -0,0 +1,97 @@
1
+ import { deriveTopoGraph } from '@ontrails/topography';
2
+ import type { TopoGraph, TopoGraphLibraryDerived } from '@ontrails/topography';
3
+
4
+ import type { TopoAwareWardenRule, WardenDiagnostic } from './types.js';
5
+
6
+ const RULE_NAME = 'library-render-coherence';
7
+ const TOPO_FILE = '<topo>';
8
+
9
+ const resolveGraph = (
10
+ topo: Parameters<TopoAwareWardenRule['checkTopo']>[0],
11
+ graph: TopoGraph | undefined
12
+ ): TopoGraph => graph ?? deriveTopoGraph(topo);
13
+
14
+ const renderTrailIds = (trailIds: readonly string[]): string =>
15
+ trailIds
16
+ .toSorted((left, right) => left.localeCompare(right))
17
+ .map((trailId) => `"${trailId}"`)
18
+ .join(', ');
19
+
20
+ const collisionDiagnostic = (
21
+ exportName: string,
22
+ trailIds: readonly string[]
23
+ ): WardenDiagnostic => ({
24
+ filePath: TOPO_FILE,
25
+ line: 1,
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.`,
27
+ rule: RULE_NAME,
28
+ severity: 'error',
29
+ });
30
+
31
+ const missingTargetDiagnostic = (
32
+ exportName: string,
33
+ trailId: string
34
+ ): WardenDiagnostic => ({
35
+ filePath: TOPO_FILE,
36
+ line: 1,
37
+ message: `Library rendering export "${exportName}" targets unknown trail "${trailId}". Resolved library exports must stay attached to existing trail contracts.`,
38
+ rule: RULE_NAME,
39
+ severity: 'error',
40
+ });
41
+
42
+ const duplicateExportDiagnostic = (
43
+ exportName: string,
44
+ trailIds: readonly string[]
45
+ ): WardenDiagnostic => ({
46
+ filePath: TOPO_FILE,
47
+ line: 1,
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.`,
49
+ rule: RULE_NAME,
50
+ severity: 'error',
51
+ });
52
+
53
+ const collectDuplicateExportDiagnostics = (
54
+ rendering: TopoGraphLibraryDerived
55
+ ): readonly WardenDiagnostic[] => {
56
+ const trailIdsByExport = new Map<string, string[]>();
57
+ for (const entry of rendering.exports) {
58
+ const trailIds = trailIdsByExport.get(entry.exportName) ?? [];
59
+ trailIds.push(entry.trailId);
60
+ trailIdsByExport.set(entry.exportName, trailIds);
61
+ }
62
+ return [...trailIdsByExport.entries()]
63
+ .filter(([, trailIds]) => trailIds.length > 1)
64
+ .map(([exportName, trailIds]) =>
65
+ duplicateExportDiagnostic(exportName, trailIds)
66
+ );
67
+ };
68
+
69
+ const collectMissingTargetDiagnostics = (
70
+ rendering: TopoGraphLibraryDerived,
71
+ knownTrailIds: ReadonlySet<string>
72
+ ): readonly WardenDiagnostic[] =>
73
+ rendering.exports
74
+ .filter((entry) => !knownTrailIds.has(entry.trailId))
75
+ .map((entry) => missingTargetDiagnostic(entry.exportName, entry.trailId));
76
+
77
+ export const libraryRenderCoherence: TopoAwareWardenRule = {
78
+ checkTopo(topo, context) {
79
+ const graph = resolveGraph(topo, context?.graph);
80
+ if (!graph.library) {
81
+ return [];
82
+ }
83
+
84
+ const knownTrailIds = new Set(topo.trails.keys());
85
+ return [
86
+ ...graph.library.collisions.map((collision) =>
87
+ collisionDiagnostic(collision.exportName, collision.trailIds)
88
+ ),
89
+ ...collectDuplicateExportDiagnostics(graph.library),
90
+ ...collectMissingTargetDiagnostics(graph.library, knownTrailIds),
91
+ ];
92
+ },
93
+ description:
94
+ 'Ensure resolved library rendering exports are collision-free and target existing trails.',
95
+ name: RULE_NAME,
96
+ severity: 'error',
97
+ };