@jasonbelmonti/markdown-engine 3.5.0 → 3.8.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 +32 -0
- package/README.md +40 -12
- package/dist/cli/declarative-validation.d.ts +3 -9
- package/dist/cli/declarative-validation.d.ts.map +1 -1
- package/dist/cli/declarative-validation.js +6 -16
- package/dist/cli/declarative-validation.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +2 -0
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/validate-args.d.ts +3 -1
- package/dist/cli/validate-args.d.ts.map +1 -1
- package/dist/cli/validate-args.js +45 -92
- package/dist/cli/validate-args.js.map +1 -1
- package/dist/cli/validation-output.d.ts +16 -0
- package/dist/cli/validation-output.d.ts.map +1 -0
- package/dist/cli/validation-output.js +26 -0
- package/dist/cli/validation-output.js.map +1 -0
- package/dist/cli/validation-report.d.ts +8 -0
- package/dist/cli/validation-report.d.ts.map +1 -0
- package/dist/cli/validation-report.js +24 -0
- package/dist/cli/validation-report.js.map +1 -0
- package/dist/cli/validation-summary.d.ts +47 -0
- package/dist/cli/validation-summary.d.ts.map +1 -0
- package/dist/cli/validation-summary.js +55 -0
- package/dist/cli/validation-summary.js.map +1 -0
- package/dist/declarative-validation/assertions/evaluator.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/evaluator.js +3 -0
- package/dist/declarative-validation/assertions/evaluator.js.map +1 -1
- package/dist/declarative-validation/assertions/id-targets.d.ts +2 -0
- package/dist/declarative-validation/assertions/id-targets.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/id-targets.js.map +1 -1
- package/dist/declarative-validation/assertions/table-column-coverage.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/table-column-coverage.js +8 -10
- package/dist/declarative-validation/assertions/table-column-coverage.js.map +1 -1
- package/dist/declarative-validation/assertions/table-rows-complete.d.ts +4 -0
- package/dist/declarative-validation/assertions/table-rows-complete.d.ts.map +1 -0
- package/dist/declarative-validation/assertions/table-rows-complete.js +40 -0
- package/dist/declarative-validation/assertions/table-rows-complete.js.map +1 -0
- package/dist/declarative-validation/assertions/text.d.ts.map +1 -1
- package/dist/declarative-validation/assertions/text.js +5 -0
- package/dist/declarative-validation/assertions/text.js.map +1 -1
- package/dist/declarative-validation/assertions/visible-text.d.ts +5 -0
- package/dist/declarative-validation/assertions/visible-text.d.ts.map +1 -0
- package/dist/declarative-validation/assertions/visible-text.js +42 -0
- package/dist/declarative-validation/assertions/visible-text.js.map +1 -0
- package/dist/declarative-validation/compiler/assertion-builders.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/assertion-builders.js +13 -2
- package/dist/declarative-validation/compiler/assertion-builders.js.map +1 -1
- package/dist/declarative-validation/compiler/assertion-shapes.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/assertion-shapes.js +20 -4
- package/dist/declarative-validation/compiler/assertion-shapes.js.map +1 -1
- package/dist/declarative-validation/compiler/assertions.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/assertions.js +1 -0
- 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 +1 -0
- package/dist/declarative-validation/compiler/compatibility.js.map +1 -1
- package/dist/declarative-validation/compiler/plan.d.ts +6 -1
- package/dist/declarative-validation/compiler/plan.d.ts.map +1 -1
- package/dist/declarative-validation/compiler/table-rows-complete.d.ts +3 -0
- package/dist/declarative-validation/compiler/table-rows-complete.d.ts.map +1 -0
- package/dist/declarative-validation/compiler/table-rows-complete.js +17 -0
- package/dist/declarative-validation/compiler/table-rows-complete.js.map +1 -0
- package/dist/declarative-validation/profile/assertion-schema.d.ts.map +1 -1
- package/dist/declarative-validation/profile/assertion-schema.js +35 -6
- package/dist/declarative-validation/profile/assertion-schema.js.map +1 -1
- package/dist/declarative-validation/profile/cell-predicate-schema.d.ts +4 -0
- package/dist/declarative-validation/profile/cell-predicate-schema.d.ts.map +1 -0
- package/dist/declarative-validation/profile/cell-predicate-schema.js +29 -0
- package/dist/declarative-validation/profile/cell-predicate-schema.js.map +1 -0
- package/dist/declarative-validation/profile/index.d.ts +4 -0
- package/dist/declarative-validation/profile/index.d.ts.map +1 -1
- package/dist/declarative-validation/profile/selector-schema.d.ts.map +1 -1
- package/dist/declarative-validation/profile/selector-schema.js +1 -23
- package/dist/declarative-validation/profile/selector-schema.js.map +1 -1
- package/dist/declarative-validation/selectors/table-targets.d.ts +2 -1
- package/dist/declarative-validation/selectors/table-targets.d.ts.map +1 -1
- package/dist/declarative-validation/selectors/table-targets.js +13 -5
- package/dist/declarative-validation/selectors/table-targets.js.map +1 -1
- package/dist/internal/package-version.d.ts +1 -1
- package/dist/internal/package-version.js +1 -1
- package/dist-bundled/markdown-engine-cli.mjs +444 -215
- package/docs/contracts/declarative-validation.md +155 -8
- package/fixtures/declarative-validation/examples/table-rows-complete/README.md +65 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/blockquote.md +3 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/complete.md +3 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/consumer-missing.md +50 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/consumer-repaired.md +50 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/excess.md +3 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/explicit-empty.md +3 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/header-only.md +2 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/legacy.yaml +10 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/missing.md +3 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/multiple.md +5 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/oracle.json +85 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/profile.yaml +11 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/shape.yaml +8 -0
- package/fixtures/declarative-validation/examples/table-rows-complete/syntax.md +3 -0
- package/package.json +2 -2
- package/scripts/install-markdown-engine-cli.sh +2 -2
|
@@ -1,10 +1,11 @@
|
|
|
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-
|
|
4
|
+
Last updated: 2026-09-25
|
|
5
5
|
Current v2 surface: flat-rule result/evidence shell, generic selector
|
|
6
6
|
`selectionCount` bounds, document `sourceLength` schema and runtime
|
|
7
|
-
measurement,
|
|
7
|
+
measurement, opt-in body-row `tableRowsComplete` assertions, exact normalized
|
|
8
|
+
table-header `tableColumnsExact` assertions, ID count-bound schema and
|
|
8
9
|
runtime evaluator contract, plus `tableColumnCoverage` schema, compiled-plan,
|
|
9
10
|
and runtime evaluator contract, `frontmatterShape` schema, compiled-plan, and
|
|
10
11
|
runtime evaluator contract, `textFormat` schema, compiled-plan, and runtime
|
|
@@ -237,7 +238,7 @@ emit `profile.config.invalidShape`.
|
|
|
237
238
|
Rule-level `when` is allowed only on v2 rules. Branch-level `when` remains
|
|
238
239
|
unsupported. V1 profiles preserve the original flat rule authoring contract;
|
|
239
240
|
grouped `anyOf` / `allOf`, ID count bounds, `tableColumnCoverage`,
|
|
240
|
-
`tableColumnsExact`, `frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
|
|
241
|
+
`tableColumnsExact`, `tableRowsComplete`, `frontmatterShape`, `textFormat`, and rule-level `when` are v2 additions.
|
|
241
242
|
|
|
242
243
|
Profile values must be JSON-safe data properties after YAML materialization.
|
|
243
244
|
Functions, accessors, proxies, cyclic structures, sparse arrays, `undefined`
|
|
@@ -308,6 +309,7 @@ interface DeclarativeAssertion {
|
|
|
308
309
|
tableColumnsRequired?: {
|
|
309
310
|
columns: readonly string[];
|
|
310
311
|
};
|
|
312
|
+
tableRowsComplete?: true; // v2 only
|
|
311
313
|
tableColumnsExact?: {
|
|
312
314
|
columns: readonly string[];
|
|
313
315
|
};
|
|
@@ -323,7 +325,9 @@ interface DeclarativeAssertion {
|
|
|
323
325
|
mustAppearIn: readonly string[];
|
|
324
326
|
};
|
|
325
327
|
tableColumnCoverage?: {
|
|
328
|
+
allowEmptySource?: boolean;
|
|
326
329
|
source: {
|
|
330
|
+
rowWhere?: { column: string; equals?: string; includes?: string };
|
|
327
331
|
section: string;
|
|
328
332
|
column: string;
|
|
329
333
|
prefix?: string;
|
|
@@ -349,6 +353,7 @@ interface DeclarativeAssertion {
|
|
|
349
353
|
}[];
|
|
350
354
|
};
|
|
351
355
|
text?: {
|
|
356
|
+
nonBlank?: true; // v2 only
|
|
352
357
|
contains?: string;
|
|
353
358
|
excludes?: readonly string[];
|
|
354
359
|
};
|
|
@@ -389,6 +394,7 @@ Selector/assertion compatibility is part of the public contract:
|
|
|
389
394
|
| `sectionsRequired` | `document` |
|
|
390
395
|
| `tableColumnsRequired` | `table` |
|
|
391
396
|
| `tableColumnsExact` | `table` |
|
|
397
|
+
| `tableRowsComplete` | `table` |
|
|
392
398
|
| `ids` | all supported selector targets |
|
|
393
399
|
| `references` | `document` |
|
|
394
400
|
| `tableColumnCoverage` | `document` |
|
|
@@ -430,6 +436,32 @@ the normal selected-table source evidence is retained. This assertion does not
|
|
|
430
436
|
change the ordered-subsequence behavior of `tableColumnsRequired` or table
|
|
431
437
|
selector `header` / `tableHeader` matching.
|
|
432
438
|
|
|
439
|
+
For v2 profiles, `tableRowsComplete: true` requires every body row of each
|
|
440
|
+
selected table to have exactly that table's normalized header column positions.
|
|
441
|
+
Only `true` is admitted; `false`, objects, strings and other payloads emit
|
|
442
|
+
`profile.config.invalidShape`. Only `table` selectors are compatible. V1 rejects
|
|
443
|
+
the assertion through the existing unsupported-assertion/key behavior.
|
|
444
|
+
|
|
445
|
+
The assertion compares normalized cell coordinates, not text or Markdown pipe
|
|
446
|
+
characters. Missing and excess cells both fail. An explicitly present empty cell
|
|
447
|
+
passes shape; compose `text.nonBlank` on required `tableCell` columns to reject
|
|
448
|
+
blank content. A header-only table passes shape; compose a `tableRow`
|
|
449
|
+
`selectionCount` minimum to require body rows. No matched tables fails with
|
|
450
|
+
`profile.validation.emptySelection`. Existing table section/header selectors
|
|
451
|
+
scope the check, including tables in descendant sections; unselected malformed
|
|
452
|
+
tables do not fail it. Header matching and normalization are unchanged.
|
|
453
|
+
|
|
454
|
+
Each malformed row emits one `profile.validation.assertionFailed` at the rule's
|
|
455
|
+
severity. Its deterministic message names the table target ID, body row index
|
|
456
|
+
(header is row 0; first body row is 1), and expected/actual zero-based column
|
|
457
|
+
positions. For example: `Selected table "node:0:table" body row 1 must have column
|
|
458
|
+
positions [0,1]; found [0].` Source evidence is the first available body-cell
|
|
459
|
+
range in column order, not a fabricated missing-cell or full-row range. When no
|
|
460
|
+
body-cell range is available, the diagnostic omits `sourceRange`, even if a table
|
|
461
|
+
range is known. Normal diagnostic ordering applies; without ranges, selected-table
|
|
462
|
+
order and numeric row order are retained. This check needs no original source
|
|
463
|
+
text and does not alter GFM parsing or strengthen existing profiles.
|
|
464
|
+
|
|
433
465
|
`sectionsRequired.order` defaults to `none`. `strict` checks that configured
|
|
434
466
|
headings appear as an ordered subsequence in the normalized section tree
|
|
435
467
|
flattened in source order.
|
|
@@ -461,6 +493,22 @@ target section do not satisfy coverage. Missing target sections, missing target
|
|
|
461
493
|
columns, and missing target-column IDs emit deterministic validation diagnostics
|
|
462
494
|
source-grounded to the source ID when source evidence is available.
|
|
463
495
|
|
|
496
|
+
The opt-in `source.rowWhere` filter uses the same closed sibling-cell predicate
|
|
497
|
+
as the `tableCell` selector: a non-empty `column` plus `equals`, `includes`, or
|
|
498
|
+
both. Both predicates must match when both are present. Matching uses normalized,
|
|
499
|
+
case-sensitive cell text before ID extraction; `source.caseSensitive` affects ID
|
|
500
|
+
comparison only. Rows marked optional can therefore be excluded without encoding
|
|
501
|
+
consumer vocabulary in the engine. A missing predicate column is unresolved,
|
|
502
|
+
not a resolved empty source.
|
|
503
|
+
|
|
504
|
+
`allowEmptySource` is an optional boolean. Absent or `false` preserves the existing
|
|
505
|
+
`profile.validation.emptySelection` failure when no source IDs are extracted.
|
|
506
|
+
`true` permits zero extracted IDs only when the source section and column resolve
|
|
507
|
+
(including the predicate column when configured). With no source IDs there are no
|
|
508
|
+
target obligations; target resolution is not evaluated. A missing source structure
|
|
509
|
+
still fails. Pair this policy with consumer shape rules when tables or rows are
|
|
510
|
+
mandatory. This option also permits prose sentinels in reverse declared-ID checks.
|
|
511
|
+
|
|
464
512
|
For v2 profiles, `frontmatterShape` is admitted as a flat-rule schema and
|
|
465
513
|
internal compiled-plan assertion. It is compatible only with a `document`
|
|
466
514
|
selector because frontmatter is document metadata. `presence` is optional and
|
|
@@ -505,7 +553,18 @@ is combined with `equals` or `nonBlank`, a non-string value emits only
|
|
|
505
553
|
string-predicate diagnostic. Diagnostic messages do not include frontmatter
|
|
506
554
|
values.
|
|
507
555
|
|
|
508
|
-
`text` must include `contains
|
|
556
|
+
`text` must include `contains`, a non-empty `excludes` array, or (v2 only)
|
|
557
|
+
`nonBlank: true`. `nonBlank` cannot be `false`. It requires at least one character
|
|
558
|
+
after JavaScript `trim()` on normalized node text excluding raw `html`, `definition`,
|
|
559
|
+
and `yaml` nodes. Comments, empty HTML tags, and decoded whitespace entities cannot
|
|
560
|
+
satisfy it. Markdown emphasis, link labels, literal code, and escaped angle brackets
|
|
561
|
+
retain their text. Each selected target is checked independently; an empty selector
|
|
562
|
+
still fails. Section selection includes its heading and direct body, as with ordinary
|
|
563
|
+
section text. Original source ranges are optional; the normalized nodes provide the
|
|
564
|
+
content. This is a structural textual-content check, not CSS visibility evaluation
|
|
565
|
+
or browser rendering. It does not evaluate external evidence, freshness, truth,
|
|
566
|
+
or implementation acceptance. Existing `contains`, `excludes`, and `textLength`
|
|
567
|
+
semantics are unchanged, even when combined with `nonBlank`.
|
|
509
568
|
`textOccurrenceCount.count` is a finite number and counts non-overlapping
|
|
510
569
|
literal occurrences per selected target.
|
|
511
570
|
`textLength` must include `min`, `max`, or both. Bounds are non-negative
|
|
@@ -538,7 +597,7 @@ parsing, or implement date ordering.
|
|
|
538
597
|
|
|
539
598
|
Empty selector results produce `profile.validation.emptySelection` for exists,
|
|
540
599
|
table, ID, reference, text, occurrence, text-length, text-format, and
|
|
541
|
-
tableColumnsExact assertions.
|
|
600
|
+
tableColumnsExact and tableRowsComplete assertions.
|
|
542
601
|
`selectionCount` instead evaluates the empty selection as zero. Document-scoped
|
|
543
602
|
required-section, required-frontmatter, and frontmatter-shape assertions
|
|
544
603
|
evaluate against the document.
|
|
@@ -592,6 +651,7 @@ an unproven pass.
|
|
|
592
651
|
| `profile.validation.tableColumnCoverageIdMissing` | Rule severity | A source ID is absent from the configured target table column. |
|
|
593
652
|
| `profile.validation.tableColumnCoverageTargetColumnMissing` | Rule severity | The configured target table column cannot be resolved. |
|
|
594
653
|
| `profile.validation.tableColumnCoverageTargetSectionMissing` | Rule severity | The configured target section cannot be resolved. |
|
|
654
|
+
| `profile.validation.textBlank` | Rule severity | A target configured with `text.nonBlank` lacks non-whitespace text outside raw HTML. |
|
|
595
655
|
| `profile.validation.textExcluded` | Rule severity | A selected target contains forbidden literal text. |
|
|
596
656
|
| `profile.validation.textMissing` | Rule severity | A selected target lacks required literal text from a `text.contains` assertion. |
|
|
597
657
|
| `profile.validation.assertionUnsupported` | `error` | A compiled assertion or compiled assertion feature has no evaluator implementation; this is an internal safety diagnostic. |
|
|
@@ -738,7 +798,7 @@ that use `sourceLength`.
|
|
|
738
798
|
The declarative validation CLI command is:
|
|
739
799
|
|
|
740
800
|
```sh
|
|
741
|
-
markdown-engine validate --file <markdown-file> --profile <profile-file> [--format json]
|
|
801
|
+
markdown-engine validate --file <markdown-file> --profile <profile-file> [--format json] [--output full|summary] [--report-file <new-file>]
|
|
742
802
|
```
|
|
743
803
|
|
|
744
804
|
`--format json` is the default and only supported validation output format.
|
|
@@ -757,7 +817,7 @@ union; there is no extra CLI discriminator beyond
|
|
|
757
817
|
|
|
758
818
|
## CLI JSON Union
|
|
759
819
|
|
|
760
|
-
The CLI JSON output is:
|
|
820
|
+
The default (`--output full`) CLI JSON output is:
|
|
761
821
|
|
|
762
822
|
```ts
|
|
763
823
|
type DeclarativeValidationCliJsonResult =
|
|
@@ -785,13 +845,70 @@ profiles, that same validation-result JSON can include `evaluatedRuleCount`,
|
|
|
785
845
|
`skippedRuleCount`, `status: "skipped"`, nested `when` diagnostics, and
|
|
786
846
|
`evaluation.kind: "skipped"`.
|
|
787
847
|
|
|
848
|
+
## Compact Validation Output
|
|
849
|
+
|
|
850
|
+
`--output full` (the default) preserves the existing full JSON contract and
|
|
851
|
+
serialization. `--output summary --report-file <new-file>` selects a separate
|
|
852
|
+
CLI-only `schemaVersion: "markdown-engine.validation-summary.v1"` representation.
|
|
853
|
+
Both modes still use `--format json`. Each new selector accepts spaced or
|
|
854
|
+
assignment syntax and may occur only once. Missing/blank paths, unsupported
|
|
855
|
+
output modes and summary mode without a report path are usage errors (exit 2).
|
|
856
|
+
These options do not apply to the normalization command.
|
|
857
|
+
|
|
858
|
+
Summary fields:
|
|
859
|
+
|
|
860
|
+
| Field | Meaning |
|
|
861
|
+
| --- | --- |
|
|
862
|
+
| `schemaVersion` | Exact `markdown-engine.validation-summary.v1` discriminator. |
|
|
863
|
+
| `valid`, `exitCode` | Original validation verdict and its 0/1 CLI status. |
|
|
864
|
+
| `stage` | `profile` for rejected profiles; otherwise `validation`, including normalization failures. |
|
|
865
|
+
| `engineVersion`, `runtimeVersion` | Producing package and Node versions, including profile-stage failures. |
|
|
866
|
+
| `profile` | Existing admitted-profile metadata, when available, including V2 evaluated/skipped counts. |
|
|
867
|
+
| `evidence` | Existing input/profile hashes, engine/runtime versions and optional sourceLength; no duplicated rule results or diagnostics. Omitted when full-result evidence is absent. |
|
|
868
|
+
| `diagnosticCounts` | Total and error/warning/info counts over all top-level diagnostics. |
|
|
869
|
+
| `diagnostics` | Up to ten top-level diagnostics, ordered error, warning, info; existing order is preserved within a severity. |
|
|
870
|
+
| `diagnosticsOmitted` | Number of top-level diagnostics not shown. |
|
|
871
|
+
| `diagnosticsTruncated` | Number of shown diagnostics with at least one shortened field. |
|
|
872
|
+
| `report` | Absolute `path`, UTF-8 `bytes`, and raw-file `sha256` of the complete retained result, including its final newline. |
|
|
873
|
+
|
|
874
|
+
Shown diagnostics preserve severity and available sourceRange. Messages are
|
|
875
|
+
limited to 512 UTF-16 code units; code and ruleId to 128 each, including a final
|
|
876
|
+
ellipsis when shortened. Each diagnostic has `truncatedFields`, naming exactly
|
|
877
|
+
which fields were shortened (empty when none). Full unmodified fields remain
|
|
878
|
+
in the report. Counts exclude nested skipped-applicability and branch details,
|
|
879
|
+
which remain in the complete result. A passing verdict can contain warnings;
|
|
880
|
+
summary presentation neither discards those warnings nor changes the exit code.
|
|
881
|
+
|
|
882
|
+
The full report is byte-identical to default stdout for the same invocation's
|
|
883
|
+
validation result: stable pretty JSON followed by one newline. Its raw-file
|
|
884
|
+
SHA-256 is distinct from normalized `evidence.inputHash` and `profileHash`.
|
|
885
|
+
Summary stdout is stable compact JSON followed by one newline, with no rule
|
|
886
|
+
result arrays. These presentation fields are not additions to the engine's
|
|
887
|
+
public API result types or evidence hashes.
|
|
888
|
+
|
|
889
|
+
`--report-file` is also permitted with full output. Relative destinations resolve
|
|
890
|
+
against the invocation's current working directory. The parent directory must
|
|
891
|
+
exist; the destination must not exist. Report publication stages a complete file
|
|
892
|
+
in that directory, then publishes it with an exclusive hard link and removes
|
|
893
|
+
the temporary file. Existing files, directories, symlinks and hard links are
|
|
894
|
+
never replaced. Filesystems that do not support this operation report an I/O
|
|
895
|
+
error. Report bytes are written before any validation JSON is emitted to stdout.
|
|
896
|
+
|
|
897
|
+
A report-publication failure overrides a 0/1 validation status with exit 2,
|
|
898
|
+
emits a stderr error, and emits no validation JSON to stdout. Usage or input-read
|
|
899
|
+
failures continue to use the existing stderr contract and do not create a report.
|
|
900
|
+
Profile-stage validation failures do create a full report when requested, retain
|
|
901
|
+
exit 1, and omit unavailable evidence identities from the summary. The report
|
|
902
|
+
writer is a narrow CLI filesystem-output boundary; the validator/API do not gain
|
|
903
|
+
persistence or filesystem-write behavior.
|
|
904
|
+
|
|
788
905
|
## Exit Codes
|
|
789
906
|
|
|
790
907
|
| Exit code | Meaning |
|
|
791
908
|
| --- | --- |
|
|
792
909
|
| `0` | Validation completed with no top-level error-severity diagnostics. |
|
|
793
910
|
| `1` | Profile/config/compile, Markdown normalization, document-version mismatch, or top-level validation diagnostics include at least one error. |
|
|
794
|
-
| `2` | CLI usage, unsupported format, unknown argument, missing argument value, repeated singleton flag, unsupported `--document-version`, or local file read error. |
|
|
911
|
+
| `2` | CLI usage, unsupported format, unknown argument, missing argument value, repeated singleton flag, unsupported `--document-version`, or local file read/report-publication error. |
|
|
795
912
|
|
|
796
913
|
## Compatibility And Migration
|
|
797
914
|
|
|
@@ -896,6 +1013,36 @@ Migration notes:
|
|
|
896
1013
|
|
|
897
1014
|
## Examples
|
|
898
1015
|
|
|
1016
|
+
### Opt-in table body completeness
|
|
1017
|
+
|
|
1018
|
+
```yaml
|
|
1019
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
1020
|
+
documentVersion: 1.0.0
|
|
1021
|
+
rules:
|
|
1022
|
+
- id: sources.rows.complete
|
|
1023
|
+
select:
|
|
1024
|
+
target: table
|
|
1025
|
+
section: Sources and baseline
|
|
1026
|
+
assert:
|
|
1027
|
+
tableRowsComplete: true
|
|
1028
|
+
- id: sources.identity.nonblank
|
|
1029
|
+
select:
|
|
1030
|
+
target: tableCell
|
|
1031
|
+
section: Sources and baseline
|
|
1032
|
+
column: SHA-256 / immutable identity
|
|
1033
|
+
assert:
|
|
1034
|
+
text:
|
|
1035
|
+
nonBlank: true
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
The [runnable table-row example](../../fixtures/declarative-validation/examples/table-rows-complete/README.md)
|
|
1039
|
+
adapts delegation-planner's missing-dynamic-row-cell reproducer. It demonstrates
|
|
1040
|
+
legacy header-only validation passing the truncated row, opt-in shape validation
|
|
1041
|
+
failing at that row, and a repaired document passing directly through Engine.
|
|
1042
|
+
Consumer-owned section selection remains profile policy; no supplemental shape
|
|
1043
|
+
parser is needed. Older installed runtimes do not admit this new assertion;
|
|
1044
|
+
adoption requires a separately released compatible runtime.
|
|
1045
|
+
|
|
899
1046
|
Minimal profile:
|
|
900
1047
|
|
|
901
1048
|
```yaml
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Table body completeness
|
|
2
|
+
|
|
3
|
+
`tableRowsComplete: true` is an opt-in v2 assertion for `table` selectors. It
|
|
4
|
+
compares each body row's normalized column positions with its table's header.
|
|
5
|
+
Missing and excess cells fail. Header validation remains independent.
|
|
6
|
+
|
|
7
|
+
From the repository root, build and run the example:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm run build
|
|
11
|
+
node dist/cli/index.js validate --file fixtures/declarative-validation/examples/table-rows-complete/consumer-missing.md --profile fixtures/declarative-validation/examples/table-rows-complete/legacy.yaml --format json
|
|
12
|
+
node dist/cli/index.js validate --file fixtures/declarative-validation/examples/table-rows-complete/consumer-missing.md --profile fixtures/declarative-validation/examples/table-rows-complete/profile.yaml --format json
|
|
13
|
+
node dist/cli/index.js validate --file fixtures/declarative-validation/examples/table-rows-complete/consumer-repaired.md --profile fixtures/declarative-validation/examples/table-rows-complete/profile.yaml --format json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Expected exits are **0, 1, 0**. The second command reports rule `sources.columns`,
|
|
17
|
+
body row 2 at source line 20, expected `[0,1,2,3]`, actual `[0,1,2]`. Its range
|
|
18
|
+
locates the first available cell of the failing row. Table and row identities
|
|
19
|
+
remain in the message when cell ranges are unavailable; no location is invented.
|
|
20
|
+
|
|
21
|
+
`consumer-missing.md` is adapted from delegation-planner commit
|
|
22
|
+
`bc11ace3e50315313d428f3b681424fee910aef3`,
|
|
23
|
+
`skills/delegation-planner/validation/fixtures/missing-dynamic-row-cell.md`,
|
|
24
|
+
with an unrelated metadata row omitted.
|
|
25
|
+
`consumer-repaired.md` adds the missing execution-plan identity cell.
|
|
26
|
+
`legacy.yaml` checks the exact source-table header; `profile.yaml` adds only the
|
|
27
|
+
row-completeness assertion. This focused adaptation proves Engine's shape decision
|
|
28
|
+
without invoking or migrating the consumer helper. It does not claim to validate
|
|
29
|
+
all delegation policy, source integrity, reserved markers or semantic readiness.
|
|
30
|
+
|
|
31
|
+
`oracle.json` records independently authored valid/invalid decisions, row indexes,
|
|
32
|
+
source lines and actual column positions for the small Markdown fixtures. API and
|
|
33
|
+
real CLI tests use these expectations. Fixtures include escaped pipes, inline code,
|
|
34
|
+
Unicode, a blockquote table, excess and truncated rows, and explicit empty cells.
|
|
35
|
+
|
|
36
|
+
An explicit empty cell (`| x | |`) passes shape. Require nonblank values separately:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- id: values.required
|
|
40
|
+
select:
|
|
41
|
+
target: tableCell
|
|
42
|
+
column: B
|
|
43
|
+
assert:
|
|
44
|
+
text:
|
|
45
|
+
nonBlank: true
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A header-only table also passes shape. To require body rows, compose:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
- id: rows.required
|
|
52
|
+
select:
|
|
53
|
+
target: tableRow
|
|
54
|
+
assert:
|
|
55
|
+
selectionCount:
|
|
56
|
+
min: 1
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Apply the same section/header scope to composed rules when appropriate. Unmatched
|
|
60
|
+
table selection fails with `profile.validation.emptySelection`; unselected
|
|
61
|
+
malformed tables do not affect the rule. Section selection includes descendant
|
|
62
|
+
sections. The Engine parser and normalized document remain unchanged.
|
|
63
|
+
|
|
64
|
+
Older runtimes reject this assertion. These commands use the local build;
|
|
65
|
+
release, installed-runtime adoption and consumer-helper removal are separate work.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: DelegationPlan
|
|
3
|
+
artifact_version: "2.0"
|
|
4
|
+
revision: "1"
|
|
5
|
+
delegation_id: fixture
|
|
6
|
+
---
|
|
7
|
+
# Structural fixture only
|
|
8
|
+
|
|
9
|
+
## Control
|
|
10
|
+
|
|
11
|
+
| State | Granularity | Selected pilot extent | Max attempts per packet | Scheduling |
|
|
12
|
+
| --- | --- | --- | --- | --- |
|
|
13
|
+
| READY | action | DP-WP-1 | 2 | Serial |
|
|
14
|
+
|
|
15
|
+
## Sources and baseline
|
|
16
|
+
|
|
17
|
+
| Role | Full read path / reference | Revision | SHA-256 / immutable identity |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| Source contract | /fixture/task.md | 1 | synthetic-source-identity |
|
|
20
|
+
| Execution plan | /fixture/plan.md | 1 |
|
|
21
|
+
| Repository baseline and instructions | /fixture/repository | initial | synthetic-code-identity |
|
|
22
|
+
| Upstream validation | /fixture/validation.json | 1 | synthetic-validation-identity |
|
|
23
|
+
|
|
24
|
+
## Model roster
|
|
25
|
+
|
|
26
|
+
| Order | Model | Reasoning effort | Harness | Availability evidence |
|
|
27
|
+
| --- | --- | --- | --- | --- |
|
|
28
|
+
| 1 | gpt-6-luna | medium | manual | Unverified; check before dispatch |
|
|
29
|
+
|
|
30
|
+
## Ordered assignments
|
|
31
|
+
|
|
32
|
+
| Packet / coordinator | Ordered source steps | Source outcomes | Worker packet | Model / effort | Selection basis | Escalation / stop |
|
|
33
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
34
|
+
| DP-WP-1 | EP-ACT-1, EP-GATE-1 | TD-SC-1 | /fixture/DP-WP-1.md | gpt-6-luna / medium | experimental; bounded fixture | Stop after two total attempts |
|
|
35
|
+
|
|
36
|
+
## Readiness
|
|
37
|
+
|
|
38
|
+
| Field | Value |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Audit decision | PASS |
|
|
41
|
+
| Audit evidence | Synthetic mapping only; not dispatchable real work |
|
|
42
|
+
| Dispatch prerequisites | Obtain real sources, authorization and model availability |
|
|
43
|
+
| Blockers | None |
|
|
44
|
+
| Resume condition | Not applicable |
|
|
45
|
+
|
|
46
|
+
## Revision note
|
|
47
|
+
|
|
48
|
+
| Field | Value |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| Change and authority | Initial independent structural fixture; no implementation authority |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: DelegationPlan
|
|
3
|
+
artifact_version: "2.0"
|
|
4
|
+
revision: "1"
|
|
5
|
+
delegation_id: fixture
|
|
6
|
+
---
|
|
7
|
+
# Structural fixture only
|
|
8
|
+
|
|
9
|
+
## Control
|
|
10
|
+
|
|
11
|
+
| State | Granularity | Selected pilot extent | Max attempts per packet | Scheduling |
|
|
12
|
+
| --- | --- | --- | --- | --- |
|
|
13
|
+
| READY | action | DP-WP-1 | 2 | Serial |
|
|
14
|
+
|
|
15
|
+
## Sources and baseline
|
|
16
|
+
|
|
17
|
+
| Role | Full read path / reference | Revision | SHA-256 / immutable identity |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| Source contract | /fixture/task.md | 1 | synthetic-source-identity |
|
|
20
|
+
| Execution plan | /fixture/plan.md | 1 | synthetic-plan-identity |
|
|
21
|
+
| Repository baseline and instructions | /fixture/repository | initial | synthetic-code-identity |
|
|
22
|
+
| Upstream validation | /fixture/validation.json | 1 | synthetic-validation-identity |
|
|
23
|
+
|
|
24
|
+
## Model roster
|
|
25
|
+
|
|
26
|
+
| Order | Model | Reasoning effort | Harness | Availability evidence |
|
|
27
|
+
| --- | --- | --- | --- | --- |
|
|
28
|
+
| 1 | gpt-6-luna | medium | manual | Unverified; check before dispatch |
|
|
29
|
+
|
|
30
|
+
## Ordered assignments
|
|
31
|
+
|
|
32
|
+
| Packet / coordinator | Ordered source steps | Source outcomes | Worker packet | Model / effort | Selection basis | Escalation / stop |
|
|
33
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
34
|
+
| DP-WP-1 | EP-ACT-1, EP-GATE-1 | TD-SC-1 | /fixture/DP-WP-1.md | gpt-6-luna / medium | experimental; bounded fixture | Stop after two total attempts |
|
|
35
|
+
|
|
36
|
+
## Readiness
|
|
37
|
+
|
|
38
|
+
| Field | Value |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| Audit decision | PASS |
|
|
41
|
+
| Audit evidence | Synthetic mapping only; not dispatchable real work |
|
|
42
|
+
| Dispatch prerequisites | Obtain real sources, authorization and model availability |
|
|
43
|
+
| Blockers | None |
|
|
44
|
+
| Resume condition | Not applicable |
|
|
45
|
+
|
|
46
|
+
## Revision note
|
|
47
|
+
|
|
48
|
+
| Field | Value |
|
|
49
|
+
| --- | --- |
|
|
50
|
+
| Change and authority | Initial independent structural fixture; no implementation authority |
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
2
|
+
documentVersion: 1.0.0
|
|
3
|
+
rules:
|
|
4
|
+
- id: sources.columns
|
|
5
|
+
select:
|
|
6
|
+
target: table
|
|
7
|
+
section: Sources and baseline
|
|
8
|
+
assert:
|
|
9
|
+
tableColumnsExact:
|
|
10
|
+
columns: [Role, Full read path / reference, Revision, SHA-256 / immutable identity]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "complete",
|
|
4
|
+
"valid": true,
|
|
5
|
+
"failures": []
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"name": "missing",
|
|
9
|
+
"valid": false,
|
|
10
|
+
"failures": [
|
|
11
|
+
{
|
|
12
|
+
"row": 1,
|
|
13
|
+
"line": 3,
|
|
14
|
+
"actual": [
|
|
15
|
+
0
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "excess",
|
|
22
|
+
"valid": false,
|
|
23
|
+
"failures": [
|
|
24
|
+
{
|
|
25
|
+
"row": 1,
|
|
26
|
+
"line": 3,
|
|
27
|
+
"actual": [
|
|
28
|
+
0,
|
|
29
|
+
1,
|
|
30
|
+
2
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "explicit-empty",
|
|
37
|
+
"valid": true,
|
|
38
|
+
"failures": []
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "header-only",
|
|
42
|
+
"valid": true,
|
|
43
|
+
"failures": []
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "syntax",
|
|
47
|
+
"valid": true,
|
|
48
|
+
"failures": []
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": "multiple",
|
|
52
|
+
"valid": false,
|
|
53
|
+
"failures": [
|
|
54
|
+
{
|
|
55
|
+
"row": 1,
|
|
56
|
+
"line": 3,
|
|
57
|
+
"actual": [
|
|
58
|
+
0
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"row": 2,
|
|
63
|
+
"line": 4,
|
|
64
|
+
"actual": [
|
|
65
|
+
0,
|
|
66
|
+
1,
|
|
67
|
+
2
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"name": "blockquote",
|
|
74
|
+
"valid": false,
|
|
75
|
+
"failures": [
|
|
76
|
+
{
|
|
77
|
+
"row": 1,
|
|
78
|
+
"line": 3,
|
|
79
|
+
"actual": [
|
|
80
|
+
0
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
syntaxVersion: markdown-engine.validation@v2
|
|
2
|
+
documentVersion: 1.0.0
|
|
3
|
+
rules:
|
|
4
|
+
- id: sources.columns
|
|
5
|
+
select:
|
|
6
|
+
target: table
|
|
7
|
+
section: Sources and baseline
|
|
8
|
+
assert:
|
|
9
|
+
tableColumnsExact:
|
|
10
|
+
columns: [Role, Full read path / reference, Revision, SHA-256 / immutable identity]
|
|
11
|
+
tableRowsComplete: true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jasonbelmonti/markdown-engine",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.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-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/**\"",
|
|
74
|
+
"test:validation:assertions": "npm run build && vitest run tests/declarative-validation-assertions.test.ts tests/declarative-validation-filtered-coverage.test.ts tests/declarative-validation-visible-text.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-table-rows-complete.test.ts tests/declarative-validation-table-rows-complete-config.test.ts tests/declarative-validation-table-rows-complete-cli.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.
|
|
4
|
+
VERSION="3.8.0"
|
|
5
5
|
PACKAGE="@jasonbelmonti/markdown-engine"
|
|
6
|
-
EXPECTED_SHA256="
|
|
6
|
+
EXPECTED_SHA256="cecfb88ab9cb9a3030c13e41b5e56ac30b44436fb1da7bb890bea5f9bd215fce"
|
|
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}"
|