@jasonbelmonti/markdown-engine 2.0.0 → 3.1.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.
- package/CHANGELOG.md +45 -1
- package/README.md +132 -57
- package/dist/api/contracts.d.ts +2 -1
- package/dist/api/contracts.d.ts.map +1 -1
- package/dist/api/contracts.js +1 -0
- package/dist/api/contracts.js.map +1 -1
- package/dist/api/declarative-validation.d.ts +2 -2
- package/dist/api/declarative-validation.d.ts.map +1 -1
- package/dist/api/declarative-validation.js +53 -31
- package/dist/api/declarative-validation.js.map +1 -1
- package/dist/api/document-set-validation-types.d.ts +32 -0
- package/dist/api/document-set-validation-types.d.ts.map +1 -0
- package/dist/api/document-set-validation-types.js +2 -0
- package/dist/api/document-set-validation-types.js.map +1 -0
- package/dist/api/document-set-validation.d.ts +4 -0
- package/dist/api/document-set-validation.d.ts.map +1 -0
- package/dist/api/document-set-validation.js +60 -0
- package/dist/api/document-set-validation.js.map +1 -0
- package/dist/api/serialize.d.ts +2 -1
- package/dist/api/serialize.d.ts.map +1 -1
- package/dist/api/serialize.js.map +1 -1
- package/dist/cli/declarative-validation.d.ts.map +1 -1
- package/dist/cli/declarative-validation.js +7 -12
- package/dist/cli/declarative-validation.js.map +1 -1
- package/dist/declarative-validation/applicability/classifier.d.ts +18 -0
- package/dist/declarative-validation/applicability/classifier.d.ts.map +1 -0
- package/dist/declarative-validation/applicability/classifier.js +37 -0
- package/dist/declarative-validation/applicability/classifier.js.map +1 -0
- package/dist/declarative-validation/applicability/index.d.ts +2 -0
- package/dist/declarative-validation/applicability/index.d.ts.map +1 -0
- package/dist/declarative-validation/applicability/index.js +2 -0
- package/dist/declarative-validation/applicability/index.js.map +1 -0
- package/dist/declarative-validation/assertions/context.d.ts +2 -2
- package/dist/declarative-validation/assertions/context.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/diagnostics.d.ts +7 -4
- package/dist/declarative-validation/assertions/diagnostics.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/diagnostics.js +26 -1
- package/dist/declarative-validation/assertions/diagnostics.js.map +1 -1
- package/dist/declarative-validation/assertions/evaluator.d.ts +2 -2
- package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/evaluator.js +9 -0
- package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
- package/dist/declarative-validation/assertions/frontmatter-shape.d.ts +9 -0
- package/dist/declarative-validation/assertions/frontmatter-shape.d.ts.map +1 -0
- package/dist/declarative-validation/assertions/frontmatter-shape.js +105 -0
- package/dist/declarative-validation/assertions/frontmatter-shape.js.map +1 -0
- package/dist/declarative-validation/assertions/group-evaluator.d.ts +6 -0
- package/dist/declarative-validation/assertions/group-evaluator.d.ts.map +1 -0
- package/dist/declarative-validation/assertions/group-evaluator.js +57 -0
- package/dist/declarative-validation/assertions/group-evaluator.js.map +1 -0
- package/dist/declarative-validation/assertions/id-targets.d.ts +15 -0
- package/dist/declarative-validation/assertions/id-targets.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/id-targets.js +18 -55
- package/dist/declarative-validation/assertions/id-targets.js.map +1 -1
- package/dist/declarative-validation/assertions/ids.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/ids.js +100 -14
- package/dist/declarative-validation/assertions/ids.js.map +1 -1
- package/dist/declarative-validation/assertions/index.d.ts +1 -0
- package/dist/declarative-validation/assertions/index.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/index.js +1 -0
- package/dist/declarative-validation/assertions/index.js.map +1 -1
- package/dist/declarative-validation/assertions/table-column-coverage.d.ts +9 -0
- package/dist/declarative-validation/assertions/table-column-coverage.d.ts.map +1 -0
- package/dist/declarative-validation/assertions/table-column-coverage.js +108 -0
- package/dist/declarative-validation/assertions/table-column-coverage.js.map +1 -0
- package/dist/declarative-validation/assertions/text-format.d.ts +9 -0
- package/dist/declarative-validation/assertions/text-format.d.ts.map +1 -0
- package/dist/declarative-validation/assertions/text-format.js +75 -0
- package/dist/declarative-validation/assertions/text-format.js.map +1 -0
- package/dist/declarative-validation/compiler/applicability-plan.d.ts +5 -0
- package/dist/declarative-validation/compiler/applicability-plan.d.ts.map +1 -0
- package/dist/declarative-validation/compiler/applicability-plan.js +22 -0
- package/dist/declarative-validation/compiler/applicability-plan.js.map +1 -0
- package/dist/declarative-validation/compiler/assertion-builders.d.ts +2 -1
- package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/assertion-builders.js +88 -13
- package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
- package/dist/declarative-validation/compiler/assertion-shapes.d.ts +5 -1
- package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/assertion-shapes.js +120 -0
- package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -1
- package/dist/declarative-validation/compiler/assertions.d.ts +2 -1
- package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/assertions.js +16 -4
- package/dist/declarative-validation/compiler/assertions.js.map +1 -1
- package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/compatibility.js +3 -0
- package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
- package/dist/declarative-validation/compiler/group-plans.d.ts +6 -0
- package/dist/declarative-validation/compiler/group-plans.d.ts.map +1 -0
- package/dist/declarative-validation/compiler/group-plans.js +67 -0
- package/dist/declarative-validation/compiler/group-plans.js.map +1 -0
- package/dist/declarative-validation/compiler/index.d.ts +1 -1
- package/dist/declarative-validation/compiler/index.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/index.js +70 -35
- package/dist/declarative-validation/compiler/index.js.map +1 -1
- package/dist/declarative-validation/compiler/plan.d.ts +80 -5
- package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/rule-fields.d.ts +6 -0
- package/dist/declarative-validation/compiler/rule-fields.d.ts.map +1 -0
- package/dist/declarative-validation/compiler/rule-fields.js +40 -0
- package/dist/declarative-validation/compiler/rule-fields.js.map +1 -0
- package/dist/declarative-validation/diagnostics/profile-config-diagnostics.d.ts +0 -1
- package/dist/declarative-validation/diagnostics/profile-config-diagnostics.d.ts.map +1 -1
- package/dist/declarative-validation/diagnostics/profile-config-diagnostics.js +8 -2
- package/dist/declarative-validation/diagnostics/profile-config-diagnostics.js.map +1 -1
- package/dist/declarative-validation/evidence/index.d.ts +3 -3
- package/dist/declarative-validation/evidence/index.d.ts.map +1 -1
- package/dist/declarative-validation/evidence/index.js +6 -6
- package/dist/declarative-validation/evidence/index.js.map +1 -1
- package/dist/declarative-validation/profile/applicability-schema.d.ts +4 -0
- package/dist/declarative-validation/profile/applicability-schema.d.ts.map +1 -0
- package/dist/declarative-validation/profile/applicability-schema.js +24 -0
- package/dist/declarative-validation/profile/applicability-schema.js.map +1 -0
- package/dist/declarative-validation/profile/assertion-schema.d.ts +2 -1
- package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
- package/dist/declarative-validation/profile/assertion-schema.js +194 -14
- package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
- package/dist/declarative-validation/profile/direct-profile-diagnostics.d.ts +3 -1
- package/dist/declarative-validation/profile/direct-profile-diagnostics.d.ts.map +1 -1
- package/dist/declarative-validation/profile/direct-profile-diagnostics.js +10 -2
- package/dist/declarative-validation/profile/direct-profile-diagnostics.js.map +1 -1
- package/dist/declarative-validation/profile/frontmatter-shape-schema.d.ts +8 -0
- package/dist/declarative-validation/profile/frontmatter-shape-schema.d.ts.map +1 -0
- package/dist/declarative-validation/profile/frontmatter-shape-schema.js +178 -0
- package/dist/declarative-validation/profile/frontmatter-shape-schema.js.map +1 -0
- package/dist/declarative-validation/profile/group-schema.d.ts +5 -0
- package/dist/declarative-validation/profile/group-schema.d.ts.map +1 -0
- package/dist/declarative-validation/profile/group-schema.js +55 -0
- package/dist/declarative-validation/profile/group-schema.js.map +1 -0
- package/dist/declarative-validation/profile/ids-assertion-contract.d.ts +12 -0
- package/dist/declarative-validation/profile/ids-assertion-contract.d.ts.map +1 -0
- package/dist/declarative-validation/profile/ids-assertion-contract.js +32 -0
- package/dist/declarative-validation/profile/ids-assertion-contract.js.map +1 -0
- package/dist/declarative-validation/profile/index.d.ts +63 -2
- package/dist/declarative-validation/profile/index.d.ts.map +1 -1
- package/dist/declarative-validation/profile/index.js.map +1 -1
- package/dist/declarative-validation/profile/materialization.d.ts.map +1 -1
- package/dist/declarative-validation/profile/materialization.js +13 -10
- package/dist/declarative-validation/profile/materialization.js.map +1 -1
- package/dist/declarative-validation/profile/schema.d.ts.map +1 -1
- package/dist/declarative-validation/profile/schema.js +73 -12
- package/dist/declarative-validation/profile/schema.js.map +1 -1
- package/dist/declarative-validation/profile/syntax-version.d.ts +7 -0
- package/dist/declarative-validation/profile/syntax-version.d.ts.map +1 -0
- package/dist/declarative-validation/profile/syntax-version.js +11 -0
- package/dist/declarative-validation/profile/syntax-version.js.map +1 -0
- package/dist/declarative-validation/profile/text-format-contract.d.ts +4 -0
- package/dist/declarative-validation/profile/text-format-contract.d.ts.map +1 -0
- package/dist/declarative-validation/profile/text-format-contract.js +6 -0
- package/dist/declarative-validation/profile/text-format-contract.js.map +1 -0
- package/dist/declarative-validation/results/clone-rule-result.d.ts +6 -0
- package/dist/declarative-validation/results/clone-rule-result.d.ts.map +1 -0
- package/dist/declarative-validation/results/clone-rule-result.js +125 -0
- package/dist/declarative-validation/results/clone-rule-result.js.map +1 -0
- package/dist/declarative-validation/results/create-result.d.ts +15 -0
- package/dist/declarative-validation/results/create-result.d.ts.map +1 -0
- package/dist/declarative-validation/results/create-result.js +58 -0
- package/dist/declarative-validation/results/create-result.js.map +1 -0
- package/dist/declarative-validation/results/index.d.ts +3 -25
- package/dist/declarative-validation/results/index.d.ts.map +1 -1
- package/dist/declarative-validation/results/index.js +2 -1
- package/dist/declarative-validation/results/index.js.map +1 -1
- package/dist/declarative-validation/results/skipped-rule-result.d.ts +10 -0
- package/dist/declarative-validation/results/skipped-rule-result.d.ts.map +1 -0
- package/dist/declarative-validation/results/skipped-rule-result.js +18 -0
- package/dist/declarative-validation/results/skipped-rule-result.js.map +1 -0
- package/dist/declarative-validation/results/types.d.ts +77 -0
- package/dist/declarative-validation/results/types.d.ts.map +1 -0
- package/dist/declarative-validation/results/types.js +2 -0
- package/dist/declarative-validation/results/types.js.map +1 -0
- package/dist/declarative-validation/selectors/table-targets.d.ts +23 -0
- package/dist/declarative-validation/selectors/table-targets.d.ts.map +1 -1
- package/dist/declarative-validation/selectors/table-targets.js +52 -9
- package/dist/declarative-validation/selectors/table-targets.js.map +1 -1
- package/dist-bundled/markdown-engine-cli.mjs +27373 -0
- package/docs/contracts/api.md +79 -15
- package/docs/contracts/declarative-validation.md +462 -40
- package/fixtures/declarative-validation/examples/okf-v0.1/fail/invalid-log-date/log.md +5 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/fail/missing-concept-type/concepts/customer-metric.md +8 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/fail/non-root-index-frontmatter/datasets/index.md +7 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/pass/datasets/index.md +3 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/pass/datasets/sales.md +16 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/pass/index.md +8 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/pass/log.md +9 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/pass/playbooks/incident-response.md +17 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/profiles/concept.yaml +14 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/profiles/log.yaml +10 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/profiles/non-root-index.yaml +9 -0
- package/fixtures/declarative-validation/examples/okf-v0.1/profiles/root-index.yaml +12 -0
- package/package.json +9 -3
- package/scripts/install-markdown-engine-cli.sh +113 -0
- package/skills/profile-backed-markdown/SKILL.md +54 -0
- package/skills/profile-backed-markdown/assets/profiles/operational-spec.yaml +98 -0
- package/skills/profile-backed-markdown/references/repair-brief.md +14 -0
- package/skills/profile-backed-markdown/scripts/validate-profile-backed-markdown.mjs +590 -0
|
@@ -1,19 +1,41 @@
|
|
|
1
1
|
# Declarative Validation Contract
|
|
2
2
|
|
|
3
|
-
Status: package
|
|
4
|
-
Last updated: 2026-
|
|
3
|
+
Status: package 3.0.0, v1 profile syntax with v2 Conditional V2, document contract 1.0.0
|
|
4
|
+
Last updated: 2026-06-15
|
|
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, `frontmatterShape` schema, compiled-plan, and
|
|
8
|
+
runtime evaluator contract, `textFormat` schema, compiled-plan, and runtime
|
|
9
|
+
evaluator contract, grouped rule runtime contract, and rule-level `when`
|
|
10
|
+
schema, matcher, public skipped-rule result, skipped counts, and evidence
|
|
11
|
+
cloning contract.
|
|
5
12
|
|
|
6
13
|
This document defines the public declarative validation contract for
|
|
7
14
|
`@jasonbelmonti/markdown-engine`. The stable surface is the package-root API,
|
|
8
|
-
the v1 profile syntax, the
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
public contracts.
|
|
12
|
-
|
|
13
|
-
Package
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
document contract
|
|
15
|
+
the v1 profile syntax, the admitted v2 profile syntax and runtime subset, the
|
|
16
|
+
CLI validation command, diagnostic codes, serialized result shapes, and evidence
|
|
17
|
+
fields. Internal parser output, compiled rule-plan records, selector target
|
|
18
|
+
records, and evaluator implementation modules are not public contracts.
|
|
19
|
+
|
|
20
|
+
Package 3.0 does not introduce `documentVersion: "3.0.0"` or CLI JSON
|
|
21
|
+
discrimination.
|
|
22
|
+
Declarative validation continues to use the existing `documentVersion: "1.0.0"`
|
|
23
|
+
rich IR document contract, while the profile admission path recognizes
|
|
24
|
+
`markdown-engine.validation@v2` for the same flat rule shape with `id`, optional
|
|
25
|
+
`severity`, `select`, and `assert`; non-recursive `anyOf` and `allOf`; and
|
|
26
|
+
optional rule-level `when`. The admitted v2 path exposes the result and evidence
|
|
27
|
+
shell needed to distinguish assertion, grouped, and skipped evaluation output
|
|
28
|
+
from v1 output, plus the ID count-bound schema, compiled-plan, and runtime
|
|
29
|
+
evaluator contract; the `tableColumnCoverage` schema, compiled-plan, and
|
|
30
|
+
runtime evaluator contract; the `frontmatterShape` schema, private
|
|
31
|
+
compiled-plan, and runtime evaluator contract; the `textFormat` schema,
|
|
32
|
+
private compiled-plan, and runtime evaluator contract; and the `when` schema
|
|
33
|
+
plus private compiled-plan and matcher contract. Matched applicability
|
|
34
|
+
continues into normal rule evaluation.
|
|
35
|
+
Non-matching applicability returns a public skipped rule result with
|
|
36
|
+
`status: "skipped"`, `passed: true`, `evaluation.kind: "skipped"`,
|
|
37
|
+
`reason: "whenNotMatched"`, `skippedRuleCount`, no top-level diagnostics, and a
|
|
38
|
+
nested `when` applicability result.
|
|
17
39
|
|
|
18
40
|
## 1.0 Contract
|
|
19
41
|
|
|
@@ -36,6 +58,11 @@ validateWithProfile(
|
|
|
36
58
|
profile: ValidationProfile,
|
|
37
59
|
options?: DeclarativeValidationOptions,
|
|
38
60
|
): DeclarativeValidationResult
|
|
61
|
+
|
|
62
|
+
validateDocumentSet(
|
|
63
|
+
entries: readonly ValidateDocumentSetEntry[],
|
|
64
|
+
options?: ValidateDocumentSetOptions,
|
|
65
|
+
): ValidateDocumentSetResult
|
|
39
66
|
```
|
|
40
67
|
|
|
41
68
|
Compiled declarative validation plans are internal. They are not exported from
|
|
@@ -53,8 +80,28 @@ syntaxVersion: markdown-engine.validation@v1
|
|
|
53
80
|
`syntaxVersion` is required. Missing or unsupported values emit
|
|
54
81
|
`profile.config.unsupportedSyntaxVersion`.
|
|
55
82
|
|
|
56
|
-
The
|
|
57
|
-
|
|
83
|
+
The v2 syntax is admitted as an additive profile syntax:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This release recognizes v2 as a distinct syntax version at profile admission,
|
|
90
|
+
admits ID count bounds at the schema, compiled-plan, and runtime evaluator
|
|
91
|
+
layers, admits `tableColumnCoverage` at the schema, internal compiled-plan, and
|
|
92
|
+
runtime evaluator layers, admits `frontmatterShape` at the schema, internal
|
|
93
|
+
compiled-plan, and runtime evaluator layers, admits `textFormat` at the schema,
|
|
94
|
+
internal compiled-plan, and runtime evaluator layers, admits non-recursive
|
|
95
|
+
grouped rules at the schema, compiled-plan, and runtime evaluator layers, and
|
|
96
|
+
admits optional rule-level `when` at the schema, internal compiled-plan, and
|
|
97
|
+
matcher layers.
|
|
98
|
+
Matching `when` rules continue through normal flat or grouped evaluation and do
|
|
99
|
+
not add a public `when` field to the evaluated rule result. Non-matching `when`
|
|
100
|
+
rules are not evaluated; they return the public skipped-rule result shape,
|
|
101
|
+
increment `skippedRuleCount`, and leave `evaluatedRuleCount` unchanged.
|
|
102
|
+
|
|
103
|
+
The admitted v1/v2 flat vocabulary is closed. Unknown profile keys, rule keys,
|
|
104
|
+
selector keys, known assertion keys, and nested assertion keys emit
|
|
58
105
|
`profile.config.unsupportedKey` unless the contract assigns a more specific
|
|
59
106
|
compile diagnostic for an unsupported selector target or unsupported assertion
|
|
60
107
|
member.
|
|
@@ -113,15 +160,52 @@ results, and does not evaluate rules.
|
|
|
113
160
|
The top-level profile shape is:
|
|
114
161
|
|
|
115
162
|
```ts
|
|
163
|
+
type ValidationProfileSyntaxVersion =
|
|
164
|
+
| "markdown-engine.validation@v1"
|
|
165
|
+
| "markdown-engine.validation@v2";
|
|
166
|
+
|
|
116
167
|
interface ValidationProfile {
|
|
117
|
-
syntaxVersion:
|
|
168
|
+
syntaxVersion: ValidationProfileSyntaxVersion;
|
|
118
169
|
documentVersion?: EngineDocumentVersion;
|
|
119
170
|
rules: readonly DeclarativeValidationRule[];
|
|
120
171
|
}
|
|
121
172
|
|
|
122
|
-
|
|
173
|
+
type DeclarativeValidationRule =
|
|
174
|
+
| DeclarativeValidationFlatRule
|
|
175
|
+
| DeclarativeValidationGroupRule;
|
|
176
|
+
|
|
177
|
+
interface DeclarativeValidationRuleFields {
|
|
123
178
|
id: string;
|
|
124
179
|
severity?: "error" | "warning" | "info";
|
|
180
|
+
when?: DeclarativeValidationApplicability;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface DeclarativeValidationFlatRule extends DeclarativeValidationRuleFields {
|
|
184
|
+
select: DeclarativeSelector;
|
|
185
|
+
assert: DeclarativeAssertion;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
interface DeclarativeValidationApplicability {
|
|
189
|
+
select: DeclarativeSelector;
|
|
190
|
+
assert: DeclarativeAssertion;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
type DeclarativeValidationGroupRule =
|
|
194
|
+
| DeclarativeValidationAnyOfRule
|
|
195
|
+
| DeclarativeValidationAllOfRule;
|
|
196
|
+
|
|
197
|
+
interface DeclarativeValidationAnyOfRule
|
|
198
|
+
extends DeclarativeValidationRuleFields {
|
|
199
|
+
anyOf: readonly DeclarativeValidationBranch[];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
interface DeclarativeValidationAllOfRule
|
|
203
|
+
extends DeclarativeValidationRuleFields {
|
|
204
|
+
allOf: readonly DeclarativeValidationBranch[];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
interface DeclarativeValidationBranch {
|
|
208
|
+
label?: string;
|
|
125
209
|
select: DeclarativeSelector;
|
|
126
210
|
assert: DeclarativeAssertion;
|
|
127
211
|
}
|
|
@@ -134,6 +218,11 @@ evidence identify output by `ruleId`.
|
|
|
134
218
|
Rule `severity` defaults to `error` when omitted. Unsupported severity values
|
|
135
219
|
emit `profile.config.invalidShape`.
|
|
136
220
|
|
|
221
|
+
Rule-level `when` is allowed only on v2 rules. Branch-level `when` remains
|
|
222
|
+
unsupported. V1 profiles preserve the original flat rule authoring contract;
|
|
223
|
+
grouped `anyOf` / `allOf`, ID count bounds, `tableColumnCoverage`,
|
|
224
|
+
`frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
|
|
225
|
+
|
|
137
226
|
Profile values must be JSON-safe data properties after YAML materialization.
|
|
138
227
|
Functions, accessors, proxies, cyclic structures, sparse arrays, `undefined`
|
|
139
228
|
payloads in required positions, non-finite numbers, and `__proto__` properties
|
|
@@ -203,11 +292,36 @@ interface DeclarativeAssertion {
|
|
|
203
292
|
prefix?: string;
|
|
204
293
|
unique?: boolean;
|
|
205
294
|
caseSensitive?: boolean;
|
|
295
|
+
minCount?: number;
|
|
296
|
+
maxCount?: number;
|
|
206
297
|
};
|
|
207
298
|
references?: {
|
|
208
299
|
idsFrom: { section?: string; column?: string; prefix?: string };
|
|
209
300
|
mustAppearIn: readonly string[];
|
|
210
301
|
};
|
|
302
|
+
tableColumnCoverage?: {
|
|
303
|
+
source: {
|
|
304
|
+
section: string;
|
|
305
|
+
column: string;
|
|
306
|
+
prefix?: string;
|
|
307
|
+
caseSensitive?: boolean;
|
|
308
|
+
};
|
|
309
|
+
target: {
|
|
310
|
+
section: string;
|
|
311
|
+
tableHeader?: readonly string[];
|
|
312
|
+
column: string;
|
|
313
|
+
};
|
|
314
|
+
require: "everySourceId";
|
|
315
|
+
};
|
|
316
|
+
frontmatterShape?: {
|
|
317
|
+
presence?: "required" | "forbidden";
|
|
318
|
+
fields?: readonly {
|
|
319
|
+
field: string;
|
|
320
|
+
required?: true;
|
|
321
|
+
valueType?: "string" | "number" | "boolean" | "array" | "object" | "null";
|
|
322
|
+
nonEmpty?: true;
|
|
323
|
+
}[];
|
|
324
|
+
};
|
|
211
325
|
text?: {
|
|
212
326
|
contains?: string;
|
|
213
327
|
excludes?: readonly string[];
|
|
@@ -220,6 +334,9 @@ interface DeclarativeAssertion {
|
|
|
220
334
|
min?: number;
|
|
221
335
|
max?: number;
|
|
222
336
|
};
|
|
337
|
+
textFormat?: {
|
|
338
|
+
format: "isoDate";
|
|
339
|
+
};
|
|
223
340
|
frontmatterRequired?: {
|
|
224
341
|
fields: readonly string[];
|
|
225
342
|
};
|
|
@@ -242,9 +359,12 @@ Selector/assertion compatibility is part of the public contract:
|
|
|
242
359
|
| `tableColumnsRequired` | `table` |
|
|
243
360
|
| `ids` | all supported selector targets |
|
|
244
361
|
| `references` | `document` |
|
|
362
|
+
| `tableColumnCoverage` | `document` |
|
|
363
|
+
| `frontmatterShape` | `document` |
|
|
245
364
|
| `text` | all supported selector targets |
|
|
246
365
|
| `textOccurrenceCount` | all supported selector targets |
|
|
247
366
|
| `textLength` | all supported selector targets |
|
|
367
|
+
| `textFormat` | all supported selector targets |
|
|
248
368
|
| `frontmatterRequired` | `document` |
|
|
249
369
|
|
|
250
370
|
Incompatible supported selector/assertion pairs emit
|
|
@@ -258,9 +378,61 @@ resolves zero targets.
|
|
|
258
378
|
headings appear as an ordered subsequence in the normalized section tree
|
|
259
379
|
flattened in source order.
|
|
260
380
|
|
|
261
|
-
`ids.unique` must be `true
|
|
262
|
-
|
|
381
|
+
For v1 profiles, `ids.unique` must be `true`. For v2 profiles, `ids.unique`
|
|
382
|
+
must be `true` when provided and may be omitted when `ids.minCount` or
|
|
383
|
+
`ids.maxCount` provides the predicate. `prefix` and `caseSensitive` are modifiers,
|
|
384
|
+
not standalone predicates. `caseSensitive` defaults to `true`. ID tokens use the
|
|
263
385
|
documented token grammar `[A-Za-z][A-Za-z0-9]*-[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*`.
|
|
386
|
+
For v2 profiles, `ids.minCount` and `ids.maxCount` are admitted as non-negative
|
|
387
|
+
integer schema and compiled-plan fields. When both are present, `minCount` must
|
|
388
|
+
be less than or equal to `maxCount`. Runtime count evaluation uses unique
|
|
389
|
+
comparison values after prefix filtering and duplicate occurrence de-duplication.
|
|
390
|
+
Failed lower and upper bounds emit `profile.validation.idCountTooLow` and
|
|
391
|
+
`profile.validation.idCountTooHigh`.
|
|
392
|
+
|
|
393
|
+
For v2 profiles, `tableColumnCoverage` is admitted as a flat-rule schema and
|
|
394
|
+
internal compiled-plan assertion. It is compatible only with a `document`
|
|
395
|
+
selector because the assertion owns its source and target table-column inputs.
|
|
396
|
+
`source.section`, `source.column`, `target.section`, and `target.column` are
|
|
397
|
+
required non-empty strings. `source.prefix` is optional and must be non-empty
|
|
398
|
+
when provided. `source.caseSensitive` is optional and defaults to `true` in the
|
|
399
|
+
compiled plan. `target.tableHeader` is an optional non-empty string array.
|
|
400
|
+
`require` must be exactly `"everySourceId"`. Runtime evaluation extracts unique
|
|
401
|
+
source IDs from `source.section` and `source.column`, applies `source.prefix`
|
|
402
|
+
and `source.caseSensitive`, and requires every source ID comparison value to
|
|
403
|
+
appear in the configured target table column. IDs appearing elsewhere in the
|
|
404
|
+
target section do not satisfy coverage. Missing target sections, missing target
|
|
405
|
+
columns, and missing target-column IDs emit deterministic validation diagnostics
|
|
406
|
+
source-grounded to the source ID when source evidence is available.
|
|
407
|
+
|
|
408
|
+
For v2 profiles, `frontmatterShape` is admitted as a flat-rule schema and
|
|
409
|
+
internal compiled-plan assertion. It is compatible only with a `document`
|
|
410
|
+
selector because frontmatter is document metadata. `presence` is optional and
|
|
411
|
+
must be exactly `"required"` or `"forbidden"` when provided. `fields` is an
|
|
412
|
+
optional non-empty array of field constraints. Each field constraint has a
|
|
413
|
+
required non-empty `field` name and must include at least one effective
|
|
414
|
+
constraint: `required: true`, `valueType`, or `nonEmpty: true`. Field names
|
|
415
|
+
within one `frontmatterShape.fields` array must be unique. `valueType` must be
|
|
416
|
+
one of `"string"`, `"number"`, `"boolean"`, `"array"`, `"object"`, or `"null"`.
|
|
417
|
+
`nonEmpty: true` is a string predicate; when it is combined with `valueType`,
|
|
418
|
+
`valueType` must be `"string"`. `presence: "forbidden"` cannot be combined with
|
|
419
|
+
`fields`.
|
|
420
|
+
|
|
421
|
+
Runtime evaluation treats frontmatter as present when
|
|
422
|
+
`document.frontmatter !== undefined`. `presence: "required"` fails absent
|
|
423
|
+
frontmatter with `profile.validation.frontmatterMissing`.
|
|
424
|
+
`presence: "forbidden"` fails any present frontmatter value, including empty
|
|
425
|
+
frontmatter, with `profile.validation.frontmatterForbidden`. Field constraints
|
|
426
|
+
evaluate only top-level frontmatter fields and do not coerce values. A required
|
|
427
|
+
field that is absent, or any required field on non-object frontmatter, emits
|
|
428
|
+
`profile.validation.frontmatterFieldMissing`. Optional fields are ignored when
|
|
429
|
+
absent. `valueType` checks distinguish `"string"`, finite `"number"`,
|
|
430
|
+
`"boolean"`, `"array"`, plain `"object"`, and `"null"` values and emit
|
|
431
|
+
`profile.validation.frontmatterFieldTypeMismatch` on mismatch. `nonEmpty: true`
|
|
432
|
+
requires a present field value to be a non-empty string and emits
|
|
433
|
+
`profile.validation.frontmatterFieldEmpty` when that predicate fails. When
|
|
434
|
+
`valueType: "string"` and `nonEmpty: true` are combined, a non-string value
|
|
435
|
+
emits the type-mismatch diagnostic without a duplicate empty-string diagnostic.
|
|
264
436
|
|
|
265
437
|
`text` must include `contains` or a non-empty `excludes` array.
|
|
266
438
|
`textOccurrenceCount.count` is a finite number and counts non-overlapping
|
|
@@ -270,10 +442,22 @@ integers, `min` must be less than or equal to `max` when both are present, and
|
|
|
270
442
|
evaluation uses JavaScript string `.length` for each selected target's
|
|
271
443
|
normalized text.
|
|
272
444
|
|
|
445
|
+
For v2 profiles, `textFormat` is admitted as a flat-rule schema and internal
|
|
446
|
+
compiled-plan assertion. `textFormat.format` must be exactly `"isoDate"`;
|
|
447
|
+
custom formats, locale options, regex-like formats, profile-supplied patterns,
|
|
448
|
+
and additional `textFormat` members are unsupported. Runtime evaluation checks
|
|
449
|
+
each selected target's normalized text as an exact real calendar date in
|
|
450
|
+
`YYYY-MM-DD` form. Valid dates such as `2026-06-14` and leap dates such as
|
|
451
|
+
`2024-02-29` pass. Non-dates, invalid calendar dates, timestamps, partial
|
|
452
|
+
dates, and strings with extra text fail with
|
|
453
|
+
`profile.validation.assertionFailed`. The evaluator does not compile
|
|
454
|
+
profile-supplied regular expressions, call `Date.parse`, perform locale
|
|
455
|
+
parsing, or implement date ordering.
|
|
456
|
+
|
|
273
457
|
Empty selector results produce `profile.validation.emptySelection` for exists,
|
|
274
|
-
table, ID, reference, text, occurrence, and text-
|
|
275
|
-
Document-scoped required-section
|
|
276
|
-
against the document.
|
|
458
|
+
table, ID, reference, text, occurrence, text-length, and text-format
|
|
459
|
+
assertions. Document-scoped required-section, required-frontmatter, and
|
|
460
|
+
frontmatter-shape assertions evaluate against the document.
|
|
277
461
|
|
|
278
462
|
## Diagnostics
|
|
279
463
|
|
|
@@ -284,11 +468,17 @@ error severity. Validation diagnostics use the rule severity. Source ranges are
|
|
|
284
468
|
included when a selected target has source evidence; locations are omitted
|
|
285
469
|
rather than fabricated when unavailable.
|
|
286
470
|
|
|
471
|
+
Rule-level `when` uses the existing validation diagnostic codes. When
|
|
472
|
+
applicability does not match, those diagnostics are cloned into
|
|
473
|
+
`ruleResults[].when.diagnostics`; they are not promoted into top-level
|
|
474
|
+
`diagnostics`, so a skipped rule with a nested error-severity applicability
|
|
475
|
+
diagnostic can still leave the aggregate `valid` value `true`.
|
|
476
|
+
|
|
287
477
|
| Code | Severity source | Emitted when |
|
|
288
478
|
| --- | --- | --- |
|
|
289
479
|
| `profile.config.invalidYaml` | `error` | YAML text cannot be parsed or materialized as JSON-safe profile data. |
|
|
290
480
|
| `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`. |
|
|
481
|
+
| `profile.config.unsupportedSyntaxVersion` | `error` | `syntaxVersion` is missing or is not `markdown-engine.validation@v1` or `markdown-engine.validation@v2`. |
|
|
292
482
|
| `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
483
|
| `profile.config.documentVersionMismatch` | `error` | Resolved profile `documentVersion` differs from the supplied `EngineDocument.version`. |
|
|
294
484
|
| `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. |
|
|
@@ -296,15 +486,24 @@ rather than fabricated when unavailable.
|
|
|
296
486
|
| `profile.compile.unsupportedAssertion` | `error` | Parsed YAML or JSON-safe `assert` input contains an unsupported first-level assertion member that does not have unsupported-key precedence. |
|
|
297
487
|
| `profile.compile.incompatibleSelectorAssertion` | `error` | A supported selector target is paired with an incompatible supported assertion. |
|
|
298
488
|
| `profile.validation.emptySelection` | Rule severity | A rule cannot evaluate because its selector matches no applicable target. |
|
|
299
|
-
| `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,
|
|
489
|
+
| `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, text-length bound failures, and text-format `isoDate` failures. |
|
|
300
490
|
| `profile.validation.duplicateId` | Rule severity | An `ids.unique` assertion finds repeated IDs. |
|
|
491
|
+
| `profile.validation.frontmatterForbidden` | Rule severity | A `frontmatterShape` assertion with `presence: "forbidden"` finds present frontmatter. |
|
|
492
|
+
| `profile.validation.frontmatterFieldEmpty` | Rule severity | A frontmatter field configured with `nonEmpty: true` is not a non-empty string. |
|
|
301
493
|
| `profile.validation.frontmatterFieldMissing` | Rule severity | A required frontmatter field is absent. |
|
|
494
|
+
| `profile.validation.frontmatterFieldTypeMismatch` | Rule severity | A frontmatter field value does not match the configured `frontmatterShape.fields[].valueType` without coercion. |
|
|
495
|
+
| `profile.validation.frontmatterMissing` | Rule severity | A `frontmatterShape` assertion with `presence: "required"` finds absent frontmatter. |
|
|
496
|
+
| `profile.validation.idCountTooHigh` | Rule severity | Unique ID count after filtering is higher than `ids.maxCount`. |
|
|
497
|
+
| `profile.validation.idCountTooLow` | Rule severity | Unique ID count after filtering is lower than `ids.minCount`. |
|
|
302
498
|
| `profile.validation.referenceMissing` | Rule severity | A source ID is absent from a required target section. |
|
|
303
499
|
| `profile.validation.sectionMissing` | Rule severity | A required section heading is absent. |
|
|
304
500
|
| `profile.validation.sectionOrder` | Rule severity | A strict required-section order cannot be satisfied. |
|
|
501
|
+
| `profile.validation.tableColumnCoverageIdMissing` | Rule severity | A source ID is absent from the configured target table column. |
|
|
502
|
+
| `profile.validation.tableColumnCoverageTargetColumnMissing` | Rule severity | The configured target table column cannot be resolved. |
|
|
503
|
+
| `profile.validation.tableColumnCoverageTargetSectionMissing` | Rule severity | The configured target section cannot be resolved. |
|
|
305
504
|
| `profile.validation.textExcluded` | Rule severity | A selected target contains forbidden literal text. |
|
|
306
505
|
| `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. |
|
|
506
|
+
| `profile.validation.assertionUnsupported` | `error` | A compiled assertion or compiled assertion feature has no evaluator implementation; this is an internal safety diagnostic. |
|
|
308
507
|
|
|
309
508
|
## Result Shape
|
|
310
509
|
|
|
@@ -314,9 +513,12 @@ rather than fabricated when unavailable.
|
|
|
314
513
|
interface DeclarativeValidationResult extends ValidationResult {
|
|
315
514
|
valid: boolean;
|
|
316
515
|
diagnostics: readonly MarkdownDiagnostic[];
|
|
317
|
-
ruleResults: readonly
|
|
516
|
+
ruleResults: readonly (
|
|
517
|
+
| ValidationRuleResult
|
|
518
|
+
| DeclarativeValidationRuleResultV2
|
|
519
|
+
)[];
|
|
318
520
|
profile: {
|
|
319
|
-
syntaxVersion:
|
|
521
|
+
syntaxVersion: ValidationProfileSyntaxVersion;
|
|
320
522
|
documentVersion: EngineDocumentVersion;
|
|
321
523
|
ruleCount: number;
|
|
322
524
|
};
|
|
@@ -324,9 +526,61 @@ interface DeclarativeValidationResult extends ValidationResult {
|
|
|
324
526
|
}
|
|
325
527
|
```
|
|
326
528
|
|
|
327
|
-
|
|
328
|
-
validation
|
|
329
|
-
|
|
529
|
+
For admitted v2 profiles, result metadata records
|
|
530
|
+
`syntaxVersion: "markdown-engine.validation@v2"`, `evaluatedRuleCount`, and
|
|
531
|
+
`skippedRuleCount`. V2 rule results include the v1-compatible `ruleId`,
|
|
532
|
+
`passed`, and `diagnostics` fields plus `status`, optional skipped
|
|
533
|
+
applicability metadata, and flat, grouped, or skipped evaluation metadata:
|
|
534
|
+
|
|
535
|
+
```ts
|
|
536
|
+
interface DeclarativeValidationRuleResultV2 extends ValidationRuleResult {
|
|
537
|
+
status: "passed" | "failed" | "skipped";
|
|
538
|
+
when?: DeclarativeValidationApplicabilityResult;
|
|
539
|
+
evaluation:
|
|
540
|
+
| { kind: "assertions"; diagnostics: readonly MarkdownDiagnostic[] }
|
|
541
|
+
| {
|
|
542
|
+
kind: "anyOf";
|
|
543
|
+
selectedBranch?: DeclarativeValidationBranchReference;
|
|
544
|
+
branches: readonly DeclarativeValidationBranchResult[];
|
|
545
|
+
}
|
|
546
|
+
| {
|
|
547
|
+
kind: "allOf";
|
|
548
|
+
branches: readonly DeclarativeValidationBranchResult[];
|
|
549
|
+
}
|
|
550
|
+
| { kind: "skipped"; reason: "whenNotMatched" };
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
interface DeclarativeValidationApplicabilityResult {
|
|
554
|
+
status: "matched" | "notMatched";
|
|
555
|
+
diagnostics: readonly MarkdownDiagnostic[];
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
interface DeclarativeValidationBranchReference {
|
|
559
|
+
branchIndex: number;
|
|
560
|
+
label?: string;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
interface DeclarativeValidationBranchResult
|
|
564
|
+
extends DeclarativeValidationBranchReference {
|
|
565
|
+
status: "passed" | "failed";
|
|
566
|
+
diagnostics: readonly MarkdownDiagnostic[];
|
|
567
|
+
}
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
For configured `when`, matched applicability continues into normal flat or
|
|
571
|
+
grouped evaluation and contributes one evaluated rule. The evaluated rule result
|
|
572
|
+
does not serialize a `when` field. Non-matching applicability returns one
|
|
573
|
+
skipped rule result with `status: "skipped"`, `passed: true`, empty top-level
|
|
574
|
+
rule `diagnostics`, `when.status: "notMatched"`, nested applicability
|
|
575
|
+
diagnostics, `evaluation.kind: "skipped"`, and `reason: "whenNotMatched"`.
|
|
576
|
+
Skipped rules increment `skippedRuleCount`, do not increment
|
|
577
|
+
`evaluatedRuleCount`, and do not evaluate flat assertions or grouped branches.
|
|
578
|
+
|
|
579
|
+
`valid` is `false` when any top-level error-severity diagnostic exists in
|
|
580
|
+
`diagnostics`. Nested skipped applicability diagnostics under
|
|
581
|
+
`ruleResults[].when.diagnostics` do not by themselves make the aggregate result
|
|
582
|
+
invalid. Warning and info validation diagnostics can make a rule result fail
|
|
583
|
+
without making the aggregate result invalid.
|
|
330
584
|
|
|
331
585
|
Rule results are sorted deterministically. Each rule result includes the public
|
|
332
586
|
`ruleId`, `passed`, and cloned diagnostics. Results do not expose compiled rule
|
|
@@ -338,16 +592,27 @@ Evidence is emitted only when `DeclarativeValidationOptions.includeEvidence` is
|
|
|
338
592
|
`true`:
|
|
339
593
|
|
|
340
594
|
```ts
|
|
341
|
-
interface DeclarativeValidationEvidence
|
|
595
|
+
interface DeclarativeValidationEvidence<
|
|
596
|
+
RuleResult extends ValidationRuleResult = ValidationRuleResult,
|
|
597
|
+
> {
|
|
342
598
|
inputHash: string;
|
|
343
599
|
profileHash: string;
|
|
344
600
|
engineVersion: string;
|
|
345
601
|
runtimeVersion: string;
|
|
346
|
-
ruleResults: readonly
|
|
602
|
+
ruleResults: readonly RuleResult[];
|
|
347
603
|
diagnostics: readonly MarkdownDiagnostic[];
|
|
348
604
|
}
|
|
349
605
|
```
|
|
350
606
|
|
|
607
|
+
For v1 profiles, `ruleResults` contains the unchanged v1 rule-result shape. For
|
|
608
|
+
admitted v2 profiles, `ruleResults` clones the public v2 rule-result shape,
|
|
609
|
+
including `status`, flat assertion evaluation, and grouped `anyOf` / `allOf`
|
|
610
|
+
branch evaluation. Skipped v2 rule results are cloned through evidence in the
|
|
611
|
+
same `ruleResults` array, and `evidence.diagnostics` clones the top-level
|
|
612
|
+
diagnostics array. Evidence does not serialize compiled rule plans, selector
|
|
613
|
+
target records, assertion-specific ID count evidence, or assertion-specific
|
|
614
|
+
table-column coverage evidence.
|
|
615
|
+
|
|
351
616
|
`inputHash` is a lowercase hexadecimal SHA-256 digest of the stable JSON
|
|
352
617
|
serialization of the supplied normalized `EngineDocument` after omitting only
|
|
353
618
|
the top-level `document.path` field. Structural target paths remain part of the
|
|
@@ -360,7 +625,7 @@ serialization of the resolved `ValidationProfile` after applying the resolved
|
|
|
360
625
|
the same profile hash for the same document version and rules.
|
|
361
626
|
|
|
362
627
|
`engineVersion` records the package version that produced the evidence. In the
|
|
363
|
-
|
|
628
|
+
3.0 release line this is `"3.0.0"` even though `documentVersion` remains
|
|
364
629
|
`"1.0.0"`.
|
|
365
630
|
|
|
366
631
|
Raw Markdown bytes, raw YAML bytes, YAML comments, caller file paths, and
|
|
@@ -384,7 +649,9 @@ validate the Markdown file.
|
|
|
384
649
|
|
|
385
650
|
After profile compilation succeeds, the CLI emits a validation-result JSON
|
|
386
651
|
shape whether the document passes or fails. Validation CLI results include
|
|
387
|
-
evidence.
|
|
652
|
+
evidence. V2 CLI results use the same validation-result arm of the CLI JSON
|
|
653
|
+
union; there is no extra CLI discriminator beyond
|
|
654
|
+
`profile.syntaxVersion: "markdown-engine.validation@v2"`.
|
|
388
655
|
|
|
389
656
|
## CLI JSON Union
|
|
390
657
|
|
|
@@ -411,23 +678,85 @@ unsupported assertion members, and incompatible selector/assertion pairs. It
|
|
|
411
678
|
contains no `profile` and no `evidence`.
|
|
412
679
|
|
|
413
680
|
Validation-result JSON is used after profile compilation succeeds. It contains
|
|
414
|
-
`profile`, `ruleResults`, `diagnostics`, `valid`, and `evidence`.
|
|
681
|
+
`profile`, `ruleResults`, `diagnostics`, `valid`, and `evidence`. For v2
|
|
682
|
+
profiles, that same validation-result JSON can include `evaluatedRuleCount`,
|
|
683
|
+
`skippedRuleCount`, `status: "skipped"`, nested `when` diagnostics, and
|
|
684
|
+
`evaluation.kind: "skipped"`.
|
|
415
685
|
|
|
416
686
|
## Exit Codes
|
|
417
687
|
|
|
418
688
|
| Exit code | Meaning |
|
|
419
689
|
| --- | --- |
|
|
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. |
|
|
690
|
+
| `0` | Validation completed with no top-level error-severity diagnostics. |
|
|
691
|
+
| `1` | Profile/config/compile, Markdown normalization, document-version mismatch, or top-level validation diagnostics include at least one error. |
|
|
422
692
|
| `2` | CLI usage, unsupported format, unknown argument, missing argument value, repeated singleton flag, unsupported `--document-version`, or local file read error. |
|
|
423
693
|
|
|
424
694
|
## Compatibility And Migration
|
|
425
695
|
|
|
426
696
|
The v1 declarative validation syntax is a durable authoring contract for the
|
|
427
|
-
|
|
697
|
+
3.0 package release line. Changes to profile syntax names, selector names,
|
|
428
698
|
assertion names, result fields, diagnostic codes, CLI flags, CLI JSON shape, or
|
|
429
699
|
evidence hash inputs require explicit compatibility review.
|
|
430
700
|
|
|
701
|
+
V1 preservation is explicit: v1 authoring syntax, v1 rule result shape, v1
|
|
702
|
+
diagnostic inventory, v1 CLI JSON behavior, and v1 evidence hash inputs remain
|
|
703
|
+
unchanged by the admitted v2 syntax.
|
|
704
|
+
|
|
705
|
+
Compatibility examples:
|
|
706
|
+
|
|
707
|
+
```yaml
|
|
708
|
+
# v1 compatibility profile: remains on the v1 authoring and result contract.
|
|
709
|
+
syntaxVersion: markdown-engine.validation@v1
|
|
710
|
+
rules:
|
|
711
|
+
- id: sections.present
|
|
712
|
+
select:
|
|
713
|
+
target: document
|
|
714
|
+
assert:
|
|
715
|
+
sectionsRequired:
|
|
716
|
+
headings:
|
|
717
|
+
- Mission Brief
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
```yaml
|
|
721
|
+
# v2 opt-in profile: selects Conditional V2 behavior explicitly.
|
|
722
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
723
|
+
rules:
|
|
724
|
+
- id: release.docs
|
|
725
|
+
anyOf:
|
|
726
|
+
- label: release-section
|
|
727
|
+
select:
|
|
728
|
+
target: section
|
|
729
|
+
title: Release
|
|
730
|
+
assert:
|
|
731
|
+
exists: true
|
|
732
|
+
- label: changelog-link
|
|
733
|
+
select:
|
|
734
|
+
target: link
|
|
735
|
+
text: changelog
|
|
736
|
+
assert:
|
|
737
|
+
exists: true
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
```yaml
|
|
741
|
+
# v2 date-heading profile: validates exact ISO date heading text.
|
|
742
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
743
|
+
rules:
|
|
744
|
+
- id: log.date-heading
|
|
745
|
+
select:
|
|
746
|
+
target: heading
|
|
747
|
+
depth: 2
|
|
748
|
+
assert:
|
|
749
|
+
textFormat:
|
|
750
|
+
format: isoDate
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
The v1 profile above continues to emit the v1 validation-result shape. V2
|
|
754
|
+
profiles emit syntax-versioned v2 result metadata with
|
|
755
|
+
`evaluatedRuleCount` and `skippedRuleCount`; v2 rule results include `status`
|
|
756
|
+
and `evaluation`. The CLI does not add a second discriminator for v2; consumers
|
|
757
|
+
branch on
|
|
758
|
+
`profile.syntaxVersion: "markdown-engine.validation@v2"`.
|
|
759
|
+
|
|
431
760
|
Migration notes:
|
|
432
761
|
|
|
433
762
|
- Consumers using fixed `validate(document, config)` rule families can continue
|
|
@@ -438,6 +767,11 @@ Migration notes:
|
|
|
438
767
|
- Consumers parsing CLI validation output must handle the
|
|
439
768
|
`DeclarativeValidationCliJsonResult` union. Profile-stage failures do not
|
|
440
769
|
include `profile` or `evidence`.
|
|
770
|
+
- Consumers opting into `markdown-engine.validation@v2` must handle rule
|
|
771
|
+
`status` values of `"passed"`, `"failed"`, and `"skipped"`, plus
|
|
772
|
+
`evaluatedRuleCount`, `skippedRuleCount`, grouped branch results, and nested
|
|
773
|
+
skipped-rule `when` diagnostics. Aggregate validity is still determined from
|
|
774
|
+
top-level diagnostics.
|
|
441
775
|
- Consumers that compare evidence hashes must normalize expectations around
|
|
442
776
|
resolved `documentVersion`, default rule severity, stable key order, and
|
|
443
777
|
exclusion of only top-level `document.path` from `inputHash`.
|
|
@@ -501,6 +835,88 @@ rules:
|
|
|
501
835
|
count: 1
|
|
502
836
|
```
|
|
503
837
|
|
|
838
|
+
Conditional v2 grouped rule with applicability:
|
|
839
|
+
|
|
840
|
+
```yaml
|
|
841
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
842
|
+
rules:
|
|
843
|
+
- id: release.when.docs-ready
|
|
844
|
+
when:
|
|
845
|
+
select:
|
|
846
|
+
target: section
|
|
847
|
+
title: Release
|
|
848
|
+
assert:
|
|
849
|
+
exists: true
|
|
850
|
+
anyOf:
|
|
851
|
+
- label: contract-link
|
|
852
|
+
select:
|
|
853
|
+
target: link
|
|
854
|
+
section: Release
|
|
855
|
+
text: contract
|
|
856
|
+
assert:
|
|
857
|
+
exists: true
|
|
858
|
+
- label: contract-heading
|
|
859
|
+
select:
|
|
860
|
+
target: heading
|
|
861
|
+
text: Contract
|
|
862
|
+
assert:
|
|
863
|
+
exists: true
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
### OKF v0.1 Hard-Validation Profile Composition
|
|
867
|
+
|
|
868
|
+
`markdown-engine` does not ship a built-in OKF validator. Consumers that want to
|
|
869
|
+
validate OKF v0.1 hard conformance compose generic declarative-validation
|
|
870
|
+
profiles and route documents to those profiles themselves. The caller owns
|
|
871
|
+
bundle traversal, path classification, IO, and role selection before invoking
|
|
872
|
+
`validateDocumentSet`.
|
|
873
|
+
|
|
874
|
+
The packaged OKF proof under
|
|
875
|
+
`fixtures/declarative-validation/examples/okf-v0.1/**` uses four generic
|
|
876
|
+
profiles:
|
|
877
|
+
|
|
878
|
+
| Caller-classified role | Example profile | Generic checks |
|
|
879
|
+
| --- | --- | --- |
|
|
880
|
+
| Concept document | `profiles/concept.yaml` | `frontmatterShape` requires parseable frontmatter with a non-empty string `type`. |
|
|
881
|
+
| Bundle-root `index.md` | `profiles/root-index.yaml` | `frontmatterShape` accepts a non-empty string `okf_version` when root version frontmatter is present. |
|
|
882
|
+
| Non-root `index.md` | `profiles/non-root-index.yaml` | `frontmatterShape` forbids frontmatter on directory index files. |
|
|
883
|
+
| `log.md` | `profiles/log.yaml` | `textFormat` checks level-2 log headings as real `YYYY-MM-DD` dates. |
|
|
884
|
+
|
|
885
|
+
Concept validation applies only to non-reserved concept documents. Reserved
|
|
886
|
+
`index.md` and `log.md` files are not routed through the concept profile and do
|
|
887
|
+
not fail merely because they lack concept `type` frontmatter. The bundle-root
|
|
888
|
+
`index.md` may carry the OKF version exception `okf_version: "0.1"` through the
|
|
889
|
+
root-index profile; non-root `index.md` files use the non-root index profile and
|
|
890
|
+
forbid frontmatter. `log.md` is validated as a log with `textFormat`, not as a
|
|
891
|
+
concept document.
|
|
892
|
+
|
|
893
|
+
A caller-owned routing layer can classify entries by path and choose the
|
|
894
|
+
matching profile before validation:
|
|
895
|
+
|
|
896
|
+
```ts
|
|
897
|
+
const entries = bundleMarkdownFiles.map((file) => {
|
|
898
|
+
const role = classifyOkfPath(file.path);
|
|
899
|
+
|
|
900
|
+
return {
|
|
901
|
+
path: file.path,
|
|
902
|
+
markdown: file.markdown,
|
|
903
|
+
profile: profilesByRole[role],
|
|
904
|
+
profilePath: profilePathsByRole[role],
|
|
905
|
+
};
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
const result = validateDocumentSet(entries, {
|
|
909
|
+
documentVersion: "1.0.0",
|
|
910
|
+
includeEvidence: true,
|
|
911
|
+
});
|
|
912
|
+
```
|
|
913
|
+
|
|
914
|
+
This composition proves hard OKF conformance only. It does not add CLI
|
|
915
|
+
traversal, an OKF adapter, runtime OKF semantics, directory watching, bundle
|
|
916
|
+
discovery, link checking, unknown-type rejection, optional metadata quality
|
|
917
|
+
checks, or rejection of missing optional `index.md` files. It also does not add
|
|
918
|
+
engine-owned path classification.
|
|
919
|
+
|
|
504
920
|
CLI invocation:
|
|
505
921
|
|
|
506
922
|
```sh
|
|
@@ -540,6 +956,11 @@ The v1 contract explicitly excludes:
|
|
|
540
956
|
- operational-design-spec, AGENTS.md, TASK.md, or other domain-specific rule
|
|
541
957
|
meaning in core engine code
|
|
542
958
|
|
|
959
|
+
The admitted v2 Conditional V2 surface also excludes `documentVersion: "3.0.0"`,
|
|
960
|
+
recursive grouped rules, branch-level `when`, profile-defined predicates,
|
|
961
|
+
assertion-specific evidence payloads, a separate skipped-rule evidence channel,
|
|
962
|
+
and a new CLI JSON discriminator.
|
|
963
|
+
|
|
543
964
|
The CLI reads only the caller-specified local Markdown and profile files. The
|
|
544
965
|
API owns no file traversal, daemon, database, browser runtime, network service,
|
|
545
966
|
agent adapter, MCP transport, runtime lens, or persistent cache.
|
|
@@ -553,7 +974,8 @@ npm run docs:declarative-validation-contract
|
|
|
553
974
|
npm run audit:declarative-validation-boundary
|
|
554
975
|
```
|
|
555
976
|
|
|
556
|
-
The documentation gate checks this contract, README links,
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
977
|
+
The documentation gate checks this contract, README links, legacy contract and
|
|
978
|
+
boundary evidence files, Conditional V2 EVD-6 reviewer notes, and package script
|
|
979
|
+
wiring. The boundary audit checks dependency drift, source-level runtime
|
|
980
|
+
boundary patterns, unsupported regex-like and executable profile-key coverage,
|
|
981
|
+
and declarative validation boundary evidence.
|