@jasonbelmonti/markdown-engine 3.2.0 → 3.4.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 (42) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +27 -10
  3. package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
  4. package/dist/declarative-validation/assertions/evaluator.js +6 -0
  5. package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
  6. package/dist/declarative-validation/assertions/selection-count.d.ts +9 -0
  7. package/dist/declarative-validation/assertions/selection-count.d.ts.map +1 -0
  8. package/dist/declarative-validation/assertions/selection-count.js +36 -0
  9. package/dist/declarative-validation/assertions/selection-count.js.map +1 -0
  10. package/dist/declarative-validation/assertions/table-columns-exact.d.ts +9 -0
  11. package/dist/declarative-validation/assertions/table-columns-exact.d.ts.map +1 -0
  12. package/dist/declarative-validation/assertions/table-columns-exact.js +44 -0
  13. package/dist/declarative-validation/assertions/table-columns-exact.js.map +1 -0
  14. package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
  15. package/dist/declarative-validation/compiler/assertion-builders.js +20 -1
  16. package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
  17. package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
  18. package/dist/declarative-validation/compiler/assertions.js +2 -0
  19. package/dist/declarative-validation/compiler/assertions.js.map +1 -1
  20. package/dist/declarative-validation/compiler/compatibility.d.ts.map +1 -1
  21. package/dist/declarative-validation/compiler/compatibility.js +2 -0
  22. package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
  23. package/dist/declarative-validation/compiler/length-assertion-builders.d.ts +1 -0
  24. package/dist/declarative-validation/compiler/length-assertion-builders.d.ts.map +1 -1
  25. package/dist/declarative-validation/compiler/length-assertion-builders.js +6 -0
  26. package/dist/declarative-validation/compiler/length-assertion-builders.js.map +1 -1
  27. package/dist/declarative-validation/compiler/plan.d.ts +7 -0
  28. package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
  29. package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
  30. package/dist/declarative-validation/profile/assertion-schema.js +28 -0
  31. package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
  32. package/dist/declarative-validation/profile/index.d.ts +7 -0
  33. package/dist/declarative-validation/profile/index.d.ts.map +1 -1
  34. package/dist/declarative-validation/profile/length-bound-schema.d.ts +1 -1
  35. package/dist/declarative-validation/profile/length-bound-schema.d.ts.map +1 -1
  36. package/dist/declarative-validation/profile/length-bound-schema.js.map +1 -1
  37. package/dist/internal/package-version.d.ts +1 -1
  38. package/dist/internal/package-version.js +1 -1
  39. package/dist-bundled/markdown-engine-cli.mjs +439 -276
  40. package/docs/contracts/declarative-validation.md +88 -12
  41. package/package.json +2 -2
  42. package/scripts/install-markdown-engine-cli.sh +2 -2
@@ -1,9 +1,10 @@
1
1
  # Declarative Validation Contract
2
2
 
3
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, document `sourceLength`
6
- schema and runtime measurement, ID count-bound schema and
4
+ Last updated: 2026-08-15
5
+ Current v2 surface: flat-rule result/evidence shell, generic selector
6
+ `selectionCount` bounds, document `sourceLength` schema and runtime
7
+ measurement, exact normalized table-header `tableColumnsExact` assertions, ID count-bound schema and
7
8
  runtime evaluator contract, plus `tableColumnCoverage` schema, compiled-plan,
8
9
  and runtime evaluator contract, `frontmatterShape` schema, compiled-plan, and
9
10
  runtime evaluator contract, `textFormat` schema, compiled-plan, and runtime
@@ -26,7 +27,8 @@ rich IR document contract, while the profile admission path recognizes
26
27
  `severity`, `select`, and `assert`; non-recursive `anyOf` and `allOf`; and
27
28
  optional rule-level `when`. The admitted v2 path exposes the result and evidence
28
29
  shell needed to distinguish assertion, grouped, and skipped evaluation output
29
- from v1 output, plus the ID count-bound schema, compiled-plan, and runtime
30
+ from v1 output, plus generic selector `selectionCount` bounds, the ID
31
+ count-bound schema, compiled-plan, and runtime
30
32
  evaluator contract; the `tableColumnCoverage` schema, compiled-plan, and
31
33
  runtime evaluator contract; the `frontmatterShape` schema, private
32
34
  compiled-plan, and runtime evaluator contract; the `textFormat` schema,
@@ -99,11 +101,13 @@ syntaxVersion: markdown-engine.validation@v2
99
101
  ```
100
102
 
101
103
  This release recognizes v2 as a distinct syntax version at profile admission,
102
- admits ID count bounds at the schema, compiled-plan, and runtime evaluator
103
- layers, admits `tableColumnCoverage` at the schema, internal compiled-plan, and
104
+ admits `selectionCount` and ID count bounds at the schema, compiled-plan, and
105
+ runtime evaluator layers, admits `tableColumnCoverage` at the schema, internal
106
+ compiled-plan, and
104
107
  runtime evaluator layers, admits `frontmatterShape` at the schema, internal
105
108
  compiled-plan, and runtime evaluator layers, admits `textFormat` at the schema,
106
- internal compiled-plan, and runtime evaluator layers, admits non-recursive
109
+ internal compiled-plan, and runtime evaluator layers, admits `tableColumnsExact`
110
+ at the schema, internal compiled-plan, and runtime evaluator layers, admits non-recursive
107
111
  grouped rules at the schema, compiled-plan, and runtime evaluator layers, and
108
112
  admits optional rule-level `when` at the schema, internal compiled-plan, and
109
113
  matcher layers.
@@ -233,7 +237,7 @@ emit `profile.config.invalidShape`.
233
237
  Rule-level `when` is allowed only on v2 rules. Branch-level `when` remains
234
238
  unsupported. V1 profiles preserve the original flat rule authoring contract;
235
239
  grouped `anyOf` / `allOf`, ID count bounds, `tableColumnCoverage`,
236
- `frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
240
+ `tableColumnsExact`, `frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
237
241
 
238
242
  Profile values must be JSON-safe data properties after YAML materialization.
239
243
  Functions, accessors, proxies, cyclic structures, sparse arrays, `undefined`
@@ -293,6 +297,10 @@ Supported assertion members are:
293
297
  ```ts
294
298
  interface DeclarativeAssertion {
295
299
  exists?: true;
300
+ selectionCount?: {
301
+ min?: number;
302
+ max?: number;
303
+ };
296
304
  sectionsRequired?: {
297
305
  headings: readonly string[];
298
306
  order?: "none" | "strict";
@@ -300,6 +308,9 @@ interface DeclarativeAssertion {
300
308
  tableColumnsRequired?: {
301
309
  columns: readonly string[];
302
310
  };
311
+ tableColumnsExact?: {
312
+ columns: readonly string[];
313
+ };
303
314
  ids?: {
304
315
  prefix?: string;
305
316
  unique?: boolean;
@@ -371,8 +382,10 @@ Selector/assertion compatibility is part of the public contract:
371
382
  | Assertion | Compatible selector targets |
372
383
  | --- | --- |
373
384
  | `exists` | all supported selector targets |
385
+ | `selectionCount` | all supported selector targets |
374
386
  | `sectionsRequired` | `document` |
375
387
  | `tableColumnsRequired` | `table` |
388
+ | `tableColumnsExact` | `table` |
376
389
  | `ids` | all supported selector targets |
377
390
  | `references` | `document` |
378
391
  | `tableColumnCoverage` | `document` |
@@ -391,6 +404,29 @@ Incompatible supported selector/assertion pairs emit
391
404
  target and fails with `profile.validation.emptySelection` when the selector
392
405
  resolves zero targets.
393
406
 
407
+ For v2 profiles, `selectionCount` must include `min`, `max`, or both. Bounds
408
+ are inclusive non-negative integers, and `min` must be less than or equal to
409
+ `max` when both are present. It evaluates the number of targets resolved by the
410
+ rule selector and is compatible with every supported selector target. An empty
411
+ selection is count zero, so `max: 0` passes and `min: 1` fails with
412
+ `profile.validation.assertionFailed`; `selectionCount` does not replace its
413
+ result with `profile.validation.emptySelection`. Maximum-bound failures use the
414
+ first excess selected target as source evidence when available. Minimum-bound
415
+ failures at zero omit source location rather than fabricating one.
416
+
417
+ For v2 profiles, `tableColumnsExact` requires a non-empty `columns` array of
418
+ non-empty strings and is compatible only with a `table` selector. It compares
419
+ the complete normalized header sequence from the selected `EngineTable` exactly:
420
+ count, order, text, and duplicate occurrences must all match. Additional
421
+ columns before, between, or after configured columns, and missing, reordered,
422
+ renamed, or duplicated columns fail with
423
+ `profile.validation.assertionFailed`. The deterministic diagnostic includes the
424
+ expected and actual header sequences. When an actual header first mismatches or
425
+ is excess, its header-cell source range is attached when available; otherwise
426
+ the normal selected-table source evidence is retained. This assertion does not
427
+ change the ordered-subsequence behavior of `tableColumnsRequired` or table
428
+ selector `header` / `tableHeader` matching.
429
+
394
430
  `sectionsRequired.order` defaults to `none`. `strict` checks that configured
395
431
  headings appear as an ordered subsequence in the normalized section tree
396
432
  flattened in source order.
@@ -483,9 +519,11 @@ profile-supplied regular expressions, call `Date.parse`, perform locale
483
519
  parsing, or implement date ordering.
484
520
 
485
521
  Empty selector results produce `profile.validation.emptySelection` for exists,
486
- table, ID, reference, text, occurrence, text-length, and text-format
487
- assertions. Document-scoped required-section, required-frontmatter, and
488
- frontmatter-shape assertions evaluate against the document.
522
+ table, ID, reference, text, occurrence, text-length, text-format, and
523
+ tableColumnsExact assertions.
524
+ `selectionCount` instead evaluates the empty selection as zero. Document-scoped
525
+ required-section, required-frontmatter, and frontmatter-shape assertions
526
+ evaluate against the document.
489
527
 
490
528
  ## Diagnostics
491
529
 
@@ -517,7 +555,7 @@ an unproven pass.
517
555
  | `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. |
518
556
  | `profile.compile.incompatibleSelectorAssertion` | `error` | A supported selector target is paired with an incompatible supported assertion. |
519
557
  | `profile.validation.emptySelection` | Rule severity | A rule cannot evaluate because its selector matches no applicable target. |
520
- | `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. |
558
+ | `profile.validation.assertionFailed` | Rule severity | A supported assertion evaluates and fails without a more specific diagnostic code, including selector-count bound failures, missing table columns, exact table-header mismatches, exact occurrence-count mismatches, text-length bound failures, and text-format `isoDate` failures. |
521
559
  | `profile.validation.duplicateId` | Rule severity | An `ids.unique` assertion finds repeated IDs. |
522
560
  | `profile.validation.frontmatterForbidden` | Rule severity | A `frontmatterShape` assertion with `presence: "forbidden"` finds present frontmatter. |
523
561
  | `profile.validation.frontmatterFieldEmpty` | Rule severity | A frontmatter field configured with `nonEmpty: true` is not a non-empty string. |
@@ -919,6 +957,44 @@ rules:
919
957
  exists: true
920
958
  ```
921
959
 
960
+ V2 selector cardinality profile:
961
+
962
+ ```yaml
963
+ syntaxVersion: markdown-engine.validation@v2
964
+ rules:
965
+ - id: actions.table.exactly-one
966
+ select:
967
+ target: table
968
+ section: Execution Actions
969
+ header:
970
+ - Step ID
971
+ - Action
972
+ assert:
973
+ selectionCount:
974
+ min: 1
975
+ max: 1
976
+ ```
977
+
978
+ V2 exact table-column profile:
979
+
980
+ ```yaml
981
+ syntaxVersion: markdown-engine.validation@v2
982
+ rules:
983
+ - id: task-control.columns
984
+ select:
985
+ target: table
986
+ assert:
987
+ tableColumnsExact:
988
+ columns:
989
+ - Contract state
990
+ - Execution route
991
+ - State rationale
992
+ ```
993
+
994
+ The assertion compares the complete normalized header sequence. It is stricter
995
+ than `tableColumnsRequired`, which continues to accept ordered-subsequence
996
+ matches with unrelated columns before, between, or after required names.
997
+
922
998
  ### OKF v0.1 Hard-Validation Profile Composition
923
999
 
924
1000
  `markdown-engine` does not ship a built-in OKF validator. Consumers that want to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasonbelmonti/markdown-engine",
3
- "version": "3.2.0",
3
+ "version": "3.4.0",
4
4
  "description": "Deterministic Markdown parsing and validation engine package.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -71,7 +71,7 @@
71
71
  "test:validation:profile": "npm run build && vitest run tests/declarative-validation-profile.test.ts \"--exclude=.worktrees/**\"",
72
72
  "test:validation:compiler": "npm run build && vitest run tests/declarative-validation-compiler.test.ts \"--exclude=.worktrees/**\"",
73
73
  "test:validation:selectors": "npm run build && vitest run tests/declarative-validation-selectors.test.ts \"--exclude=.worktrees/**\"",
74
- "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-frontmatter-shape-assertions.test.ts tests/declarative-validation-table-column-coverage-fixtures.test.ts tests/declarative-validation-source-assertion-coverage.test.ts tests/declarative-validation-source-length.test.ts tests/declarative-validation-normalized-source-ranges.test.ts tests/declarative-validation-grouped-rules-fixtures.test.ts tests/declarative-validation-when-skipped-rules-fixtures.test.ts \"--exclude=.worktrees/**\"",
74
+ "test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-frontmatter-shape-assertions.test.ts tests/declarative-validation-table-column-coverage-fixtures.test.ts tests/declarative-validation-table-columns-exact.test.ts tests/declarative-validation-source-assertion-coverage.test.ts tests/declarative-validation-source-length.test.ts tests/declarative-validation-selection-count.test.ts tests/declarative-validation-normalized-source-ranges.test.ts tests/declarative-validation-grouped-rules-fixtures.test.ts tests/declarative-validation-when-skipped-rules-fixtures.test.ts \"--exclude=.worktrees/**\"",
75
75
  "test:validation:diagnostics": "npm run build && vitest run tests/declarative-validation-diagnostics.test.ts \"--exclude=.worktrees/**\"",
76
76
  "test:validation:cli": "npm run build && vitest run tests/declarative-validation-cli.test.ts \"--exclude=.worktrees/**\"",
77
77
  "test:validation:examples": "npm run build && vitest run tests/declarative-validation-examples.test.ts \"--exclude=.worktrees/**\"",
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env sh
2
2
  set -eu
3
3
 
4
- VERSION="3.2.0"
4
+ VERSION="3.4.0"
5
5
  PACKAGE="@jasonbelmonti/markdown-engine"
6
- EXPECTED_SHA256="5033d08160fcd3f44b11498dc29d01db73d7aff2a55eb3257fd9a1c14e29c3f6"
6
+ EXPECTED_SHA256="41225925904c038c05392b19e7203bd723d1cbaaa47b48d96648fb0275d3d05b"
7
7
  DEFAULT_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
8
8
  MARKDOWN_ENGINE_HOME="${MARKDOWN_ENGINE_HOME:-$DEFAULT_DATA_HOME/markdown-engine}"
9
9
  MARKDOWN_ENGINE_BIN_DIR="${MARKDOWN_ENGINE_BIN_DIR:-$HOME/.local/bin}"