@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
|
@@ -0,0 +1,871 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WardenFixClass,
|
|
3
|
+
WardenFixSafety,
|
|
4
|
+
WardenRule,
|
|
5
|
+
WardenRuleConcern,
|
|
6
|
+
WardenRuleLifecycleState,
|
|
7
|
+
WardenRuleMetadata,
|
|
8
|
+
WardenRuleScope,
|
|
9
|
+
WardenRuleTier,
|
|
10
|
+
} from './types.js';
|
|
11
|
+
|
|
12
|
+
export const wardenRuleTiers = [
|
|
13
|
+
'source-static',
|
|
14
|
+
'project-static',
|
|
15
|
+
'topo-aware',
|
|
16
|
+
'drift',
|
|
17
|
+
'advisory',
|
|
18
|
+
] as const satisfies readonly WardenRuleTier[];
|
|
19
|
+
|
|
20
|
+
export const wardenRuleScopes = [
|
|
21
|
+
'external',
|
|
22
|
+
'extension',
|
|
23
|
+
'internal',
|
|
24
|
+
'repo-local',
|
|
25
|
+
'temporary',
|
|
26
|
+
'advisory',
|
|
27
|
+
] as const satisfies readonly WardenRuleScope[];
|
|
28
|
+
|
|
29
|
+
export const wardenRuleConcerns = [
|
|
30
|
+
'composition',
|
|
31
|
+
'general',
|
|
32
|
+
'lifecycle',
|
|
33
|
+
'meta',
|
|
34
|
+
'permits',
|
|
35
|
+
'resources',
|
|
36
|
+
'results',
|
|
37
|
+
'signals',
|
|
38
|
+
] as const satisfies readonly WardenRuleConcern[];
|
|
39
|
+
|
|
40
|
+
export const wardenRuleLifecycleStates = [
|
|
41
|
+
'durable',
|
|
42
|
+
'temporary',
|
|
43
|
+
'deprecated',
|
|
44
|
+
] as const satisfies readonly WardenRuleLifecycleState[];
|
|
45
|
+
|
|
46
|
+
export const wardenFixClasses = [
|
|
47
|
+
'export-restructure',
|
|
48
|
+
'term-rewrite',
|
|
49
|
+
] as const satisfies readonly WardenFixClass[];
|
|
50
|
+
|
|
51
|
+
export const wardenFixSafeties = [
|
|
52
|
+
'review',
|
|
53
|
+
'safe',
|
|
54
|
+
] as const satisfies readonly WardenFixSafety[];
|
|
55
|
+
|
|
56
|
+
type BuiltinWardenRuleMetadataInput = Omit<
|
|
57
|
+
WardenRuleMetadata,
|
|
58
|
+
'concern' | 'depth'
|
|
59
|
+
> &
|
|
60
|
+
Partial<Pick<WardenRuleMetadata, 'concern' | 'depth'>>;
|
|
61
|
+
|
|
62
|
+
const depthByTier = {
|
|
63
|
+
advisory: 'all',
|
|
64
|
+
drift: 'all',
|
|
65
|
+
'project-static': 'project',
|
|
66
|
+
'source-static': 'source',
|
|
67
|
+
'topo-aware': 'topo',
|
|
68
|
+
} as const satisfies Record<WardenRuleTier, WardenRuleMetadata['depth']>;
|
|
69
|
+
|
|
70
|
+
const concernByRuleName: Partial<Record<string, WardenRuleConcern>> = {
|
|
71
|
+
'activation-orphan': 'signals',
|
|
72
|
+
'captured-kernel': 'general',
|
|
73
|
+
'cli-command-route-coherence': 'meta',
|
|
74
|
+
'composes-declarations': 'composition',
|
|
75
|
+
'context-no-surface-types': 'composition',
|
|
76
|
+
'dead-internal-trail': 'composition',
|
|
77
|
+
'dead-public-trail': 'composition',
|
|
78
|
+
'deprecation-without-guidance': 'lifecycle',
|
|
79
|
+
'draft-file-marking': 'lifecycle',
|
|
80
|
+
'draft-visible-debt': 'lifecycle',
|
|
81
|
+
'duplicate-exported-symbol': 'general',
|
|
82
|
+
'duplicate-public-contract': 'meta',
|
|
83
|
+
'error-mapping-completeness': 'results',
|
|
84
|
+
'fires-declarations': 'signals',
|
|
85
|
+
'fork-without-preserved-implementation': 'lifecycle',
|
|
86
|
+
'governed-symbol-residue': 'lifecycle',
|
|
87
|
+
'implementation-returns-result': 'results',
|
|
88
|
+
'intent-propagation': 'composition',
|
|
89
|
+
'library-projection-coherence': 'meta',
|
|
90
|
+
'marker-schema-unsupported': 'lifecycle',
|
|
91
|
+
'missing-reconcile': 'resources',
|
|
92
|
+
'missing-visibility': 'composition',
|
|
93
|
+
'no-destructured-compose': 'composition',
|
|
94
|
+
'no-dev-permit-in-source': 'permits',
|
|
95
|
+
'no-direct-implementation-call': 'composition',
|
|
96
|
+
'no-legacy-cli-alias-export': 'general',
|
|
97
|
+
'no-native-error-result': 'results',
|
|
98
|
+
'no-redundant-result-error-wrap': 'results',
|
|
99
|
+
'no-retired-cross-vocabulary': 'composition',
|
|
100
|
+
'no-sync-result-assumption': 'results',
|
|
101
|
+
'no-throw-in-detour-recover': 'results',
|
|
102
|
+
'no-throw-in-implementation': 'results',
|
|
103
|
+
'on-references-exist': 'signals',
|
|
104
|
+
'orphaned-signal': 'signals',
|
|
105
|
+
'pending-force': 'lifecycle',
|
|
106
|
+
'permit-governance': 'permits',
|
|
107
|
+
'public-output-schema': 'results',
|
|
108
|
+
'read-intent-fires': 'signals',
|
|
109
|
+
'resolved-import-boundary': 'composition',
|
|
110
|
+
'resource-declarations': 'resources',
|
|
111
|
+
'resource-exists': 'resources',
|
|
112
|
+
'resource-id-grammar': 'resources',
|
|
113
|
+
'resource-mock-coverage': 'resources',
|
|
114
|
+
'scheduled-destroy-intent': 'lifecycle',
|
|
115
|
+
'signal-graph-coaching': 'signals',
|
|
116
|
+
'static-resource-accessor-preference': 'resources',
|
|
117
|
+
'surface-overlay-coherence': 'meta',
|
|
118
|
+
'surface-trailhead-coherence': 'meta',
|
|
119
|
+
'trail-fork-coaching': 'meta',
|
|
120
|
+
'trailhead-override-divergence': 'meta',
|
|
121
|
+
'unmaterialized-activation-source': 'lifecycle',
|
|
122
|
+
'valid-detour-contract': 'results',
|
|
123
|
+
'version-gap': 'lifecycle',
|
|
124
|
+
'version-pinned-compose': 'composition',
|
|
125
|
+
'version-without-examples': 'lifecycle',
|
|
126
|
+
'webhook-route-collision': 'composition',
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const durableExternal = {
|
|
130
|
+
lifecycle: { state: 'durable' },
|
|
131
|
+
scope: 'external',
|
|
132
|
+
} as const;
|
|
133
|
+
|
|
134
|
+
const durableExtension = {
|
|
135
|
+
lifecycle: { state: 'durable' },
|
|
136
|
+
scope: 'extension',
|
|
137
|
+
} as const;
|
|
138
|
+
|
|
139
|
+
const durableRepoLocal = {
|
|
140
|
+
lifecycle: { state: 'durable' },
|
|
141
|
+
scope: 'repo-local',
|
|
142
|
+
} as const;
|
|
143
|
+
|
|
144
|
+
const trailContractDocs = {
|
|
145
|
+
label: 'Trail Rules',
|
|
146
|
+
path: 'AGENTS.md#trail-rules',
|
|
147
|
+
} as const;
|
|
148
|
+
|
|
149
|
+
const wardenDocs = {
|
|
150
|
+
label: 'Warden',
|
|
151
|
+
path: 'docs/warden.md',
|
|
152
|
+
} as const;
|
|
153
|
+
|
|
154
|
+
const builtinWardenRuleMetadataInput = {
|
|
155
|
+
'activation-orphan': {
|
|
156
|
+
...durableExternal,
|
|
157
|
+
invariant:
|
|
158
|
+
'Signal activation consumers reference sources with producer declarations.',
|
|
159
|
+
tier: 'topo-aware',
|
|
160
|
+
},
|
|
161
|
+
'captured-kernel': {
|
|
162
|
+
guidance: {
|
|
163
|
+
relatedRules: [
|
|
164
|
+
'duplicate-exported-symbol',
|
|
165
|
+
'public-internal-deep-imports',
|
|
166
|
+
'resolved-import-boundary',
|
|
167
|
+
],
|
|
168
|
+
steps: [
|
|
169
|
+
'Review whether the public subpath should become an owned package surface, move back behind the package root, or be split into a better-owned package.',
|
|
170
|
+
'If the imported capability is reusable source-code machinery, serves at least two independently owned toolchain capabilities, exposes one genuinely shared contract, and owns no verdict, migration plan, graph query, or surface rendering, consider relocating it to @ontrails/source.',
|
|
171
|
+
'Otherwise, preserve the current owner or choose another doctrinal owner.',
|
|
172
|
+
],
|
|
173
|
+
summary:
|
|
174
|
+
'Review ownership before an internal re-exported kernel hardens into a public package seam.',
|
|
175
|
+
},
|
|
176
|
+
invariant:
|
|
177
|
+
'Public subpath exports that re-export internal kernels receive ownership review after multiple production packages consume them.',
|
|
178
|
+
lifecycle: { state: 'durable' },
|
|
179
|
+
scope: 'advisory',
|
|
180
|
+
tier: 'project-static',
|
|
181
|
+
},
|
|
182
|
+
'circular-refs': {
|
|
183
|
+
...durableExternal,
|
|
184
|
+
invariant: 'Entity reference graphs must be acyclic.',
|
|
185
|
+
tier: 'project-static',
|
|
186
|
+
},
|
|
187
|
+
'cli-command-route-coherence': {
|
|
188
|
+
...durableExternal,
|
|
189
|
+
guidance: {
|
|
190
|
+
docs: [
|
|
191
|
+
{
|
|
192
|
+
label: 'CLI command routes ADR',
|
|
193
|
+
path: 'docs/adr/drafts/20260613-cli-command-routes.md',
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
relatedRules: ['webhook-route-collision'],
|
|
197
|
+
summary:
|
|
198
|
+
'Keep every CLI command route and alias normalized into one trail contract.',
|
|
199
|
+
},
|
|
200
|
+
invariant:
|
|
201
|
+
'CLI command routes and aliases resolve to one coherent trail contract.',
|
|
202
|
+
tier: 'topo-aware',
|
|
203
|
+
},
|
|
204
|
+
'composes-declarations': {
|
|
205
|
+
...durableExternal,
|
|
206
|
+
invariant: 'Declared composes stay aligned with ctx.compose() usage.',
|
|
207
|
+
tier: 'source-static',
|
|
208
|
+
},
|
|
209
|
+
'context-no-surface-types': {
|
|
210
|
+
...durableExternal,
|
|
211
|
+
invariant: 'Trail logic stays surface-agnostic.',
|
|
212
|
+
tier: 'source-static',
|
|
213
|
+
},
|
|
214
|
+
'dead-internal-trail': {
|
|
215
|
+
...durableExternal,
|
|
216
|
+
invariant: 'Internal trails should be reachable through declared composes.',
|
|
217
|
+
tier: 'project-static',
|
|
218
|
+
},
|
|
219
|
+
'dead-public-trail': {
|
|
220
|
+
...durableExternal,
|
|
221
|
+
guidance: {
|
|
222
|
+
relatedRules: ['dead-internal-trail', 'duplicate-public-contract'],
|
|
223
|
+
summary:
|
|
224
|
+
'Anchor exported public trails in a topo, composition edge, or activation source.',
|
|
225
|
+
},
|
|
226
|
+
invariant:
|
|
227
|
+
'Exported public trails are anchored in configured app topos, composition, or activation.',
|
|
228
|
+
tier: 'project-static',
|
|
229
|
+
},
|
|
230
|
+
'deprecation-without-guidance': {
|
|
231
|
+
...durableExternal,
|
|
232
|
+
invariant:
|
|
233
|
+
'Deprecated trail version entries carry successor, migration, or note guidance.',
|
|
234
|
+
tier: 'topo-aware',
|
|
235
|
+
},
|
|
236
|
+
'draft-file-marking': {
|
|
237
|
+
...durableExternal,
|
|
238
|
+
invariant: 'Draft-authored state is visibly marked in filenames.',
|
|
239
|
+
tier: 'source-static',
|
|
240
|
+
},
|
|
241
|
+
'draft-visible-debt': {
|
|
242
|
+
...durableExternal,
|
|
243
|
+
invariant: 'Draft-authored IDs remain visible debt.',
|
|
244
|
+
tier: 'source-static',
|
|
245
|
+
},
|
|
246
|
+
'duplicate-exported-symbol': {
|
|
247
|
+
...durableRepoLocal,
|
|
248
|
+
guidance: {
|
|
249
|
+
docs: [
|
|
250
|
+
{
|
|
251
|
+
label: 'Package ownership',
|
|
252
|
+
path: 'docs/contributing/package-ownership.md',
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
relatedRules: [
|
|
256
|
+
'resolved-import-boundary',
|
|
257
|
+
'public-internal-deep-imports',
|
|
258
|
+
],
|
|
259
|
+
summary:
|
|
260
|
+
'Keep exported symbol ownership from drifting across first-party packages.',
|
|
261
|
+
},
|
|
262
|
+
invariant:
|
|
263
|
+
'First-party packages should not define the same exported symbol name in parallel.',
|
|
264
|
+
tier: 'project-static',
|
|
265
|
+
},
|
|
266
|
+
'duplicate-public-contract': {
|
|
267
|
+
...durableExternal,
|
|
268
|
+
guidance: {
|
|
269
|
+
docs: [
|
|
270
|
+
{
|
|
271
|
+
label: 'Surface accommodations',
|
|
272
|
+
path: 'docs/surfaces/surface-accommodations.md',
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
relatedRules: ['cli-command-route-coherence', 'trail-fork-coaching'],
|
|
276
|
+
summary:
|
|
277
|
+
'Keep duplicate public contract facts from drifting into separate capabilities.',
|
|
278
|
+
},
|
|
279
|
+
invariant:
|
|
280
|
+
'Public surface trails should not expose duplicate normalized contract facts.',
|
|
281
|
+
tier: 'topo-aware',
|
|
282
|
+
},
|
|
283
|
+
'entity-exists': {
|
|
284
|
+
...durableExternal,
|
|
285
|
+
invariant: 'Declared entity references resolve to known entities.',
|
|
286
|
+
tier: 'project-static',
|
|
287
|
+
},
|
|
288
|
+
'error-mapping-completeness': {
|
|
289
|
+
...durableExtension,
|
|
290
|
+
invariant: 'Registered surface error mappers cover every error category.',
|
|
291
|
+
tier: 'source-static',
|
|
292
|
+
},
|
|
293
|
+
'example-valid': {
|
|
294
|
+
...durableExternal,
|
|
295
|
+
guidance: {
|
|
296
|
+
docs: [trailContractDocs],
|
|
297
|
+
steps: [
|
|
298
|
+
'Update the example input, expected output, or schema so they describe the same contract.',
|
|
299
|
+
'Run the package tests that exercise the affected trail examples.',
|
|
300
|
+
],
|
|
301
|
+
summary: 'Keep trail examples synchronized with their authored schemas.',
|
|
302
|
+
},
|
|
303
|
+
invariant: 'Trail examples remain valid against their authored schema.',
|
|
304
|
+
tier: 'source-static',
|
|
305
|
+
},
|
|
306
|
+
'fires-declarations': {
|
|
307
|
+
...durableExternal,
|
|
308
|
+
invariant: 'Declared fires stay aligned with signal firing usage.',
|
|
309
|
+
tier: 'source-static',
|
|
310
|
+
},
|
|
311
|
+
'fork-without-preserved-implementation': {
|
|
312
|
+
...durableExternal,
|
|
313
|
+
invariant: 'Fork version entries preserve their historical implementation.',
|
|
314
|
+
tier: 'source-static',
|
|
315
|
+
},
|
|
316
|
+
'governed-symbol-residue': {
|
|
317
|
+
...durableExternal,
|
|
318
|
+
fix: { class: 'term-rewrite', safety: 'safe' },
|
|
319
|
+
invariant:
|
|
320
|
+
'Active governed vocabulary symbol renames do not leave retired identifiers in source.',
|
|
321
|
+
tier: 'source-static',
|
|
322
|
+
},
|
|
323
|
+
'implementation-returns-result': {
|
|
324
|
+
...durableExternal,
|
|
325
|
+
invariant: 'Implementations return Result values.',
|
|
326
|
+
tier: 'source-static',
|
|
327
|
+
},
|
|
328
|
+
'incomplete-accessor-for-standard-op': {
|
|
329
|
+
...durableExternal,
|
|
330
|
+
invariant: 'Standard CRUD operations expose the expected accessor shape.',
|
|
331
|
+
tier: 'topo-aware',
|
|
332
|
+
},
|
|
333
|
+
'incomplete-crud': {
|
|
334
|
+
...durableExternal,
|
|
335
|
+
invariant: 'Versioned CRUD entities expose complete operation coverage.',
|
|
336
|
+
tier: 'project-static',
|
|
337
|
+
},
|
|
338
|
+
'intent-propagation': {
|
|
339
|
+
...durableExternal,
|
|
340
|
+
invariant: 'Composite trail intent cannot be safer than composed trails.',
|
|
341
|
+
tier: 'project-static',
|
|
342
|
+
},
|
|
343
|
+
'layer-field-name-drift': {
|
|
344
|
+
...durableExternal,
|
|
345
|
+
invariant:
|
|
346
|
+
'Layer input field reserved names are shared across surface projections.',
|
|
347
|
+
tier: 'source-static',
|
|
348
|
+
},
|
|
349
|
+
'library-projection-coherence': {
|
|
350
|
+
...durableExternal,
|
|
351
|
+
guidance: {
|
|
352
|
+
docs: [
|
|
353
|
+
{
|
|
354
|
+
label: 'Library Surface ADR',
|
|
355
|
+
path: 'docs/adr/drafts/20260612-library-surface-and-compiler.md',
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
relatedRules: [
|
|
359
|
+
'cli-command-route-coherence',
|
|
360
|
+
'surface-trailhead-coherence',
|
|
361
|
+
],
|
|
362
|
+
steps: [
|
|
363
|
+
'Rename one source trail or add an explicit library export override before generating a package.',
|
|
364
|
+
'Keep serialized library projection exports attached to existing trail IDs.',
|
|
365
|
+
'Run the generated-package smoke after repairing projection drift.',
|
|
366
|
+
],
|
|
367
|
+
summary:
|
|
368
|
+
'Keep resolved library projection exports collision-free and attached to one trail contract.',
|
|
369
|
+
},
|
|
370
|
+
invariant:
|
|
371
|
+
'Resolved library projection exports are collision-free and target existing trails.',
|
|
372
|
+
tier: 'topo-aware',
|
|
373
|
+
},
|
|
374
|
+
'marker-schema-unsupported': {
|
|
375
|
+
...durableExternal,
|
|
376
|
+
invariant:
|
|
377
|
+
'Versioned schemas stay inside the supported marker projection subset.',
|
|
378
|
+
tier: 'source-static',
|
|
379
|
+
},
|
|
380
|
+
'missing-reconcile': {
|
|
381
|
+
...durableExternal,
|
|
382
|
+
invariant: 'Versioned CRUD store tables provide reconcile coverage.',
|
|
383
|
+
tier: 'project-static',
|
|
384
|
+
},
|
|
385
|
+
'missing-visibility': {
|
|
386
|
+
...durableExternal,
|
|
387
|
+
invariant: 'Composition-only trails declare internal visibility.',
|
|
388
|
+
tier: 'project-static',
|
|
389
|
+
},
|
|
390
|
+
'no-destructured-compose': {
|
|
391
|
+
...durableExternal,
|
|
392
|
+
invariant:
|
|
393
|
+
'Trail implementations compose through ctx.compose() directly instead of destructuring compose from the context.',
|
|
394
|
+
tier: 'source-static',
|
|
395
|
+
},
|
|
396
|
+
'no-dev-permit-in-source': {
|
|
397
|
+
...durableExternal,
|
|
398
|
+
invariant:
|
|
399
|
+
'The `--dev-permit` CLI flag string never appears in committed source.',
|
|
400
|
+
tier: 'source-static',
|
|
401
|
+
},
|
|
402
|
+
'no-direct-implementation-call': {
|
|
403
|
+
...durableExternal,
|
|
404
|
+
invariant: 'Application code composes trails through ctx.compose().',
|
|
405
|
+
tier: 'source-static',
|
|
406
|
+
},
|
|
407
|
+
'no-legacy-cli-alias-export': {
|
|
408
|
+
fix: { class: 'export-restructure', safety: 'review' },
|
|
409
|
+
invariant:
|
|
410
|
+
'Legacy app-module CLI alias exports (cliAliases, trailsCliAliases) removed in the TRL-1207 surfaces-overlay cutover do not reappear in committed source.',
|
|
411
|
+
lifecycle: {
|
|
412
|
+
retireWhen:
|
|
413
|
+
'TRL-1207 surfaces-overlay migration window closes and supported apps have moved CLI bindings into surfaceOverlay entries in their trailsOverlays exports.',
|
|
414
|
+
state: 'temporary',
|
|
415
|
+
},
|
|
416
|
+
scope: 'external',
|
|
417
|
+
tier: 'source-static',
|
|
418
|
+
},
|
|
419
|
+
'no-legacy-layer-imports': {
|
|
420
|
+
fix: { class: 'term-rewrite', safety: 'review' },
|
|
421
|
+
invariant:
|
|
422
|
+
'Legacy layer exports removed across TRL-475/TRL-476 (authLayer, autoIterateLayer, dateShortcutsLayer) do not reappear in committed source.',
|
|
423
|
+
lifecycle: {
|
|
424
|
+
retireWhen:
|
|
425
|
+
'Layer Evolution legacy layer migration window closes (one minor release after the legacy exports are removed).',
|
|
426
|
+
state: 'temporary',
|
|
427
|
+
},
|
|
428
|
+
scope: 'external',
|
|
429
|
+
tier: 'source-static',
|
|
430
|
+
},
|
|
431
|
+
'no-native-error-result': {
|
|
432
|
+
...durableExternal,
|
|
433
|
+
invariant: 'Result error boundaries carry specific TrailsError subclasses.',
|
|
434
|
+
tier: 'source-static',
|
|
435
|
+
},
|
|
436
|
+
'no-redundant-result-error-wrap': {
|
|
437
|
+
...durableExternal,
|
|
438
|
+
invariant:
|
|
439
|
+
'Result error pass-throughs preserve the original Result boundary.',
|
|
440
|
+
tier: 'source-static',
|
|
441
|
+
},
|
|
442
|
+
'no-retired-cross-vocabulary': {
|
|
443
|
+
fix: { class: 'term-rewrite', safety: 'safe' },
|
|
444
|
+
invariant:
|
|
445
|
+
'Retired cross composition vocabulary does not remain in downstream source after the beta.19 compose cutover.',
|
|
446
|
+
lifecycle: {
|
|
447
|
+
retireWhen:
|
|
448
|
+
'Downstream beta.19 cross-to-compose migration window closes and supported apps have adopted compose vocabulary.',
|
|
449
|
+
state: 'temporary',
|
|
450
|
+
},
|
|
451
|
+
scope: 'external',
|
|
452
|
+
tier: 'source-static',
|
|
453
|
+
},
|
|
454
|
+
'no-sync-result-assumption': {
|
|
455
|
+
...durableExternal,
|
|
456
|
+
invariant:
|
|
457
|
+
'Result accessors are not used before async results are awaited.',
|
|
458
|
+
tier: 'source-static',
|
|
459
|
+
},
|
|
460
|
+
'no-throw-in-detour-recover': {
|
|
461
|
+
...durableExternal,
|
|
462
|
+
invariant: 'Detour recovery returns Result instead of throwing.',
|
|
463
|
+
tier: 'source-static',
|
|
464
|
+
},
|
|
465
|
+
'no-throw-in-implementation': {
|
|
466
|
+
...durableExternal,
|
|
467
|
+
guidance: {
|
|
468
|
+
docs: [trailContractDocs],
|
|
469
|
+
relatedRules: ['implementation-returns-result', 'no-native-error-result'],
|
|
470
|
+
steps: [
|
|
471
|
+
'Return Result.err() with the most specific TrailsError subclass available.',
|
|
472
|
+
'Use detours for recoverable runtime strategies instead of throwing inside the implementation.',
|
|
473
|
+
],
|
|
474
|
+
summary:
|
|
475
|
+
'Convert thrown failures in implementations into explicit Result.err() outcomes.',
|
|
476
|
+
},
|
|
477
|
+
invariant: 'Implementations return Result.err() instead of throwing.',
|
|
478
|
+
tier: 'source-static',
|
|
479
|
+
},
|
|
480
|
+
'no-top-level-surface': {
|
|
481
|
+
...durableExternal,
|
|
482
|
+
guidance: {
|
|
483
|
+
docs: [{ label: 'Architecture', path: 'docs/architecture.md' }],
|
|
484
|
+
relatedRules: ['context-no-surface-types'],
|
|
485
|
+
steps: [
|
|
486
|
+
'Keep the topo-export module focused on exporting `topo(...)` as `default`, `graph`, or `app`.',
|
|
487
|
+
'Move `surface(...)`, `connectStdio(...)`, server start, or `.listen(...)` calls into a separate entry or bin module.',
|
|
488
|
+
],
|
|
489
|
+
summary:
|
|
490
|
+
'Keep topo entry modules side-effect-free for survey, guide, compile, and lock generation.',
|
|
491
|
+
},
|
|
492
|
+
invariant: 'Topo export modules do not open surfaces at module top level.',
|
|
493
|
+
tier: 'source-static',
|
|
494
|
+
},
|
|
495
|
+
'on-references-exist': {
|
|
496
|
+
...durableExternal,
|
|
497
|
+
invariant: 'Trail on: declarations resolve to known signals.',
|
|
498
|
+
tier: 'project-static',
|
|
499
|
+
},
|
|
500
|
+
'orphaned-signal': {
|
|
501
|
+
...durableExternal,
|
|
502
|
+
invariant:
|
|
503
|
+
'Derived store signals are consumed by matching trail on: consumers.',
|
|
504
|
+
tier: 'project-static',
|
|
505
|
+
},
|
|
506
|
+
'owner-projection-parity': {
|
|
507
|
+
invariant: 'Framework projections stay aligned with owner exports.',
|
|
508
|
+
lifecycle: { state: 'durable' },
|
|
509
|
+
scope: 'internal',
|
|
510
|
+
tier: 'source-static',
|
|
511
|
+
},
|
|
512
|
+
'pending-force': {
|
|
513
|
+
...durableExternal,
|
|
514
|
+
invariant:
|
|
515
|
+
'Forced topo break audit events do not remain pending indefinitely.',
|
|
516
|
+
tier: 'topo-aware',
|
|
517
|
+
},
|
|
518
|
+
'permit-governance': {
|
|
519
|
+
...durableExternal,
|
|
520
|
+
guidance: {
|
|
521
|
+
docs: [
|
|
522
|
+
trailContractDocs,
|
|
523
|
+
{ label: 'Permits', path: 'packages/permits/README.md' },
|
|
524
|
+
],
|
|
525
|
+
steps: [
|
|
526
|
+
'Declare the permit required for the destructive trail.',
|
|
527
|
+
'If the write is intentionally development-only, keep the dev permit out of committed runtime source.',
|
|
528
|
+
],
|
|
529
|
+
summary:
|
|
530
|
+
'Make destructive trail authorization visible on the trail contract.',
|
|
531
|
+
},
|
|
532
|
+
invariant: 'Destroy trails declare explicit permit requirements.',
|
|
533
|
+
tier: 'topo-aware',
|
|
534
|
+
},
|
|
535
|
+
'prefer-schema-inference': {
|
|
536
|
+
guidance: {
|
|
537
|
+
docs: [trailContractDocs],
|
|
538
|
+
steps: [
|
|
539
|
+
'Remove field overrides that only repeat labels or enum options already inferable from the schema.',
|
|
540
|
+
'Keep field metadata only when it adds meaning the schema cannot derive.',
|
|
541
|
+
],
|
|
542
|
+
summary:
|
|
543
|
+
'Let schemas remain the owner for field metadata unless an override adds new information.',
|
|
544
|
+
},
|
|
545
|
+
invariant: 'Trail schemas should be inferred unless overrides add meaning.',
|
|
546
|
+
lifecycle: { state: 'durable' },
|
|
547
|
+
scope: 'advisory',
|
|
548
|
+
tier: 'source-static',
|
|
549
|
+
},
|
|
550
|
+
'public-export-example-coverage': {
|
|
551
|
+
...durableRepoLocal,
|
|
552
|
+
invariant:
|
|
553
|
+
'Public API barrel exports carry leading @example TSDoc coverage.',
|
|
554
|
+
tier: 'source-static',
|
|
555
|
+
},
|
|
556
|
+
'public-internal-deep-imports': {
|
|
557
|
+
invariant: 'Cross-package imports stay on package-owned public exports.',
|
|
558
|
+
lifecycle: { state: 'durable' },
|
|
559
|
+
scope: 'internal',
|
|
560
|
+
tier: 'project-static',
|
|
561
|
+
},
|
|
562
|
+
'public-output-schema': {
|
|
563
|
+
...durableExternal,
|
|
564
|
+
guidance: {
|
|
565
|
+
docs: [trailContractDocs, wardenDocs],
|
|
566
|
+
relatedRules: ['public-union-output-discriminants'],
|
|
567
|
+
steps: [
|
|
568
|
+
'Add an explicit output schema to public trails that can be projected onto MCP or HTTP surfaces.',
|
|
569
|
+
'If the trail is composition-only, mark it visibility: "internal" instead of exposing it by default.',
|
|
570
|
+
],
|
|
571
|
+
summary:
|
|
572
|
+
'Make public surface result contracts explicit before MCP/HTTP projection.',
|
|
573
|
+
},
|
|
574
|
+
invariant: 'Public MCP/HTTP surface trails declare output schemas.',
|
|
575
|
+
tier: 'topo-aware',
|
|
576
|
+
},
|
|
577
|
+
'public-union-output-discriminants': {
|
|
578
|
+
...durableExternal,
|
|
579
|
+
invariant: 'Public output object unions expose branch discriminants.',
|
|
580
|
+
tier: 'topo-aware',
|
|
581
|
+
},
|
|
582
|
+
'read-intent-fires': {
|
|
583
|
+
...durableExternal,
|
|
584
|
+
invariant: 'Read trails should not declare signal fires side effects.',
|
|
585
|
+
tier: 'source-static',
|
|
586
|
+
},
|
|
587
|
+
'reference-exists': {
|
|
588
|
+
...durableExternal,
|
|
589
|
+
invariant: 'Reference declarations resolve to known entities.',
|
|
590
|
+
tier: 'project-static',
|
|
591
|
+
},
|
|
592
|
+
'resolved-import-boundary': {
|
|
593
|
+
...durableExternal,
|
|
594
|
+
invariant: 'Cross-package imports resolve through public export maps.',
|
|
595
|
+
tier: 'project-static',
|
|
596
|
+
},
|
|
597
|
+
'resource-declarations': {
|
|
598
|
+
...durableExternal,
|
|
599
|
+
guidance: {
|
|
600
|
+
docs: [trailContractDocs],
|
|
601
|
+
relatedRules: ['resource-exists'],
|
|
602
|
+
steps: [
|
|
603
|
+
'Declare each external dependency in the trail resources array.',
|
|
604
|
+
'Access statically known resources through the resource definition helper rather than constructing dependencies inline.',
|
|
605
|
+
],
|
|
606
|
+
summary:
|
|
607
|
+
'Keep infrastructure dependencies declared on the trail contract.',
|
|
608
|
+
},
|
|
609
|
+
invariant: 'Resource usage is declared on the trail contract.',
|
|
610
|
+
tier: 'source-static',
|
|
611
|
+
},
|
|
612
|
+
'resource-exists': {
|
|
613
|
+
...durableExternal,
|
|
614
|
+
guidance: {
|
|
615
|
+
docs: [trailContractDocs, wardenDocs],
|
|
616
|
+
relatedRules: ['resource-declarations'],
|
|
617
|
+
steps: [
|
|
618
|
+
'Define the referenced resource in project source or import the existing resource definition.',
|
|
619
|
+
'When a resource is testable, include a mock factory so contract tests can run without real infrastructure.',
|
|
620
|
+
],
|
|
621
|
+
summary:
|
|
622
|
+
'Make declared resources resolve to authored resource definitions.',
|
|
623
|
+
},
|
|
624
|
+
invariant: 'Declared resources resolve to known resource definitions.',
|
|
625
|
+
tier: 'project-static',
|
|
626
|
+
},
|
|
627
|
+
'resource-id-grammar': {
|
|
628
|
+
...durableExternal,
|
|
629
|
+
invariant: 'Resource identifiers stay out of the scope separator grammar.',
|
|
630
|
+
tier: 'source-static',
|
|
631
|
+
},
|
|
632
|
+
'resource-mock-coverage': {
|
|
633
|
+
...durableExternal,
|
|
634
|
+
guidance: {
|
|
635
|
+
docs: [trailContractDocs],
|
|
636
|
+
relatedRules: ['resource-declarations', 'resource-exists'],
|
|
637
|
+
steps: [
|
|
638
|
+
'Add a mock() factory so testAll(app) can provision the resource without production configuration.',
|
|
639
|
+
'If the resource genuinely cannot be mocked, declare unmockable: { reason } to record that intent.',
|
|
640
|
+
],
|
|
641
|
+
summary:
|
|
642
|
+
'Make each resource declare a test mock or an explicit unmockable reason.',
|
|
643
|
+
},
|
|
644
|
+
invariant:
|
|
645
|
+
'Resource definitions declare a mock factory or an explicit unmockable reason.',
|
|
646
|
+
tier: 'source-static',
|
|
647
|
+
},
|
|
648
|
+
'scheduled-destroy-intent': {
|
|
649
|
+
...durableExternal,
|
|
650
|
+
invariant:
|
|
651
|
+
'Schedule-activated destroy trails make unattended destructive work visible for review.',
|
|
652
|
+
tier: 'topo-aware',
|
|
653
|
+
},
|
|
654
|
+
'signal-graph-coaching': {
|
|
655
|
+
...durableExternal,
|
|
656
|
+
invariant:
|
|
657
|
+
'Typed signal contracts either declare a producer or participate in reactive consumption.',
|
|
658
|
+
tier: 'topo-aware',
|
|
659
|
+
},
|
|
660
|
+
'static-resource-accessor-preference': {
|
|
661
|
+
...durableExternal,
|
|
662
|
+
guidance: {
|
|
663
|
+
docs: [trailContractDocs],
|
|
664
|
+
relatedRules: ['resource-declarations', 'resource-exists'],
|
|
665
|
+
steps: [
|
|
666
|
+
'Replace ctx.resource(db) or ctx.resource("id") with db.from(ctx) when the resource definition is statically in scope.',
|
|
667
|
+
'Move external client construction behind resource() and declare that resource on the trail contract.',
|
|
668
|
+
'Keep ctx.resource(...) for dynamic IDs, generic framework code, or cases where the definition is not statically available.',
|
|
669
|
+
],
|
|
670
|
+
summary:
|
|
671
|
+
'Use statically scoped resource helpers when the resource definition is already available.',
|
|
672
|
+
},
|
|
673
|
+
invariant:
|
|
674
|
+
'Trail logic should prefer static resource helpers over dynamic accessors.',
|
|
675
|
+
scope: 'advisory',
|
|
676
|
+
tier: 'source-static',
|
|
677
|
+
},
|
|
678
|
+
'surface-overlay-coherence': {
|
|
679
|
+
...durableExternal,
|
|
680
|
+
guidance: {
|
|
681
|
+
relatedRules: [
|
|
682
|
+
'cli-command-route-coherence',
|
|
683
|
+
'surface-trailhead-coherence',
|
|
684
|
+
],
|
|
685
|
+
steps: [
|
|
686
|
+
'Point every binding selector at an existing trail id or dotted trail-id glob.',
|
|
687
|
+
'Narrow overlapping grouped bindings so each trail has one grouped owner per surface.',
|
|
688
|
+
'Rename bindings that shadow canonical CLI command paths or derived MCP tool names.',
|
|
689
|
+
],
|
|
690
|
+
summary:
|
|
691
|
+
'Keep surface overlay bindings pointed at real trails without shadowing canonical surface entries.',
|
|
692
|
+
},
|
|
693
|
+
invariant:
|
|
694
|
+
'Surface overlay bindings resolve to real trails without group overlap or canonical-entry shadowing.',
|
|
695
|
+
tier: 'topo-aware',
|
|
696
|
+
},
|
|
697
|
+
'surface-trailhead-coherence': {
|
|
698
|
+
...durableExternal,
|
|
699
|
+
guidance: {
|
|
700
|
+
docs: [
|
|
701
|
+
{
|
|
702
|
+
label: 'Trailheads ADR',
|
|
703
|
+
path: 'docs/adr/drafts/20260603-surface-trailheads-shape-dense-topos.md',
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
steps: [
|
|
707
|
+
'Keep trailhead selectors as explicit string literals or literal arrays when possible.',
|
|
708
|
+
'Ensure each public trail belongs to one trailhead owner.',
|
|
709
|
+
'Record explicit visibility-widening acceptance and stable-description metadata when a trailhead intentionally widens visibility.',
|
|
710
|
+
],
|
|
711
|
+
summary:
|
|
712
|
+
'Keep trailhead maps reviewable before they reach MCP projection.',
|
|
713
|
+
},
|
|
714
|
+
invariant:
|
|
715
|
+
'Trailhead maps avoid selector overlap, hidden visibility widening, and drift-prone dynamic selectors.',
|
|
716
|
+
tier: 'source-static',
|
|
717
|
+
},
|
|
718
|
+
'trail-fork-coaching': {
|
|
719
|
+
...durableExternal,
|
|
720
|
+
guidance: {
|
|
721
|
+
docs: [
|
|
722
|
+
{
|
|
723
|
+
label: 'ADR-0050 Surface Accommodations',
|
|
724
|
+
path: 'docs/adr/0050-surface-accommodations-preserve-trail-identity.md',
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
label: 'Surface Accommodations',
|
|
728
|
+
path: 'docs/surfaces/surface-accommodations.md',
|
|
729
|
+
},
|
|
730
|
+
],
|
|
731
|
+
relatedRules: [
|
|
732
|
+
'surface-trailhead-coherence',
|
|
733
|
+
'cli-command-route-coherence',
|
|
734
|
+
],
|
|
735
|
+
steps: [
|
|
736
|
+
'Check the semantic fork boundary: intent, permits, outputs, errors, lifecycle, and side effects.',
|
|
737
|
+
'Check the structural fork boundary: selected trail identity stays visible instead of hiding behind action vocabulary.',
|
|
738
|
+
'Split real capability forks into distinct trails or a composing trail.',
|
|
739
|
+
'Use a trailhead only when one surface entry needs to group multiple trails while preserving selected member identity.',
|
|
740
|
+
],
|
|
741
|
+
summary:
|
|
742
|
+
'Keep surface accommodations from hiding several capabilities behind one branching trail input.',
|
|
743
|
+
},
|
|
744
|
+
invariant:
|
|
745
|
+
'Trails avoid hiding distinct capabilities behind branching action or operation inputs.',
|
|
746
|
+
scope: 'advisory',
|
|
747
|
+
tier: 'source-static',
|
|
748
|
+
},
|
|
749
|
+
'trailhead-override-divergence': {
|
|
750
|
+
...durableExternal,
|
|
751
|
+
guidance: {
|
|
752
|
+
docs: [
|
|
753
|
+
{
|
|
754
|
+
label: 'Surface Accommodations',
|
|
755
|
+
path: 'docs/surfaces/surface-accommodations.md',
|
|
756
|
+
},
|
|
757
|
+
],
|
|
758
|
+
relatedRules: [
|
|
759
|
+
'surface-overlay-coherence',
|
|
760
|
+
'surface-trailhead-coherence',
|
|
761
|
+
],
|
|
762
|
+
steps: [
|
|
763
|
+
'Author the grouped entry as an mcp list binding in surfaceOverlay({ mcp }) so the lock carries the default.',
|
|
764
|
+
'Keep the call-site trailhead map aligned with the authored binding names and member selectors, or rename one side to make the intentional divergence explicit.',
|
|
765
|
+
],
|
|
766
|
+
summary:
|
|
767
|
+
'Keep call-site MCP trailhead overrides aligned with the authored overlay default.',
|
|
768
|
+
},
|
|
769
|
+
invariant:
|
|
770
|
+
'Call-site MCP trailhead maps stay aligned with authored surfaces overlay mcp bindings.',
|
|
771
|
+
tier: 'project-static',
|
|
772
|
+
},
|
|
773
|
+
'unmaterialized-activation-source': {
|
|
774
|
+
...durableExternal,
|
|
775
|
+
invariant:
|
|
776
|
+
'Activation sources have an available runtime materializer before runtime delivery is assumed.',
|
|
777
|
+
tier: 'topo-aware',
|
|
778
|
+
},
|
|
779
|
+
'unreachable-detour-shadowing': {
|
|
780
|
+
...durableExternal,
|
|
781
|
+
invariant: 'Specific detours are not shadowed by earlier broader detours.',
|
|
782
|
+
tier: 'source-static',
|
|
783
|
+
},
|
|
784
|
+
'valid-describe-refs': {
|
|
785
|
+
invariant: 'Describe references point at known Trails concepts.',
|
|
786
|
+
lifecycle: { state: 'durable' },
|
|
787
|
+
scope: 'advisory',
|
|
788
|
+
tier: 'project-static',
|
|
789
|
+
},
|
|
790
|
+
'valid-detour-contract': {
|
|
791
|
+
...durableExternal,
|
|
792
|
+
invariant:
|
|
793
|
+
'Runtime detour contracts use error constructors and recover functions.',
|
|
794
|
+
tier: 'topo-aware',
|
|
795
|
+
},
|
|
796
|
+
'version-gap': {
|
|
797
|
+
...durableExternal,
|
|
798
|
+
invariant:
|
|
799
|
+
'Trail version coverage remains contiguous through the current version.',
|
|
800
|
+
tier: 'topo-aware',
|
|
801
|
+
},
|
|
802
|
+
'version-pinned-compose': {
|
|
803
|
+
...durableExternal,
|
|
804
|
+
invariant:
|
|
805
|
+
'Version-pinned ctx.compose() calls stay visible migration debt.',
|
|
806
|
+
tier: 'source-static',
|
|
807
|
+
},
|
|
808
|
+
'version-without-examples': {
|
|
809
|
+
...durableExternal,
|
|
810
|
+
invariant: 'Live historical version entries include examples.',
|
|
811
|
+
tier: 'topo-aware',
|
|
812
|
+
},
|
|
813
|
+
'warden-export-symmetry': {
|
|
814
|
+
...durableRepoLocal,
|
|
815
|
+
invariant: 'The Warden package exports trail wrappers, not raw rules.',
|
|
816
|
+
tier: 'source-static',
|
|
817
|
+
},
|
|
818
|
+
'warden-rules-use-ast': {
|
|
819
|
+
...durableRepoLocal,
|
|
820
|
+
invariant:
|
|
821
|
+
'Warden source rules use AST helpers instead of ad hoc parsing or raw node-field casts.',
|
|
822
|
+
tier: 'source-static',
|
|
823
|
+
},
|
|
824
|
+
'webhook-route-collision': {
|
|
825
|
+
...durableExternal,
|
|
826
|
+
invariant:
|
|
827
|
+
'Webhook routes do not collide with each other or direct HTTP trail routes.',
|
|
828
|
+
tier: 'topo-aware',
|
|
829
|
+
},
|
|
830
|
+
} as const satisfies Record<string, BuiltinWardenRuleMetadataInput>;
|
|
831
|
+
|
|
832
|
+
export type BuiltinWardenRuleName = keyof typeof builtinWardenRuleMetadataInput;
|
|
833
|
+
|
|
834
|
+
const withRuleDefaults = (
|
|
835
|
+
name: string,
|
|
836
|
+
metadata: BuiltinWardenRuleMetadataInput
|
|
837
|
+
): WardenRuleMetadata => ({
|
|
838
|
+
concern: concernByRuleName[name] ?? 'general',
|
|
839
|
+
depth: metadata.scope === 'advisory' ? 'all' : depthByTier[metadata.tier],
|
|
840
|
+
...metadata,
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
export const builtinWardenRuleMetadata = Object.fromEntries(
|
|
844
|
+
Object.entries(builtinWardenRuleMetadataInput).map(([name, metadata]) => [
|
|
845
|
+
name,
|
|
846
|
+
withRuleDefaults(name, metadata),
|
|
847
|
+
])
|
|
848
|
+
) as Readonly<Record<BuiltinWardenRuleName, WardenRuleMetadata>>;
|
|
849
|
+
|
|
850
|
+
const metadataByName: Readonly<Record<string, WardenRuleMetadata>> =
|
|
851
|
+
builtinWardenRuleMetadata;
|
|
852
|
+
|
|
853
|
+
export const getWardenRuleMetadata = (
|
|
854
|
+
rule: Pick<WardenRule, 'metadata' | 'name'> | string
|
|
855
|
+
): WardenRuleMetadata | null => {
|
|
856
|
+
if (typeof rule !== 'string' && rule.metadata) {
|
|
857
|
+
return rule.metadata;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
const name = typeof rule === 'string' ? rule : rule.name;
|
|
861
|
+
return metadataByName[name] ?? null;
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
export const listWardenRuleMetadata = (): readonly (readonly [
|
|
865
|
+
BuiltinWardenRuleName,
|
|
866
|
+
WardenRuleMetadata,
|
|
867
|
+
])[] =>
|
|
868
|
+
Object.entries(builtinWardenRuleMetadata) as readonly (readonly [
|
|
869
|
+
BuiltinWardenRuleName,
|
|
870
|
+
WardenRuleMetadata,
|
|
871
|
+
])[];
|