The pre-push validation-coverage gate caught the previous commit's renames
(Check/Issue/SubjectContext/BuiltinCheck/AnyCheck) as unclassified new
types and stale old entries. Reclassify them under their new names and add
the new Traversable protocol.
Completes issue #11. Tagged.Scalar.timestampValue parses a !!timestamp
scalar under the YAML 1.1 grammar into the pure-Swift Tagged.Timestamp
(calendar components, timezone offset, secondsSinceEpoch via the
days-from-civil closed form; epoch test expectations measured against
Python's independent implementation). Tagged.Scalar.binaryValue base64
decodes a !!binary scalar into bytes, accepting unpadded and block
scalar wrapped content and rejecting corrupt input whole.
Yams hands back Foundation Date/Data; this package is mechanically
Foundation-free, so the bridges are caller-owned one-liners:
Date(timeIntervalSince1970:) and Data(_:). !!set and !!omap stay
skipped since Model.Mapping already preserves order and duplicates.
Closes#11
Tagged.Dumper, Tagged.ScalarStyle, and Tagged.CollectionStyle from the
issue #9 work, plus Streaming.Token.Kind and Streaming.Token.ScalarStyle
which predate it but were never entered and block every push.
The located-parse port added four public Model types (LocatedValue, LocatedPair,
SourceRange, SourcePosition) that the validation-coverage gate flagged as
unclassified, blocking the push. They are positional projections and pure
position data with no validation invariant of their own: LocatedValue and
LocatedPair wrap an already-validated Model.Value, and SourceRange and
SourcePosition are data-transfer. Classify all four as excluded with reasons.
The coverage manifest claimed Tagged.Mapping and Tagged.Node validate key
uniqueness, but the tagged validator's only default check was the built-in
tag check: the claim was false. Tagged.Mapping keeps every parsed pair, so a
duplicate key survives into the tree and is as invalid as in the plain model.
Add Tagged.BuiltinCheck.mappingKeysAreUnique, a Check<Mapping, Node> on the
one validation framework, to the tagged validator's defaults; the traversal
now offers each mapping to the checks. It reports each duplicate at its key
path, with failing, near-miss, and nested fixtures, and the two default
tagged checks are pinned by description.
Correct the Model.Value coverage reason: merges and aliases resolve at parse
time, so they are not a validation-phase invariant. Every validated manifest
entry now names an invariant a check actually enforces. 403 tests pass.
A Rule was exactly a Check<Model.Value, Model.Value>: it fired on every
node, switched on the value itself, and carried the root. It was therefore a
second copy of the one check machinery, the duplication the validation idiom
exists to prevent (principle 10), now also resolved for the tagged layer.
Remove Rule and its Context. The model validator holds only checks; its
default is the type-directed BuiltinCheck.mappingKeysAreUnique; validating
takes a Check. Custom whole-tree rules become Check<Model.Value, Model.Value>
with the former context.root now context.document.
Behaviour is byte-identical: the mapping-key builtin reports the same issues
at the same paths in the same DFS order, so the public validate/collect/blank
surface is unchanged. All 399 tests pass; the coverage gate reconciles to 60
public types.
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.
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.
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.