@jasonbelmonti/markdown-engine 2.0.0 → 3.0.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 (152) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +65 -57
  3. package/dist/api/declarative-validation.d.ts +2 -2
  4. package/dist/api/declarative-validation.d.ts.map +1 -1
  5. package/dist/api/declarative-validation.js +53 -31
  6. package/dist/api/declarative-validation.js.map +1 -1
  7. package/dist/cli/declarative-validation.d.ts.map +1 -1
  8. package/dist/cli/declarative-validation.js +7 -12
  9. package/dist/cli/declarative-validation.js.map +1 -1
  10. package/dist/declarative-validation/applicability/classifier.d.ts +18 -0
  11. package/dist/declarative-validation/applicability/classifier.d.ts.map +1 -0
  12. package/dist/declarative-validation/applicability/classifier.js +37 -0
  13. package/dist/declarative-validation/applicability/classifier.js.map +1 -0
  14. package/dist/declarative-validation/applicability/index.d.ts +2 -0
  15. package/dist/declarative-validation/applicability/index.d.ts.map +1 -0
  16. package/dist/declarative-validation/applicability/index.js +2 -0
  17. package/dist/declarative-validation/applicability/index.js.map +1 -0
  18. package/dist/declarative-validation/assertions/context.d.ts +2 -2
  19. package/dist/declarative-validation/assertions/context.d.ts.map +1 -1
  20. package/dist/declarative-validation/assertions/diagnostics.d.ts +7 -4
  21. package/dist/declarative-validation/assertions/diagnostics.d.ts.map +1 -1
  22. package/dist/declarative-validation/assertions/diagnostics.js +26 -1
  23. package/dist/declarative-validation/assertions/diagnostics.js.map +1 -1
  24. package/dist/declarative-validation/assertions/evaluator.d.ts +2 -2
  25. package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
  26. package/dist/declarative-validation/assertions/evaluator.js +3 -0
  27. package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
  28. package/dist/declarative-validation/assertions/group-evaluator.d.ts +6 -0
  29. package/dist/declarative-validation/assertions/group-evaluator.d.ts.map +1 -0
  30. package/dist/declarative-validation/assertions/group-evaluator.js +57 -0
  31. package/dist/declarative-validation/assertions/group-evaluator.js.map +1 -0
  32. package/dist/declarative-validation/assertions/id-targets.d.ts +15 -0
  33. package/dist/declarative-validation/assertions/id-targets.d.ts.map +1 -1
  34. package/dist/declarative-validation/assertions/id-targets.js +18 -55
  35. package/dist/declarative-validation/assertions/id-targets.js.map +1 -1
  36. package/dist/declarative-validation/assertions/ids.d.ts.map +1 -1
  37. package/dist/declarative-validation/assertions/ids.js +100 -14
  38. package/dist/declarative-validation/assertions/ids.js.map +1 -1
  39. package/dist/declarative-validation/assertions/index.d.ts +1 -0
  40. package/dist/declarative-validation/assertions/index.d.ts.map +1 -1
  41. package/dist/declarative-validation/assertions/index.js +1 -0
  42. package/dist/declarative-validation/assertions/index.js.map +1 -1
  43. package/dist/declarative-validation/assertions/table-column-coverage.d.ts +9 -0
  44. package/dist/declarative-validation/assertions/table-column-coverage.d.ts.map +1 -0
  45. package/dist/declarative-validation/assertions/table-column-coverage.js +108 -0
  46. package/dist/declarative-validation/assertions/table-column-coverage.js.map +1 -0
  47. package/dist/declarative-validation/compiler/applicability-plan.d.ts +5 -0
  48. package/dist/declarative-validation/compiler/applicability-plan.d.ts.map +1 -0
  49. package/dist/declarative-validation/compiler/applicability-plan.js +22 -0
  50. package/dist/declarative-validation/compiler/applicability-plan.js.map +1 -0
  51. package/dist/declarative-validation/compiler/assertion-builders.d.ts +2 -1
  52. package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
  53. package/dist/declarative-validation/compiler/assertion-builders.js +51 -13
  54. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
  55. package/dist/declarative-validation/compiler/assertion-shapes.d.ts +3 -1
  56. package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -1
  57. package/dist/declarative-validation/compiler/assertion-shapes.js +90 -0
  58. package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -1
  59. package/dist/declarative-validation/compiler/assertions.d.ts +2 -1
  60. package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
  61. package/dist/declarative-validation/compiler/assertions.js +14 -4
  62. package/dist/declarative-validation/compiler/assertions.js.map +1 -1
  63. package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -1
  64. package/dist/declarative-validation/compiler/compatibility.js +1 -0
  65. package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
  66. package/dist/declarative-validation/compiler/group-plans.d.ts +6 -0
  67. package/dist/declarative-validation/compiler/group-plans.d.ts.map +1 -0
  68. package/dist/declarative-validation/compiler/group-plans.js +67 -0
  69. package/dist/declarative-validation/compiler/group-plans.js.map +1 -0
  70. package/dist/declarative-validation/compiler/index.d.ts +1 -1
  71. package/dist/declarative-validation/compiler/index.d.ts.map +1 -1
  72. package/dist/declarative-validation/compiler/index.js +70 -35
  73. package/dist/declarative-validation/compiler/index.js.map +1 -1
  74. package/dist/declarative-validation/compiler/plan.d.ts +72 -4
  75. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
  76. package/dist/declarative-validation/compiler/rule-fields.d.ts +6 -0
  77. package/dist/declarative-validation/compiler/rule-fields.d.ts.map +1 -0
  78. package/dist/declarative-validation/compiler/rule-fields.js +40 -0
  79. package/dist/declarative-validation/compiler/rule-fields.js.map +1 -0
  80. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.d.ts +0 -1
  81. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.d.ts.map +1 -1
  82. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.js +8 -2
  83. package/dist/declarative-validation/diagnostics/profile-config-diagnostics.js.map +1 -1
  84. package/dist/declarative-validation/evidence/index.d.ts +3 -3
  85. package/dist/declarative-validation/evidence/index.d.ts.map +1 -1
  86. package/dist/declarative-validation/evidence/index.js +6 -6
  87. package/dist/declarative-validation/evidence/index.js.map +1 -1
  88. package/dist/declarative-validation/profile/applicability-schema.d.ts +4 -0
  89. package/dist/declarative-validation/profile/applicability-schema.d.ts.map +1 -0
  90. package/dist/declarative-validation/profile/applicability-schema.js +24 -0
  91. package/dist/declarative-validation/profile/applicability-schema.js.map +1 -0
  92. package/dist/declarative-validation/profile/assertion-schema.d.ts +2 -1
  93. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
  94. package/dist/declarative-validation/profile/assertion-schema.js +149 -14
  95. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
  96. package/dist/declarative-validation/profile/direct-profile-diagnostics.d.ts +3 -1
  97. package/dist/declarative-validation/profile/direct-profile-diagnostics.d.ts.map +1 -1
  98. package/dist/declarative-validation/profile/direct-profile-diagnostics.js +10 -2
  99. package/dist/declarative-validation/profile/direct-profile-diagnostics.js.map +1 -1
  100. package/dist/declarative-validation/profile/group-schema.d.ts +5 -0
  101. package/dist/declarative-validation/profile/group-schema.d.ts.map +1 -0
  102. package/dist/declarative-validation/profile/group-schema.js +55 -0
  103. package/dist/declarative-validation/profile/group-schema.js.map +1 -0
  104. package/dist/declarative-validation/profile/ids-assertion-contract.d.ts +12 -0
  105. package/dist/declarative-validation/profile/ids-assertion-contract.d.ts.map +1 -0
  106. package/dist/declarative-validation/profile/ids-assertion-contract.js +32 -0
  107. package/dist/declarative-validation/profile/ids-assertion-contract.js.map +1 -0
  108. package/dist/declarative-validation/profile/index.d.ts +42 -2
  109. package/dist/declarative-validation/profile/index.d.ts.map +1 -1
  110. package/dist/declarative-validation/profile/index.js.map +1 -1
  111. package/dist/declarative-validation/profile/materialization.d.ts.map +1 -1
  112. package/dist/declarative-validation/profile/materialization.js +13 -10
  113. package/dist/declarative-validation/profile/materialization.js.map +1 -1
  114. package/dist/declarative-validation/profile/schema.d.ts.map +1 -1
  115. package/dist/declarative-validation/profile/schema.js +73 -12
  116. package/dist/declarative-validation/profile/schema.js.map +1 -1
  117. package/dist/declarative-validation/profile/syntax-version.d.ts +7 -0
  118. package/dist/declarative-validation/profile/syntax-version.d.ts.map +1 -0
  119. package/dist/declarative-validation/profile/syntax-version.js +11 -0
  120. package/dist/declarative-validation/profile/syntax-version.js.map +1 -0
  121. package/dist/declarative-validation/results/clone-rule-result.d.ts +6 -0
  122. package/dist/declarative-validation/results/clone-rule-result.d.ts.map +1 -0
  123. package/dist/declarative-validation/results/clone-rule-result.js +125 -0
  124. package/dist/declarative-validation/results/clone-rule-result.js.map +1 -0
  125. package/dist/declarative-validation/results/create-result.d.ts +15 -0
  126. package/dist/declarative-validation/results/create-result.d.ts.map +1 -0
  127. package/dist/declarative-validation/results/create-result.js +58 -0
  128. package/dist/declarative-validation/results/create-result.js.map +1 -0
  129. package/dist/declarative-validation/results/index.d.ts +3 -25
  130. package/dist/declarative-validation/results/index.d.ts.map +1 -1
  131. package/dist/declarative-validation/results/index.js +2 -1
  132. package/dist/declarative-validation/results/index.js.map +1 -1
  133. package/dist/declarative-validation/results/skipped-rule-result.d.ts +10 -0
  134. package/dist/declarative-validation/results/skipped-rule-result.d.ts.map +1 -0
  135. package/dist/declarative-validation/results/skipped-rule-result.js +18 -0
  136. package/dist/declarative-validation/results/skipped-rule-result.js.map +1 -0
  137. package/dist/declarative-validation/results/types.d.ts +77 -0
  138. package/dist/declarative-validation/results/types.d.ts.map +1 -0
  139. package/dist/declarative-validation/results/types.js +2 -0
  140. package/dist/declarative-validation/results/types.js.map +1 -0
  141. package/dist/declarative-validation/selectors/table-targets.d.ts +23 -0
  142. package/dist/declarative-validation/selectors/table-targets.d.ts.map +1 -1
  143. package/dist/declarative-validation/selectors/table-targets.js +52 -9
  144. package/dist/declarative-validation/selectors/table-targets.js.map +1 -1
  145. package/dist-bundled/markdown-engine-cli.mjs +26826 -0
  146. package/docs/contracts/api.md +19 -15
  147. package/docs/contracts/declarative-validation.md +319 -36
  148. package/package.json +8 -3
  149. package/skills/profile-backed-markdown/SKILL.md +54 -0
  150. package/skills/profile-backed-markdown/assets/profiles/operational-spec.yaml +98 -0
  151. package/skills/profile-backed-markdown/references/repair-brief.md +14 -0
  152. package/skills/profile-backed-markdown/scripts/validate-profile-backed-markdown.mjs +590 -0
@@ -1,10 +1,10 @@
1
1
  # Public API Contract
2
2
 
3
- Status: package 2.0.0, document contract 1.0.0
4
- Last updated: 2026-05-13
3
+ Status: package 3.0.0, document contract 1.0.0
4
+ Last updated: 2026-06-05
5
5
 
6
6
  This document defines the public `@jasonbelmonti/markdown-engine` package
7
- contract for the `2.0.0` package release. The serialized rich IR document
7
+ contract for the `3.0.0` package release. The serialized rich IR document
8
8
  contract remains `documentVersion: "1.0.0"`. The stable public surface is the
9
9
  package export from `@jasonbelmonti/markdown-engine`, not internal adapter
10
10
  modules or raw parser output. The 1.0 rich IR design is tracked in
@@ -72,7 +72,7 @@ normalize(parsed: ParsedMarkdown, options?: NormalizeOptions): NormalizeResult
72
72
  ```
73
73
 
74
74
  `NormalizeOptions.documentVersion` selects the document contract version.
75
- Package 2.0 defaults omitted `documentVersion` to the rich IR
75
+ Package 3.0 keeps omitted `documentVersion` defaulting to the rich IR
76
76
  `"1.0.0"` contract. The retained `0.1.0`-compatible path is `"0.0.0"` and must
77
77
  be requested explicitly.
78
78
 
@@ -227,7 +227,9 @@ validateWithProfile(
227
227
 
228
228
  `parseValidationProfile` accepts YAML text or JSON-safe profile objects. The
229
229
  top-level profile keys are `syntaxVersion`, `documentVersion`, and `rules`.
230
- `syntaxVersion` must be `"markdown-engine.validation@v1"`.
230
+ `syntaxVersion` must be `"markdown-engine.validation@v1"` or
231
+ `"markdown-engine.validation@v2"`; v2 admission is limited to the existing flat
232
+ rule shape.
231
233
  `documentVersion` is optional; when provided it must be `"0.0.0"` or
232
234
  `"1.0.0"`. The parser preserves omission and does not inject a default into
233
235
  the parsed profile.
@@ -309,7 +311,7 @@ code nodes with `kind: "fenced"`. Raw parser node objects are not public.
309
311
 
310
312
  ## 1.0 Contract
311
313
 
312
- The final 1.0 document contract is selected by default in package 2.0, remains
314
+ The final 1.0 document contract is selected by default in package 3.0, remains
313
315
  available explicitly as `documentVersion: "1.0.0"`, and is checked with
314
316
  `compatibilityMode: "default"`.
315
317
 
@@ -536,8 +538,8 @@ serialization behavior.
536
538
 
537
539
  ### Compatibility And Migration
538
540
 
539
- The current package version is `2.0.0`. The serialized document contract
540
- version remains `"1.0.0"`. Package 2.0 selects that rich IR contract by
541
+ The current package version is `3.0.0`. The serialized document contract
542
+ version remains `"1.0.0"`. Package 3.0 selects that rich IR contract by
541
543
  default for `normalize(parsed)`; callers may also request it explicitly with
542
544
  `normalize(..., { documentVersion: "1.0.0" })`. Serialization gates check it
543
545
  with `compatibilityMode: "default"`.
@@ -550,7 +552,7 @@ compatibility from the absence of rich IR fields.
550
552
  Migration from the `0.1.0` document shape, or from pre-2.0 API callers that
551
553
  depended on implicit legacy normalization, requires consumers to:
552
554
 
553
- - use package 2.0's default `normalize(parsed)` rich IR output or request
555
+ - use package 3.0's default `normalize(parsed)` rich IR output or request
554
556
  `documentVersion: "1.0.0"` during normalization;
555
557
  - read `target`, `sections`, `textSpans`, `tables`, `lists`, `links`,
556
558
  `linkReferences`, and `source` from the normalized document instead of
@@ -609,10 +611,10 @@ exit with code `2`. Validation success exits with code `0`; validation or
609
611
  normalization error diagnostics exit with code `1`. Validation JSON includes
610
612
  `profile`, `ruleResults`, `diagnostics`, and `evidence`.
611
613
 
612
- Semver classification: package 2.0 makes the rich IR contract the default API
613
- normalization output while retaining the document contract version
614
- `"1.0.0"`. This is breaking for API consumers that call `normalize(parsed)` and
615
- expect the legacy `0.0.0` document shape. Migration is to either consume the
614
+ Semver classification: package 3.0 keeps the rich IR contract as the default
615
+ API normalization output while retaining the document contract version
616
+ `"1.0.0"`. The 2.0 migration remains applicable for API consumers that call
617
+ `normalize(parsed)` and expect the legacy `0.0.0` document shape: consume the
616
618
  rich IR fields or pin `documentVersion: "0.0.0"` until the downstream consumer
617
619
  is ready. CLI consumers can still pin `--document-version 0.0.0` for explicit
618
620
  legacy output.
@@ -651,8 +653,10 @@ The `0.1.0` contract was review-gated by WP-2, MS-2, and MS-3 before first
651
653
  publication. From the published `0.1.0` baseline forward, changes to public API
652
654
  signatures, result fields, diagnostic schema, source-location semantics,
653
655
  validation config semantics, or serialized output shape require
654
- semantic-version classification. The planned 1.0 rich IR contract will update
655
- this API contract before 1.0 release approval.
656
+ semantic-version classification. The 1.0 rich IR document contract is now the
657
+ retained default document contract for package 3.0, and the explicit legacy
658
+ `0.0.0` path remains the compatibility boundary for consumers that still need
659
+ the first package-line output shape.
656
660
 
657
661
  The `@jasonbelmonti/markdown-engine` package boundary remains limited to
658
662
  parsing, normalization, deterministic validation, diagnostics, and
@@ -1,19 +1,36 @@
1
1
  # Declarative Validation Contract
2
2
 
3
- Status: package 2.0.0, v1 profile syntax, document contract 1.0.0
4
- Last updated: 2026-05-14
3
+ Status: package 3.0.0, v1 profile syntax with v2 Conditional V2, document contract 1.0.0
4
+ Last updated: 2026-06-05
5
+ Current v2 surface: flat-rule result/evidence shell, ID count-bound schema and
6
+ runtime evaluator contract, plus `tableColumnCoverage` schema, compiled-plan,
7
+ and runtime evaluator contract, grouped rule runtime contract, and rule-level
8
+ `when` schema, matcher, public skipped-rule result, skipped counts, and evidence
9
+ cloning contract.
5
10
 
6
11
  This document defines the public declarative validation contract for
7
12
  `@jasonbelmonti/markdown-engine`. The stable surface is the package-root API,
8
- the v1 profile syntax, the CLI validation command, diagnostic codes, serialized
9
- result shapes, and evidence fields. Internal parser output, compiled rule-plan
10
- records, selector target records, and evaluator implementation modules are not
11
- public contracts.
12
-
13
- Package 2.0 does not introduce `documentVersion: "2.0.0"` or
14
- `markdown-engine.validation@v2`. Declarative validation continues to use the v1
15
- profile syntax against the existing `documentVersion: "1.0.0"` rich IR
16
- document contract.
13
+ the v1 profile syntax, the admitted v2 profile syntax and runtime subset, the
14
+ CLI validation command, diagnostic codes, serialized result shapes, and evidence
15
+ fields. Internal parser output, compiled rule-plan records, selector target
16
+ records, and evaluator implementation modules are not public contracts.
17
+
18
+ Package 3.0 does not introduce `documentVersion: "3.0.0"` or CLI JSON
19
+ discrimination.
20
+ Declarative validation continues to use the existing `documentVersion: "1.0.0"`
21
+ rich IR document contract, while the profile admission path recognizes
22
+ `markdown-engine.validation@v2` for the same flat rule shape with `id`, optional
23
+ `severity`, `select`, and `assert`; non-recursive `anyOf` and `allOf`; and
24
+ optional rule-level `when`. The admitted v2 path exposes the result and evidence
25
+ shell needed to distinguish assertion, grouped, and skipped evaluation output
26
+ from v1 output, plus the ID count-bound schema, compiled-plan, and runtime
27
+ evaluator contract; the `tableColumnCoverage` schema, compiled-plan, and
28
+ runtime evaluator contract; and the `when` schema plus private compiled-plan and
29
+ matcher contract. Matched applicability continues into normal rule evaluation.
30
+ Non-matching applicability returns a public skipped rule result with
31
+ `status: "skipped"`, `passed: true`, `evaluation.kind: "skipped"`,
32
+ `reason: "whenNotMatched"`, `skippedRuleCount`, no top-level diagnostics, and a
33
+ nested `when` applicability result.
17
34
 
18
35
  ## 1.0 Contract
19
36
 
@@ -53,8 +70,25 @@ syntaxVersion: markdown-engine.validation@v1
53
70
  `syntaxVersion` is required. Missing or unsupported values emit
54
71
  `profile.config.unsupportedSyntaxVersion`.
55
72
 
56
- The v1 vocabulary is closed. Unknown profile keys, rule keys, selector keys,
57
- known assertion keys, and nested assertion keys emit
73
+ The v2 syntax is admitted as an additive profile syntax:
74
+
75
+ ```yaml
76
+ syntaxVersion: markdown-engine.validation@v2
77
+ ```
78
+
79
+ This release recognizes v2 as a distinct syntax version at profile admission,
80
+ admits ID count bounds at the schema, compiled-plan, and runtime evaluator
81
+ layers, admits `tableColumnCoverage` at the schema, internal compiled-plan, and
82
+ runtime evaluator layers, admits non-recursive grouped rules at the schema,
83
+ compiled-plan, and runtime evaluator layers, and admits optional rule-level
84
+ `when` at the schema, internal compiled-plan, and matcher layers. Matching
85
+ `when` rules continue through normal flat or grouped evaluation and do not add a
86
+ public `when` field to the evaluated rule result. Non-matching `when` rules are
87
+ not evaluated; they return the public skipped-rule result shape, increment
88
+ `skippedRuleCount`, and leave `evaluatedRuleCount` unchanged.
89
+
90
+ The admitted v1/v2 flat vocabulary is closed. Unknown profile keys, rule keys,
91
+ selector keys, known assertion keys, and nested assertion keys emit
58
92
  `profile.config.unsupportedKey` unless the contract assigns a more specific
59
93
  compile diagnostic for an unsupported selector target or unsupported assertion
60
94
  member.
@@ -113,15 +147,52 @@ results, and does not evaluate rules.
113
147
  The top-level profile shape is:
114
148
 
115
149
  ```ts
150
+ type ValidationProfileSyntaxVersion =
151
+ | "markdown-engine.validation@v1"
152
+ | "markdown-engine.validation@v2";
153
+
116
154
  interface ValidationProfile {
117
- syntaxVersion: "markdown-engine.validation@v1";
155
+ syntaxVersion: ValidationProfileSyntaxVersion;
118
156
  documentVersion?: EngineDocumentVersion;
119
157
  rules: readonly DeclarativeValidationRule[];
120
158
  }
121
159
 
122
- interface DeclarativeValidationRule {
160
+ type DeclarativeValidationRule =
161
+ | DeclarativeValidationFlatRule
162
+ | DeclarativeValidationGroupRule;
163
+
164
+ interface DeclarativeValidationRuleFields {
123
165
  id: string;
124
166
  severity?: "error" | "warning" | "info";
167
+ when?: DeclarativeValidationApplicability;
168
+ }
169
+
170
+ interface DeclarativeValidationFlatRule extends DeclarativeValidationRuleFields {
171
+ select: DeclarativeSelector;
172
+ assert: DeclarativeAssertion;
173
+ }
174
+
175
+ interface DeclarativeValidationApplicability {
176
+ select: DeclarativeSelector;
177
+ assert: DeclarativeAssertion;
178
+ }
179
+
180
+ type DeclarativeValidationGroupRule =
181
+ | DeclarativeValidationAnyOfRule
182
+ | DeclarativeValidationAllOfRule;
183
+
184
+ interface DeclarativeValidationAnyOfRule
185
+ extends DeclarativeValidationRuleFields {
186
+ anyOf: readonly DeclarativeValidationBranch[];
187
+ }
188
+
189
+ interface DeclarativeValidationAllOfRule
190
+ extends DeclarativeValidationRuleFields {
191
+ allOf: readonly DeclarativeValidationBranch[];
192
+ }
193
+
194
+ interface DeclarativeValidationBranch {
195
+ label?: string;
125
196
  select: DeclarativeSelector;
126
197
  assert: DeclarativeAssertion;
127
198
  }
@@ -134,6 +205,11 @@ evidence identify output by `ruleId`.
134
205
  Rule `severity` defaults to `error` when omitted. Unsupported severity values
135
206
  emit `profile.config.invalidShape`.
136
207
 
208
+ Rule-level `when` is allowed only on v2 rules. Branch-level `when` remains
209
+ unsupported. V1 profiles preserve the original flat rule authoring contract;
210
+ grouped `anyOf` / `allOf`, ID count bounds, `tableColumnCoverage`, and
211
+ rule-level `when` are v2 additions.
212
+
137
213
  Profile values must be JSON-safe data properties after YAML materialization.
138
214
  Functions, accessors, proxies, cyclic structures, sparse arrays, `undefined`
139
215
  payloads in required positions, non-finite numbers, and `__proto__` properties
@@ -203,11 +279,27 @@ interface DeclarativeAssertion {
203
279
  prefix?: string;
204
280
  unique?: boolean;
205
281
  caseSensitive?: boolean;
282
+ minCount?: number;
283
+ maxCount?: number;
206
284
  };
207
285
  references?: {
208
286
  idsFrom: { section?: string; column?: string; prefix?: string };
209
287
  mustAppearIn: readonly string[];
210
288
  };
289
+ tableColumnCoverage?: {
290
+ source: {
291
+ section: string;
292
+ column: string;
293
+ prefix?: string;
294
+ caseSensitive?: boolean;
295
+ };
296
+ target: {
297
+ section: string;
298
+ tableHeader?: readonly string[];
299
+ column: string;
300
+ };
301
+ require: "everySourceId";
302
+ };
211
303
  text?: {
212
304
  contains?: string;
213
305
  excludes?: readonly string[];
@@ -242,6 +334,7 @@ Selector/assertion compatibility is part of the public contract:
242
334
  | `tableColumnsRequired` | `table` |
243
335
  | `ids` | all supported selector targets |
244
336
  | `references` | `document` |
337
+ | `tableColumnCoverage` | `document` |
245
338
  | `text` | all supported selector targets |
246
339
  | `textOccurrenceCount` | all supported selector targets |
247
340
  | `textLength` | all supported selector targets |
@@ -258,9 +351,32 @@ resolves zero targets.
258
351
  headings appear as an ordered subsequence in the normalized section tree
259
352
  flattened in source order.
260
353
 
261
- `ids.unique` must be `true`; `prefix` and `caseSensitive` are modifiers, not
262
- standalone predicates. `caseSensitive` defaults to `true`. ID tokens use the
354
+ For v1 profiles, `ids.unique` must be `true`. For v2 profiles, `ids.unique`
355
+ must be `true` when provided and may be omitted when `ids.minCount` or
356
+ `ids.maxCount` provides the predicate. `prefix` and `caseSensitive` are modifiers,
357
+ not standalone predicates. `caseSensitive` defaults to `true`. ID tokens use the
263
358
  documented token grammar `[A-Za-z][A-Za-z0-9]*-[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*`.
359
+ For v2 profiles, `ids.minCount` and `ids.maxCount` are admitted as non-negative
360
+ integer schema and compiled-plan fields. When both are present, `minCount` must
361
+ be less than or equal to `maxCount`. Runtime count evaluation uses unique
362
+ comparison values after prefix filtering and duplicate occurrence de-duplication.
363
+ Failed lower and upper bounds emit `profile.validation.idCountTooLow` and
364
+ `profile.validation.idCountTooHigh`.
365
+
366
+ For v2 profiles, `tableColumnCoverage` is admitted as a flat-rule schema and
367
+ internal compiled-plan assertion. It is compatible only with a `document`
368
+ selector because the assertion owns its source and target table-column inputs.
369
+ `source.section`, `source.column`, `target.section`, and `target.column` are
370
+ required non-empty strings. `source.prefix` is optional and must be non-empty
371
+ when provided. `source.caseSensitive` is optional and defaults to `true` in the
372
+ compiled plan. `target.tableHeader` is an optional non-empty string array.
373
+ `require` must be exactly `"everySourceId"`. Runtime evaluation extracts unique
374
+ source IDs from `source.section` and `source.column`, applies `source.prefix`
375
+ and `source.caseSensitive`, and requires every source ID comparison value to
376
+ appear in the configured target table column. IDs appearing elsewhere in the
377
+ target section do not satisfy coverage. Missing target sections, missing target
378
+ columns, and missing target-column IDs emit deterministic validation diagnostics
379
+ source-grounded to the source ID when source evidence is available.
264
380
 
265
381
  `text` must include `contains` or a non-empty `excludes` array.
266
382
  `textOccurrenceCount.count` is a finite number and counts non-overlapping
@@ -284,11 +400,17 @@ error severity. Validation diagnostics use the rule severity. Source ranges are
284
400
  included when a selected target has source evidence; locations are omitted
285
401
  rather than fabricated when unavailable.
286
402
 
403
+ Rule-level `when` uses the existing validation diagnostic codes. When
404
+ applicability does not match, those diagnostics are cloned into
405
+ `ruleResults[].when.diagnostics`; they are not promoted into top-level
406
+ `diagnostics`, so a skipped rule with a nested error-severity applicability
407
+ diagnostic can still leave the aggregate `valid` value `true`.
408
+
287
409
  | Code | Severity source | Emitted when |
288
410
  | --- | --- | --- |
289
411
  | `profile.config.invalidYaml` | `error` | YAML text cannot be parsed or materialized as JSON-safe profile data. |
290
412
  | `profile.config.yamlWarning` | `warning` | YAML materialization produces a non-fatal parser warning. |
291
- | `profile.config.unsupportedSyntaxVersion` | `error` | `syntaxVersion` is missing or is not `markdown-engine.validation@v1`. |
413
+ | `profile.config.unsupportedSyntaxVersion` | `error` | `syntaxVersion` is missing or is not `markdown-engine.validation@v1` or `markdown-engine.validation@v2`. |
292
414
  | `profile.config.invalidShape` | `error` | Required fields are missing, fields have wrong types, arrays or strings are empty, rule IDs duplicate, scalar values are invalid, table predicates are ineffective, or assertion payloads contain no effective predicate. |
293
415
  | `profile.config.documentVersionMismatch` | `error` | Resolved profile `documentVersion` differs from the supplied `EngineDocument.version`. |
294
416
  | `profile.config.unsupportedKey` | `error` | A closed profile, rule, selector, known assertion object, nested object, regex-like key, executable-like key, or direct typed profile object contains unsupported syntax. |
@@ -299,12 +421,17 @@ rather than fabricated when unavailable.
299
421
  | `profile.validation.assertionFailed` | Rule severity | A supported assertion evaluates and fails without a more specific diagnostic code, including missing table columns, exact occurrence-count mismatches, and text-length bound failures. |
300
422
  | `profile.validation.duplicateId` | Rule severity | An `ids.unique` assertion finds repeated IDs. |
301
423
  | `profile.validation.frontmatterFieldMissing` | Rule severity | A required frontmatter field is absent. |
424
+ | `profile.validation.idCountTooHigh` | Rule severity | Unique ID count after filtering is higher than `ids.maxCount`. |
425
+ | `profile.validation.idCountTooLow` | Rule severity | Unique ID count after filtering is lower than `ids.minCount`. |
302
426
  | `profile.validation.referenceMissing` | Rule severity | A source ID is absent from a required target section. |
303
427
  | `profile.validation.sectionMissing` | Rule severity | A required section heading is absent. |
304
428
  | `profile.validation.sectionOrder` | Rule severity | A strict required-section order cannot be satisfied. |
429
+ | `profile.validation.tableColumnCoverageIdMissing` | Rule severity | A source ID is absent from the configured target table column. |
430
+ | `profile.validation.tableColumnCoverageTargetColumnMissing` | Rule severity | The configured target table column cannot be resolved. |
431
+ | `profile.validation.tableColumnCoverageTargetSectionMissing` | Rule severity | The configured target section cannot be resolved. |
305
432
  | `profile.validation.textExcluded` | Rule severity | A selected target contains forbidden literal text. |
306
433
  | `profile.validation.textMissing` | Rule severity | A selected target lacks required literal text from a `text.contains` assertion. |
307
- | `profile.validation.assertionUnsupported` | `error` | A compiled assertion has no evaluator implementation; this is an internal safety diagnostic. |
434
+ | `profile.validation.assertionUnsupported` | `error` | A compiled assertion or compiled assertion feature has no evaluator implementation; this is an internal safety diagnostic. |
308
435
 
309
436
  ## Result Shape
310
437
 
@@ -314,9 +441,12 @@ rather than fabricated when unavailable.
314
441
  interface DeclarativeValidationResult extends ValidationResult {
315
442
  valid: boolean;
316
443
  diagnostics: readonly MarkdownDiagnostic[];
317
- ruleResults: readonly ValidationRuleResult[];
444
+ ruleResults: readonly (
445
+ | ValidationRuleResult
446
+ | DeclarativeValidationRuleResultV2
447
+ )[];
318
448
  profile: {
319
- syntaxVersion: "markdown-engine.validation@v1";
449
+ syntaxVersion: ValidationProfileSyntaxVersion;
320
450
  documentVersion: EngineDocumentVersion;
321
451
  ruleCount: number;
322
452
  };
@@ -324,9 +454,61 @@ interface DeclarativeValidationResult extends ValidationResult {
324
454
  }
325
455
  ```
326
456
 
327
- `valid` is `false` when any error-severity diagnostic exists. Warning and info
328
- validation diagnostics can make a rule result fail without making the aggregate
329
- result invalid.
457
+ For admitted v2 profiles, result metadata records
458
+ `syntaxVersion: "markdown-engine.validation@v2"`, `evaluatedRuleCount`, and
459
+ `skippedRuleCount`. V2 rule results include the v1-compatible `ruleId`,
460
+ `passed`, and `diagnostics` fields plus `status`, optional skipped
461
+ applicability metadata, and flat, grouped, or skipped evaluation metadata:
462
+
463
+ ```ts
464
+ interface DeclarativeValidationRuleResultV2 extends ValidationRuleResult {
465
+ status: "passed" | "failed" | "skipped";
466
+ when?: DeclarativeValidationApplicabilityResult;
467
+ evaluation:
468
+ | { kind: "assertions"; diagnostics: readonly MarkdownDiagnostic[] }
469
+ | {
470
+ kind: "anyOf";
471
+ selectedBranch?: DeclarativeValidationBranchReference;
472
+ branches: readonly DeclarativeValidationBranchResult[];
473
+ }
474
+ | {
475
+ kind: "allOf";
476
+ branches: readonly DeclarativeValidationBranchResult[];
477
+ }
478
+ | { kind: "skipped"; reason: "whenNotMatched" };
479
+ }
480
+
481
+ interface DeclarativeValidationApplicabilityResult {
482
+ status: "matched" | "notMatched";
483
+ diagnostics: readonly MarkdownDiagnostic[];
484
+ }
485
+
486
+ interface DeclarativeValidationBranchReference {
487
+ branchIndex: number;
488
+ label?: string;
489
+ }
490
+
491
+ interface DeclarativeValidationBranchResult
492
+ extends DeclarativeValidationBranchReference {
493
+ status: "passed" | "failed";
494
+ diagnostics: readonly MarkdownDiagnostic[];
495
+ }
496
+ ```
497
+
498
+ For configured `when`, matched applicability continues into normal flat or
499
+ grouped evaluation and contributes one evaluated rule. The evaluated rule result
500
+ does not serialize a `when` field. Non-matching applicability returns one
501
+ skipped rule result with `status: "skipped"`, `passed: true`, empty top-level
502
+ rule `diagnostics`, `when.status: "notMatched"`, nested applicability
503
+ diagnostics, `evaluation.kind: "skipped"`, and `reason: "whenNotMatched"`.
504
+ Skipped rules increment `skippedRuleCount`, do not increment
505
+ `evaluatedRuleCount`, and do not evaluate flat assertions or grouped branches.
506
+
507
+ `valid` is `false` when any top-level error-severity diagnostic exists in
508
+ `diagnostics`. Nested skipped applicability diagnostics under
509
+ `ruleResults[].when.diagnostics` do not by themselves make the aggregate result
510
+ invalid. Warning and info validation diagnostics can make a rule result fail
511
+ without making the aggregate result invalid.
330
512
 
331
513
  Rule results are sorted deterministically. Each rule result includes the public
332
514
  `ruleId`, `passed`, and cloned diagnostics. Results do not expose compiled rule
@@ -338,16 +520,27 @@ Evidence is emitted only when `DeclarativeValidationOptions.includeEvidence` is
338
520
  `true`:
339
521
 
340
522
  ```ts
341
- interface DeclarativeValidationEvidence {
523
+ interface DeclarativeValidationEvidence<
524
+ RuleResult extends ValidationRuleResult = ValidationRuleResult,
525
+ > {
342
526
  inputHash: string;
343
527
  profileHash: string;
344
528
  engineVersion: string;
345
529
  runtimeVersion: string;
346
- ruleResults: readonly ValidationRuleResult[];
530
+ ruleResults: readonly RuleResult[];
347
531
  diagnostics: readonly MarkdownDiagnostic[];
348
532
  }
349
533
  ```
350
534
 
535
+ For v1 profiles, `ruleResults` contains the unchanged v1 rule-result shape. For
536
+ admitted v2 profiles, `ruleResults` clones the public v2 rule-result shape,
537
+ including `status`, flat assertion evaluation, and grouped `anyOf` / `allOf`
538
+ branch evaluation. Skipped v2 rule results are cloned through evidence in the
539
+ same `ruleResults` array, and `evidence.diagnostics` clones the top-level
540
+ diagnostics array. Evidence does not serialize compiled rule plans, selector
541
+ target records, assertion-specific ID count evidence, or assertion-specific
542
+ table-column coverage evidence.
543
+
351
544
  `inputHash` is a lowercase hexadecimal SHA-256 digest of the stable JSON
352
545
  serialization of the supplied normalized `EngineDocument` after omitting only
353
546
  the top-level `document.path` field. Structural target paths remain part of the
@@ -360,7 +553,7 @@ serialization of the resolved `ValidationProfile` after applying the resolved
360
553
  the same profile hash for the same document version and rules.
361
554
 
362
555
  `engineVersion` records the package version that produced the evidence. In the
363
- 2.0 release line this is `"2.0.0"` even though `documentVersion` remains
556
+ 3.0 release line this is `"3.0.0"` even though `documentVersion` remains
364
557
  `"1.0.0"`.
365
558
 
366
559
  Raw Markdown bytes, raw YAML bytes, YAML comments, caller file paths, and
@@ -384,7 +577,9 @@ validate the Markdown file.
384
577
 
385
578
  After profile compilation succeeds, the CLI emits a validation-result JSON
386
579
  shape whether the document passes or fails. Validation CLI results include
387
- evidence.
580
+ evidence. V2 CLI results use the same validation-result arm of the CLI JSON
581
+ union; there is no extra CLI discriminator beyond
582
+ `profile.syntaxVersion: "markdown-engine.validation@v2"`.
388
583
 
389
584
  ## CLI JSON Union
390
585
 
@@ -411,23 +606,72 @@ unsupported assertion members, and incompatible selector/assertion pairs. It
411
606
  contains no `profile` and no `evidence`.
412
607
 
413
608
  Validation-result JSON is used after profile compilation succeeds. It contains
414
- `profile`, `ruleResults`, `diagnostics`, `valid`, and `evidence`.
609
+ `profile`, `ruleResults`, `diagnostics`, `valid`, and `evidence`. For v2
610
+ profiles, that same validation-result JSON can include `evaluatedRuleCount`,
611
+ `skippedRuleCount`, `status: "skipped"`, nested `when` diagnostics, and
612
+ `evaluation.kind: "skipped"`.
415
613
 
416
614
  ## Exit Codes
417
615
 
418
616
  | Exit code | Meaning |
419
617
  | --- | --- |
420
- | `0` | Validation completed with no error-severity diagnostics. |
421
- | `1` | Profile/config/compile, Markdown normalization, document-version mismatch, or validation diagnostics include at least one error. |
618
+ | `0` | Validation completed with no top-level error-severity diagnostics. |
619
+ | `1` | Profile/config/compile, Markdown normalization, document-version mismatch, or top-level validation diagnostics include at least one error. |
422
620
  | `2` | CLI usage, unsupported format, unknown argument, missing argument value, repeated singleton flag, unsupported `--document-version`, or local file read error. |
423
621
 
424
622
  ## Compatibility And Migration
425
623
 
426
624
  The v1 declarative validation syntax is a durable authoring contract for the
427
- 2.0 package release line. Changes to profile syntax names, selector names,
625
+ 3.0 package release line. Changes to profile syntax names, selector names,
428
626
  assertion names, result fields, diagnostic codes, CLI flags, CLI JSON shape, or
429
627
  evidence hash inputs require explicit compatibility review.
430
628
 
629
+ V1 preservation is explicit: v1 authoring syntax, v1 rule result shape, v1
630
+ diagnostic inventory, v1 CLI JSON behavior, and v1 evidence hash inputs remain
631
+ unchanged by the admitted v2 syntax.
632
+
633
+ Compatibility examples:
634
+
635
+ ```yaml
636
+ # v1 compatibility profile: remains on the v1 authoring and result contract.
637
+ syntaxVersion: markdown-engine.validation@v1
638
+ rules:
639
+ - id: sections.present
640
+ select:
641
+ target: document
642
+ assert:
643
+ sectionsRequired:
644
+ headings:
645
+ - Mission Brief
646
+ ```
647
+
648
+ ```yaml
649
+ # v2 opt-in profile: selects Conditional V2 behavior explicitly.
650
+ syntaxVersion: markdown-engine.validation@v2
651
+ rules:
652
+ - id: release.docs
653
+ anyOf:
654
+ - label: release-section
655
+ select:
656
+ target: section
657
+ title: Release
658
+ assert:
659
+ exists: true
660
+ - label: changelog-link
661
+ select:
662
+ target: link
663
+ text: changelog
664
+ assert:
665
+ exists: true
666
+ ```
667
+
668
+ The v1 profile above continues to emit the v1 validation-result shape. The v2
669
+ profile above emits syntax-versioned v2 result metadata with
670
+ `evaluatedRuleCount` and `skippedRuleCount`; v2 rule results include `status`
671
+ and `evaluation`. The CLI does not add a second discriminator for v2; consumers
672
+ branch on
673
+ `profile.syntaxVersion: "markdown-engine.validation@v2"`.
674
+
431
675
  Migration notes:
432
676
 
433
677
  - Consumers using fixed `validate(document, config)` rule families can continue
@@ -438,6 +682,11 @@ Migration notes:
438
682
  - Consumers parsing CLI validation output must handle the
439
683
  `DeclarativeValidationCliJsonResult` union. Profile-stage failures do not
440
684
  include `profile` or `evidence`.
685
+ - Consumers opting into `markdown-engine.validation@v2` must handle rule
686
+ `status` values of `"passed"`, `"failed"`, and `"skipped"`, plus
687
+ `evaluatedRuleCount`, `skippedRuleCount`, grouped branch results, and nested
688
+ skipped-rule `when` diagnostics. Aggregate validity is still determined from
689
+ top-level diagnostics.
441
690
  - Consumers that compare evidence hashes must normalize expectations around
442
691
  resolved `documentVersion`, default rule severity, stable key order, and
443
692
  exclusion of only top-level `document.path` from `inputHash`.
@@ -501,6 +750,34 @@ rules:
501
750
  count: 1
502
751
  ```
503
752
 
753
+ Conditional v2 grouped rule with applicability:
754
+
755
+ ```yaml
756
+ syntaxVersion: markdown-engine.validation@v2
757
+ rules:
758
+ - id: release.when.docs-ready
759
+ when:
760
+ select:
761
+ target: section
762
+ title: Release
763
+ assert:
764
+ exists: true
765
+ anyOf:
766
+ - label: contract-link
767
+ select:
768
+ target: link
769
+ section: Release
770
+ text: contract
771
+ assert:
772
+ exists: true
773
+ - label: contract-heading
774
+ select:
775
+ target: heading
776
+ text: Contract
777
+ assert:
778
+ exists: true
779
+ ```
780
+
504
781
  CLI invocation:
505
782
 
506
783
  ```sh
@@ -540,6 +817,11 @@ The v1 contract explicitly excludes:
540
817
  - operational-design-spec, AGENTS.md, TASK.md, or other domain-specific rule
541
818
  meaning in core engine code
542
819
 
820
+ The admitted v2 Conditional V2 surface also excludes `documentVersion: "3.0.0"`,
821
+ recursive grouped rules, branch-level `when`, profile-defined predicates,
822
+ assertion-specific evidence payloads, a separate skipped-rule evidence channel,
823
+ and a new CLI JSON discriminator.
824
+
543
825
  The CLI reads only the caller-specified local Markdown and profile files. The
544
826
  API owns no file traversal, daemon, database, browser runtime, network service,
545
827
  agent adapter, MCP transport, runtime lens, or persistent cache.
@@ -553,7 +835,8 @@ npm run docs:declarative-validation-contract
553
835
  npm run audit:declarative-validation-boundary
554
836
  ```
555
837
 
556
- The documentation gate checks this contract, README links, evidence files, and
557
- package script wiring. The boundary audit checks dependency drift, source-level
558
- runtime boundary patterns, unsupported regex-like and executable profile-key
559
- coverage, and declarative validation boundary evidence.
838
+ The documentation gate checks this contract, README links, legacy contract and
839
+ boundary evidence files, Conditional V2 EVD-6 reviewer notes, and package script
840
+ wiring. The boundary audit checks dependency drift, source-level runtime
841
+ boundary patterns, unsupported regex-like and executable profile-key coverage,
842
+ and declarative validation boundary evidence.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasonbelmonti/markdown-engine",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Deterministic Markdown parsing and validation engine package.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,6 +31,8 @@
31
31
  "types": "./dist/index.d.ts",
32
32
  "files": [
33
33
  "dist",
34
+ "dist-bundled",
35
+ "skills",
34
36
  "docs/contracts",
35
37
  "fixtures/declarative-validation/examples",
36
38
  "CHANGELOG.md",
@@ -42,9 +44,11 @@
42
44
  "scripts": {
43
45
  "clean": "node scripts/clean-dist.mjs",
44
46
  "build": "npm run clean && tsc -p tsconfig.json",
47
+ "build:cli:bundled": "node scripts/build-bundled-cli.mjs",
48
+ "build:cli-bundle": "node scripts/build-cli-bundle.mjs",
45
49
  "prepack": "npm run release:verify",
46
50
  "prepublishOnly": "npm run release:verify",
47
- "release:verify": "npm run typecheck && npm test && node scripts/check-boundaries.mjs && npm run docs:rich-ir-contract && npm run docs:declarative-validation-contract && npm run audit:declarative-validation-boundary && npm run build && node scripts/prove-repeatability.mjs --runs 10 && npm run release:check-clean",
51
+ "release:verify": "npm run typecheck && npm test && node scripts/check-boundaries.mjs && npm run docs:rich-ir-contract && npm run docs:declarative-validation-contract && npm run audit:declarative-validation-boundary && npm run build && npm run build:cli:bundled && node scripts/prove-repeatability.mjs --runs 10 && npm run release:check-clean",
48
52
  "release:check-clean": "git diff --check HEAD -- && git diff --exit-code HEAD --",
49
53
  "snapshots:update:parser": "npm run build && vitest run tests/parser-fixtures.test.ts -u \"--exclude=.worktrees/**\"",
50
54
  "snapshots:update:rules": "npm run build && vitest run tests/rules.test.ts -u \"--exclude=.worktrees/**\"",
@@ -65,7 +69,7 @@
65
69
  "test:validation:profile": "npm run build && vitest run tests/declarative-validation-profile.test.ts \"--exclude=.worktrees/**\"",
66
70
  "test:validation:compiler": "npm run build && vitest run tests/declarative-validation-compiler.test.ts \"--exclude=.worktrees/**\"",
67
71
  "test:validation:selectors": "npm run build && vitest run tests/declarative-validation-selectors.test.ts \"--exclude=.worktrees/**\"",
68
- "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts \"--exclude=.worktrees/**\"",
72
+ "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-table-column-coverage-fixtures.test.ts tests/declarative-validation-grouped-rules-fixtures.test.ts tests/declarative-validation-when-skipped-rules-fixtures.test.ts \"--exclude=.worktrees/**\"",
69
73
  "test:validation:diagnostics": "npm run build && vitest run tests/declarative-validation-diagnostics.test.ts \"--exclude=.worktrees/**\"",
70
74
  "test:validation:cli": "npm run build && vitest run tests/declarative-validation-cli.test.ts \"--exclude=.worktrees/**\"",
71
75
  "test:validation:examples": "npm run build && vitest run tests/declarative-validation-examples.test.ts \"--exclude=.worktrees/**\"",
@@ -83,6 +87,7 @@
83
87
  "devDependencies": {
84
88
  "@types/node": "^22.19.17",
85
89
  "cmark-gfm": "^0.9.0",
90
+ "esbuild": "^0.27.7",
86
91
  "typescript": "^5.8.3",
87
92
  "vitest": "^3.1.3"
88
93
  },