Commit Graph

3 Commits

Author SHA1 Message Date
fc1a6db1dc refactor(validation): fold the tagged validator onto one framework
The tagged-node validator carried its own copy of the validation machinery:
a Tagged.Rule cloning Validation's check type and a Tagged.Context cloning
its context, differing only in subject type. That is the per-layer
duplication the validation idiom exists to prevent (principle 10).

Generalise the type-directed framework over both the subject and the
document: SubjectContext, Check, and AnyCheck now take a Document type
parameter for the validated tree's root. The model layer validates with a
Model.Value document, the tagged layer with a Tagged.Node document, each
truthful rather than forcing one root type to stand in for the other.

The tagged layer keeps only what is genuinely its own: the Node document
type, its built-in check (now Tagged.BuiltinCheck, a Check<Node, Node>), and
its traversal. Tagged.Rule and the tagged Context are removed. The validator
surface is unchanged but validating now takes a Check<Node, Node>.

All 399 tests pass; the coverage gate reconciles to 62 public types.
2026-06-28 10:41:10 +02:00
2e45ecaffe feat(validation): add type-directed Check dispatch
Introduce the core of the validation-rules idiom: a Check<Subject> with a
positive description, a when predicate, and a Bool or multi-error check
that fires only on values of its Subject type wherever they occur. AnyCheck
erases the subject and dispatches by runtime type, guarding an optional or
a wrong type from matching. The validator gains a checks list, a
validating(_:) overload, and a traversal that offers each node the values
it may specialise on. BuiltinCheck.mappingKeysAreUnique is the
type-directed counterpart of the legacy rule.

The machinery has its own negative tests (wrong type, optional, false
predicate yield nothing; same type twice yields two errors) and the builtin
ships a failing and a near-miss test. The coverage gate catches the five
new public types, which are classified as validation machinery. The default
validator is unchanged, so this is additive; retiring the legacy Rule,
moving the default onto the check, the combinator algebra, the error
rendering, and de-duplicating the Tagged validator are the remaining steps.
2026-06-28 07:23:23 +02:00
85663682a2 build: enforce validation coverage over every public type
Adopt the validation-rules discipline that every public type is either
validated or excluded, enforced mechanically rather than by review. A new
gate enumerates the public surface from the compiler symbol graph and
fails the build, naming the type, if any public type is missing from the
coverage manifest or any manifest entry is no longer public.

scripts/validation-coverage.tsv classifies all 58 public types: the value
models with the mapping-key-uniqueness runtime invariant are validated,
and the namespaces, closed sum types, data-transfer values, error types,
and engine machinery are excluded with a one-line reason each. The gate
runs in check-all.sh and CI; a planted unclassified type was confirmed to
fail it.

This is the coverage-first phase of moving the validation layer to the
type-directed Validation idiom; the framework backing the validated
entries, the de-duplication of the Tagged validator machinery, and the
exhaustive tests follow.
2026-06-28 05:00:54 +02:00