Commit Graph

566 Commits

Author SHA1 Message Date
b0479fd329 fix(schema): require a content derivation in simpleContent/complexContent (#145)
A complexContent or simpleContent's content model requires exactly one
restriction or extension. An empty <complexContent/> declares no derivation
and is now rejected as a structural error.

XSTS invalid-schemas-accepted 200 to 197, no other bucket moved.
2026-06-17 14:58:58 +02:00
947278bb56 fix(regex): reject a malformed \p{...} property name in a pattern facet (#145)
A \p{...}/\P{...} property name that can be no XSD charProp under any
repertoire (empty, the bare prefix Is, a non-block name carrying a non-letter,
or Is followed by a non-block character) is now a schema-validity error,
distinct from a well-formed but unrecognised category or block name, which
stays a lenient engine limitation.

XSTS invalid-schemas-accepted 203 to 200, no other bucket moved.
2026-06-17 14:53:32 +02:00
7e30526b2c fix(schema): reject attribute ref+form and an attribute named xmlns (#145)
An attribute ref is a use of a global declaration, so it may not also carry
form (src-attribute.3.2). And no attribute declaration may be named xmlns,
which is reserved for namespace declarations (no-xmlns).

XSTS invalid-schemas-accepted 207 to 203, no other bucket moved.
2026-06-17 14:39:31 +02:00
d402732d23 fix(schema): hold an attributeGroup reference to (annotation?) content (#145)
A referencing attributeGroup (one with a ref) names a definition elsewhere; its
own content model is (annotation?), so it may not also declare attributes,
nested attributeGroup references, or an anyAttribute. Only a defining
attributeGroup (with a name) carries those.

XSTS invalid-schemas-accepted 210 to 207, no other bucket moved.
2026-06-17 14:27:21 +02:00
709d23cc34 fix(schema): include notation names in component-name uniqueness (#145)
A notation declaration occupies its own symbol space, so two notations sharing
a name and target namespace are a duplicate just as two global types or
elements would be. Notations were omitted from the uniqueness check.

XSTS invalid-schemas-accepted 211 to 210, no other bucket moved.
2026-06-17 14:21:57 +02:00
616ec4876a fix(schema): hold xsd:notation to its (annotation?) content model (#145)
A notation declaration's content model is (annotation?): it may carry only an
optional annotation child. Any other element child, or any non-whitespace
character content, is now rejected.

XSTS invalid-schemas-accepted 214 to 211, no other bucket moved.
2026-06-17 14:17:27 +02:00
d2438a957a fix(schema): require a list or union to declare its member types (#145)
A list simple type must draw its item type from exactly one of an itemType
attribute or an inline simpleType child; neither, or both, is invalid. A union
must declare at least one member type through a memberTypes attribute or an
inline simpleType child; an empty union declares none (src-simple-type.2/.3).

XSTS invalid-schemas-accepted 215 to 214, no other bucket moved.
2026-06-17 14:11:26 +02:00
8420c7e2aa fix(schema): inherit finalDefault as a simple type's final (#145)
A simple type that declares no `final` of its own takes the schema's
`finalDefault` as its `{final}` property (XSD 1.0 Schemas section 3.14.2). The
list and union derivation checks read the `final` attribute directly but
ignored `finalDefault`, so `finalDefault="list"` or `"union"` did not block a
list item type or union member type and the schema was wrongly accepted.

The final map now falls back to `finalDefault` for a type without its own
`final`; an explicit `final=""` still overrides the default back to no
restriction, and a built-in item or member type stays unaffected (it is matched
only when the reference resolves to the schema's own namespace).

Caught two invalid schemas: XSTS invalid-schemas-accepted 217 -> 215, no other
bucket moved.
2026-06-17 13:59:19 +02:00
50c6fb9b1c fix(regex): reject a pattern facet whose regex ends mid-token (#145)
A `pattern` facet's value must be a valid XSD regular expression. Two
truncation cases were compiled with a non-rejecting error and the schema was
wrongly accepted:

- a trailing `\` with no character to escape (`a\`), and
- a `[` character class the expression ends before closing with `]`
  (`a[`, `[a[:xyz:`).

Both are unambiguously invalid, never an engine limitation on an otherwise
valid pattern, so they now raise the dedicated `RegexError.incompleteEscape`
and `RegexError.unterminatedClass` and `rejectsPattern` treats them as
schema-validity errors. A completed escape (`\[`, `\\`), a closed class, and a
class containing an escaped `]` or a subtraction stay valid.

Caught thirteen invalid schemas: XSTS invalid-schemas-accepted 230 -> 217, no
other bucket moved.
2026-06-17 13:53:41 +02:00
c80a8e06c4 fix(schema): reject an attribute reference whose fixed value contradicts its global (#145)
An attribute use that references a global attribute carrying a `fixed` value
constraint may not contradict it (au-props-correct.2): a `fixed` on the
reference whose value differs from the global's, or a `default` on a reference
to a fixed attribute, is invalid. A reference that omits its own value
constraint inherits the global's and stays valid.

The two `fixed` values are compared in the attribute type's value space, so a
value-equal pair written in different lexical forms (`01` for `1`) is not
flagged. The global is resolved by local name and only checked once its own
namespace matches the reference's resolved namespace, so a same-local-name
attribute imported from another namespace cannot surface as the wrong global
and contradict a valid reference.

Caught two invalid schemas: XSTS invalid-schemas-accepted 232 -> 230, no other
bucket moved.
2026-06-17 13:43:42 +02:00
a96e5ce3ac fix(schema): reject extending a non-empty all-group base with element content (#145)
Extending a complexType whose whole content is a non-empty xs:all group, via
complexContent extension that adds element content, nests that all in the
sequence joining base and extension content, which cos-all-limited forbids
(an all group must be the whole content model). Such schemas were accepted.

This is the symmetric companion to the existing rule (an all group in the
extension over a base with content); both now live in one pass. An empty-all
base contributes no content, and an attribute-only extension adds none, so
both stay valid.

XSTS invalid-schemas-accepted 233 to 232, no other bucket moved.
2026-06-17 13:23:26 +02:00
ac242a980b fix(schema): reject complexContent extension adding element content over a simpleContent base (#145)
A complexContent extension that adds element content (a non-empty model
group) produces an element-only/mixed content type, so its base must have
complex or empty content, not simpleContent (cos-ct-extends.1.4.2.2). Such
schemas were accepted.

The check requires the extension to add actual element content (an element or
wildcard, not an empty group), so an attribute-only complexContent extension
of a simpleContent base stays valid (the complexContent-around-simpleContent
idiom, e.g. particlesZ031).

XSTS invalid-schemas-accepted 235 to 233, no other bucket moved.
2026-06-17 13:02:37 +02:00
39649a1940 fix(schema): reject named-group compositor occurrence and XSD-namespace attributes (#145)
Two schema-for-schemas structural constraints were not enforced, so the
malformed schemas were accepted:

- The compositor of a top-level named xs:group (all/choice/sequence) may not
  carry minOccurs/maxOccurs; those belong on a group reference, not the
  definition.
- An attribute in the XML Schema namespace (e.g. xsd:type) is never valid on a
  schema element, since the vocabulary defines no namespaced attributes. A
  foreign attribute in any other namespace stays the author's own.

XSTS invalid-schemas-accepted 244 to 235, no other bucket moved. (A third
candidate, form on a top-level declaration, was already enforced by the
existing top-level attribute rule, so no new check was needed.)
2026-06-17 11:08:33 +02:00
b4c1285d8e fix(regex): reject malformed pattern facets as schema-invalid (#145)
A pattern facet with an unambiguously malformed regular expression was
compiled leniently and the schema accepted. Three such constructs are now
rejected: a reversed character-class range ([b-a]), an empty class ([]),
and a reversed bounded quantifier ({37,17}). The parser raises distinct
RegexError cases (reversedRange, emptyClass, reversedQuantifier) that the
schema-validity check treats as invalid.

Constructs the engine merely does not support (an untabulated \p{Is...}
block, the lenient {,m} form) stay non-rejecting, so a valid pattern is
never refused. XSTS invalid-schemas-accepted 262 to 244, no other bucket
moved; the 27 regex unit tests still pass.
2026-06-17 10:49:09 +02:00
fe8a6e8d6e fix(schema): resolve identity attribute-field types for value-space keys (#147)
An identity-constraint attribute field's type was resolved only among the
constraint host's descendant elements, so when the selector targets a global
element referenced from the host (e.g. unique over `.//uid` with field
`@val`), the field type was unknown and values compared lexically. Two keys
equal in value space but not lexically (3.0 and 3 for xsd:decimal) were then
treated as distinct and the duplicate was accepted.

The attribute type now resolves through the selector's target global element,
so values compare in their value space. XSTS invalid-instances-accepted
133 to 132 (idF010), no other bucket moved.
2026-06-17 10:34:19 +02:00
39a91c258e fix(schema): resolve identity-constraint XPath prefixes against the schema (#147)
Identity-constraint selector and field XPath prefixes were resolved against
the instance document's namespace bindings, so a constraint declared with
xmlns:v="..." and a selector like .//v:vehicle matched nothing when the
instance used a default namespace instead of the v prefix. The selector then
selected no nodes, no key tuples were collected, and duplicate-key and
keyref violations went undetected.

Prefixes now resolve against the namespace bindings in scope where the
constraint is declared in the schema (XSD Part 1, Identity-constraint
Definition), captured at parse time onto IdentityConstraint. A QName field
value still compares using the instance's bindings, so QName keys are
unaffected.

XSTS invalid-instances-accepted 143 to 133: the idc004/5/6 namespaced
key/keyref cluster and namespace-resolution cases (idZ010 and kin), no other
bucket moved.
2026-06-17 00:03:13 +02:00
9769f022dd fix(schema): enforce identity-constraint field cardinality (#147)
An identity-constraint field must evaluate to a node-set with at most one
member (cvc-identity-constraint.3). The validator built a value tuple per
target without checking how many nodes each field selected, so an instance
whose unique/key/keyref field matched several nodes was accepted.

fieldCardinalityError now rejects a target whose field selects more than one
node, in both the key/unique and keyref paths. XSTS invalid-instances-accepted
4 → fewer (147 to 143), no other bucket moved.
2026-06-16 23:41:30 +02:00
4df9eda52e feat(schema): compile import-chained documents as a union (#161)
Schema documents that form an import chain are now compiled as a single
union (Document.init(composing:)) rather than as separately compiled
documents merged after the fact. Merging froze each document's content
models with only the substitution-group members visible in its own import
closure, so a substitution member declared in an importing document never
reached the head's content model in the imported document.

This clears XSTS ctZ007, a cross-document substitution group, taking
valid-instances-rejected (#146) from 4 to 3 with no other bucket moving
(now 1/262/3/147). The union pools each document's container closure into
one context, deduplicating a document supplied directly and also reached
through another's import by target namespace and component-name set.

The XSTS runner uses the union only for import chains, detected by matching
import/include/redefine schemaLocation basenames against the listed
documents; independent roots stay on the lenient per-document merge, which a
union would wrongly trip with cross-document checks.

The remaining three valid-instance rejections are the Ethiopic-digit \d
cases (reS17, reS38, reZ004v), which contradict \d = \p{Nd}.
2026-06-16 23:16:26 +02:00
be8a39394d fix(schema): advance XSTS valid-instances-rejected to 4 (#146)
Consolidates the in-progress #146 work; valid-instances-rejected reaches 4
on the 2006-11-06 XSTS archive (buckets now 1/262/4/147).

Regex: match Unicode scalars rather than grapheme clusters; the empty
pattern matches only the empty string; pin the category repertoire to
Unicode 3.2, the version the corpus is consistent at, so code points added
in 3.2 (Tagalog, Cyrillic Komi) are word characters while later additions
(the Tamil digit zero) stay unassigned and match \D. New XSDCategory and a
3.2 assignment table derived from DerivedAge, replacing the old Unicode31
files.

Content model: assess each child against the particle the content automaton
matched, not a by-name lookup (#180). Two same-named particles keep their
own fixed values, and an element matching a skip wildcard is no longer
validated against its global declaration. Selection is made reproducible
when an ambiguous schema slips past the UPA check.

XSTS runner: compile and merge every schemaDocument in a schemaTest, not
just the first; this also surfaced one previously-masked invalid-instance
acceptance (recorded in the baselines).

Also folds in attribute-group anyAttribute inheritance, extension wildcard
union, namespace-qualified type tables for multi-document
xsi:schemaLocation, per-container elementFormDefault, strict and lax
attribute wildcard validation, built-in type resolution, and xs:redefine
simpleType overrides.
2026-06-16 22:22:05 +02:00
489275c6b8 feat(schema): locate compile diagnostics with validation coding paths (#169)
Schema compile findings now carry ValidationError coding paths that resolve to source spans on a ranged tree, enabling IDE underlines for id, structure, include, and facet violations.
2026-06-16 10:26:11 +02:00
229d7df3d0 feat(validation,schema): composable validation framework and cross-document composition
Ship the OpenAPIKit-style validation layer (builtin rules, warnings channel,
Validator tiers, coverage gates) and wire XSD compile checks through it. Advance
#161 with chameleon include validation, multi-container reference resolution,
and include targetNamespace composition rules so merged schema sets compile
correctly when a schemaLoader is provided.
2026-06-16 09:57:43 +02:00
5f6170a57a docs(roadmap): add living roadmap index and README focus link
Document the restructured GitHub issue hierarchy (epic #167, gates,
milestones M1-M5, task labels) and point README Status at the current
production-readiness focus and v0.2.0 XSTS baselines.
2026-06-16 01:30:37 +02:00
baa90cfcaa chore(release): prepare 0.2.0
Cut the second pre-release: schema-validity burn-down to XSTS baselines
1/266/171/155, component child-content shape checks, and synced conformance
docs. CHANGELOG updated for v0.2.0.
2026-06-16 01:27:10 +02:00
55daca46be fix(schema): implement facet applicability and validation constraints 2026-06-16 00:02:04 +02:00
5cdcde9055 fix(schema): enforce element order and import constraints 2026-06-15 23:29:47 +02:00
434bfc85ea fix(schema): validate duplicate names across separate files with identical content 2026-06-15 22:57:00 +02:00
926ff99b5a fix(schema): validate xml:lang and xml:space on documentation elements 2026-06-15 22:36:51 +02:00
20b97d1d62 fix(schema): validate notation declarations and enumeration restrictions 2026-06-15 22:27:04 +02:00
4a7e23186d fix(schema): check component name and identity constraint uniqueness across all containers 2026-06-15 22:08:14 +02:00
d0e0b96de9 fix(schema): support inline element types in substitution group exclusions 2026-06-15 21:40:01 +02:00
ec4b2aa80c fix(schema): enforce substitution group exclusions based on final 2026-06-15 20:51:43 +02:00
5a2e2f3a40 fix(schema): enforce targetNamespace matching on local references 2026-06-15 20:08:39 +02:00
614209e9c1 fix(schema): enforce variety constraints on list itemType and union memberTypes 2026-06-15 20:01:51 +02:00
0496c6948a fix(schema): implement namespace-aware reference resolution checks 2026-06-15 19:44:32 +02:00
ffff13da57 fix(schema): enforce simpleType restriction structure and QName prefix bindings 2026-06-15 19:22:10 +02:00
1dcff00874 fix(schema): reject local-use attributes on top-level group and attributeGroup definitions 2026-06-15 19:03:38 +02:00
9004efe400 fix(schema): inherit and prohibit attributes under complexContent restriction 2026-06-15 18:52:23 +02:00
80064be6b8 feat(schema): validate identity constraint keyref field arity mismatch 2026-06-15 18:23:45 +02:00
31fe3e7c22 feat(schema): enforce particle element type restriction and bypass prohibited attributes 2026-06-15 18:10:21 +02:00
b6575150e8 feat(schema): enforce attribute restriction type derivation (derivation-ok-restriction.1)
Require that any restricted attribute has a type validly derived by restriction from its base counterpart. Validates atomic built-in derivation, list item type restrictions, and union member/subset relationships recursively. Updates baseline to 362 accepted invalid schemas.
2026-06-15 17:46:15 +02:00
a7c007e969 chore(schema): update baseline count for schemaInvalidAccepted to 367
Update invalid schemas accepted baseline to 367 following correction of xs:anySimpleType restriction rule.
2026-06-15 17:09:16 +02:00
ef80934ff8 feat(schema): enforce top level names, restrict xs:anySimpleType, and validate simpleType facets
Reject top level declarations that omit a name attribute. Disallow derivation by restriction of xs:anySimpleType inside simpleType definitions. Require restriction facets length, minLength, maxLength, totalDigits, and fractionDigits to restrict or equal the base type facets. Updates the conformance baseline to 383 accepted invalid schemas.
2026-06-15 17:06:09 +02:00
f86a7c9ade feat(schema): enforce attribute fixed value constraint under restriction 2026-06-15 16:24:32 +02:00
b102503d29 docs(agents): refresh onboarding with value-space comparison lesson and current baseline 2026-06-15 16:11:13 +02:00
b80e31d562 feat(schema): a restriction may not relax a required base attribute (cos-ct-derived-ok)
In a complexContent restriction, an attribute the restriction redeclares that the
base type marks use="required" may not be made optional or prohibited. Only
attributes explicitly redeclared that also exist in the base are checked; an
omitted attribute is inherited unchanged. The base reference must resolve to the
schema's own target namespace, and the check stands down for a schema that
composes other documents.

The matching fixed-value clause is deliberately not enforced: it requires
comparing the two fixed values in the attribute type's value space, not lexically
(a list-typed fixed value differing only in whitespace is the same value), so a
string comparison wrongly rejects valid schemas. That clause is left for a later
change (a disclosed under-rejection).

XSTS: invalid schemas accepted 394 to 390, no other bucket raised.
2026-06-15 16:09:02 +02:00
c4485ad6b5 docs(agents): add contributor onboarding guide and editor rules file 2026-06-15 15:50:10 +02:00
ca0db3104f docs(changelog): cut 0.1.0 release section 2026-06-15 15:44:03 +02:00
282ceb57fc feat(schema): a nested simpleType or attributeGroup may not be named (localSimpleType)
A simpleType nested inside a restriction/list/union/element/attribute is the
anonymous localSimpleType (no name), and a nested attributeGroup is always a
reference; named simpleType and attributeGroup definitions are top-level only (a
direct child of schema or redefine). A nested definition declaring a name was
accepted; it is now rejected. A nested anonymous simpleType, an attributeGroup
reference, and a top-level named definition are unaffected.

XSTS: invalid schemas accepted 403 to 394, no other bucket raised.
2026-06-15 15:41:48 +02:00
c4679acfd8 feat(schema): reject an all group extending a base with content (cos-all-limited.1.2)
An xs:all group in a complexContent extension of a base that already has element
content was accepted. The effective content model of such an extension is
sequence(base content, extension content), which nests the all and violates the
rule that an all group must be the whole content model. The base type is read
from the compiled model: rejected only when the base resolves, in the schema's
own target namespace, to a complex type with element-only or mixed content. An
explicitly-empty base (an empty model group with mixed=false) has effectively
empty content and may still be extended by an all (the all becomes the whole
content); a simple-content, empty, foreign, or unresolved base is left alone.
Checked for a self-contained schema.

XSTS: invalid schemas accepted 411 to 403, no other bucket raised.
2026-06-15 15:31:01 +02:00
ca1a7bdf3f feat(schema): enforce simpleType final for list and union derivation (st-props-correct)
A simple type whose final contains list (or #all) may not be a list itemType,
and one whose final contains union (or #all) may not be a union memberType.
finalRespected already covered restriction and extension; the list and union
directions were unchecked because the derivation-method set does not model
final="list"/"union", so the final attribute is read directly. The itemType /
memberTypes reference is matched only when it resolves to the schema's own target
namespace, so a list or union of a built-in (whose final is empty) is not
confused with a user type of the same local name. Checked for a self-contained
schema.

XSTS: invalid schemas accepted 413 to 411, no other bucket raised.
2026-06-15 15:12:11 +02:00