Gate: invalid schemas accepted, ratchet 8 to 0 #4

Open
opened 2026-07-05 03:44:53 +00:00 by Mihaela · 2 comments
Mihaela commented 2026-07-05 03:44:53 +00:00 (Migrated from codeberg.org)

Status (2026-07-05)

Open. Metric tracker, not an implementation task.

The enforced baseline in Tests/XSTSSuiteTests.swift is knownSchemaInvalidAccepted = 8 on the settled (non-contested) XSTS 2006-11-06 archive. Every step of the burn-down from the first measurement is recorded as a per-rule delta in CHANGELOG.md and docs/xsts-deviations.md.

Per docs/release-hurdles.md, nearly all remaining cases route through one shared oracle's deliberate under-approximation: ParticleRestriction.violation (Sources/Schema/ParticleRestriction.swift), where a fix risks a false positive (the unrecoverable failure class), needs spec adjudication, or needs machinery not yet built.

Protocol: docs/schema-validity-burndown.md. Run the suite with swift test -c release --filter XSTS; per-case deviations land in the burn-down file the test writes. Ratchet the baseline down in the test with every closed case; a rising count is a regression.

## Status (2026-07-05) Open. Metric tracker, not an implementation task. The enforced baseline in `Tests/XSTSSuiteTests.swift` is `knownSchemaInvalidAccepted = 8` on the settled (non-contested) XSTS 2006-11-06 archive. Every step of the burn-down from the first measurement is recorded as a per-rule delta in `CHANGELOG.md` and `docs/xsts-deviations.md`. Per `docs/release-hurdles.md`, nearly all remaining cases route through one shared oracle's deliberate under-approximation: `ParticleRestriction.violation` (`Sources/Schema/ParticleRestriction.swift`), where a fix risks a false positive (the unrecoverable failure class), needs spec adjudication, or needs machinery not yet built. Protocol: `docs/schema-validity-burndown.md`. Run the suite with `swift test -c release --filter XSTS`; per-case deviations land in the burn-down file the test writes. Ratchet the baseline down in the test with every closed case; a rising count is a regression.
Mihaela commented 2026-07-05 06:29:09 +00:00 (Migrated from codeberg.org)

Ratchet: invalid-schemas-accepted 8 -> 6 (2026-07-05, 2e3d2e2)

First conformance ratchet since the tracker rebuild. Two of the eight remaining false-negatives are closed by fixing the layer that owned the lost information, not by special-casing the test IDs.

The fix

elemZ026 and particlesV020 both restrict a complex type by swapping an element reference for one of its substitution-group members. NameAndTypeOK (XSD 1.0 section 3.9.6, Elt:Elt clause 1) pairs the declared element declarations and requires the same {name} and {target namespace}; no clause grants substitution an allowance in particle restriction. We accepted the swap because the substitution-group expansion rewrites the compiled term (an abstract head is dropped, members join a synthetic choice), so the checker compared expanded names that happened to line up.

The root-cause fix: Particle.declaredReference now carries the pre-expansion identity of every <xs:element ref="..."/> particle through compilation, and ParticleRestriction.valid pairs the declared references before dispatching to the structural pairing table. Particles without a declared reference (local elements, inline groups) are unconstrained by this gate and fall through to their structural case as before.

Adjudication: particlesZ008

particlesZ008 is byte-for-byte the same construct as elemZ026 but ships a settled valid expectation, directly contradicting elemZ026's settled invalid. The two cannot both be honored by any checker. Adjudicated in favor of the spec text (clause 1 has no substitution allowance) and Z008 is reclassified as a spec-divergent schema test, with the full reasoning recorded in Tests/XSTSSuiteTests.swift.

Differential vs libxml2

libxml2 does not implement this edge (it accepts both cases), so elemZ026 is pinned as a stricter-than-libxml2 disagreement. Measured with the change stashed before recording: ten further baseline entries (addB070a, addB108, addB177, attO025, elemZ027_c, errC008, groupH021v, notatB005, notatE002, particlesEb040) reproduce without this change and are pre-existing drift from earlier main-line fixes, recorded separately. Baseline 231 -> 242, differential green.

State after this ratchet

Bucket Count
Valid schemas rejected 0
Invalid schemas accepted 6
Valid instances rejected 0
Invalid instances accepted 4

Remaining six, corrected against the suite's own failure report (the deviations-doc row this list first came from had gone stale: particlesIk011 was already closed at the 9 -> 8 step and schN12 was missing): particlesHa161, particlesIg004, particlesIk025, particlesIk027 (the disclosed cos-st-derived-ok 2.2.4 divergence), particlesM034, and schN12 (multi-document composition, untriaged). Per-case triage is next. Full macOS gate green (1845 tests), regression tests in Tests/XSDParticleSubstitutionRestrictionTests.swift.

## Ratchet: invalid-schemas-accepted 8 -> 6 (2026-07-05, `2e3d2e2`) First conformance ratchet since the tracker rebuild. Two of the eight remaining false-negatives are closed by fixing the layer that owned the lost information, not by special-casing the test IDs. ### The fix `elemZ026` and `particlesV020` both restrict a complex type by swapping an element reference for one of its substitution-group members. NameAndTypeOK (XSD 1.0 section 3.9.6, Elt:Elt clause 1) pairs the **declared** element declarations and requires the same {name} and {target namespace}; no clause grants substitution an allowance in particle restriction. We accepted the swap because the substitution-group expansion rewrites the compiled term (an abstract head is dropped, members join a synthetic choice), so the checker compared expanded names that happened to line up. The root-cause fix: `Particle.declaredReference` now carries the pre-expansion identity of every `<xs:element ref="..."/>` particle through compilation, and `ParticleRestriction.valid` pairs the declared references before dispatching to the structural pairing table. Particles without a declared reference (local elements, inline groups) are unconstrained by this gate and fall through to their structural case as before. ### Adjudication: particlesZ008 `particlesZ008` is byte-for-byte the same construct as `elemZ026` but ships a settled `valid` expectation, directly contradicting elemZ026's settled `invalid`. The two cannot both be honored by any checker. Adjudicated in favor of the spec text (clause 1 has no substitution allowance) and Z008 is reclassified as a spec-divergent schema test, with the full reasoning recorded in `Tests/XSTSSuiteTests.swift`. ### Differential vs libxml2 libxml2 does not implement this edge (it accepts both cases), so `elemZ026` is pinned as a stricter-than-libxml2 disagreement. Measured with the change stashed before recording: ten further baseline entries (`addB070a`, `addB108`, `addB177`, `attO025`, `elemZ027_c`, `errC008`, `groupH021v`, `notatB005`, `notatE002`, `particlesEb040`) reproduce without this change and are pre-existing drift from earlier main-line fixes, recorded separately. Baseline 231 -> 242, differential green. ### State after this ratchet | Bucket | Count | |---|---| | Valid schemas rejected | 0 | | **Invalid schemas accepted** | **6** | | Valid instances rejected | 0 | | Invalid instances accepted | 4 | Remaining six, corrected against the suite's own failure report (the deviations-doc row this list first came from had gone stale: `particlesIk011` was already closed at the 9 -> 8 step and `schN12` was missing): `particlesHa161`, `particlesIg004`, `particlesIk025`, `particlesIk027` (the disclosed cos-st-derived-ok 2.2.4 divergence), `particlesM034`, and `schN12` (multi-document composition, untriaged). Per-case triage is next. Full macOS gate green (1845 tests), regression tests in `Tests/XSDParticleSubstitutionRestrictionTests.swift`.
Mihaela commented 2026-07-05 06:59:39 +00:00 (Migrated from codeberg.org)

Ratchet: invalid-schemas-accepted 6 -> 3, and the bucket is now fully adjudicated (2026-07-05, 6a7bf78)

Per-case triage of the remaining six, each decided by the manifest annotation plus the spec text, not by pattern-matching. One structural insight closed two cases at once.

The fix: two occurrence regimes for RecurseAsIfGroup against a choice

The effective-total-range approximation in elementRestrictsChoice exists for exactly one reason: the literal section 3.9.6 pairing cannot express occurrence counts accumulated across a repeating choice's iterations, and over-rejects W3C-read-valid schemas (particlesZ001, "ambiguous" per its own manifest). But that ambiguity is impossible when the choice cannot repeat: at {max occurs} = 1 every accepted child comes from one traversal of one branch, so the literal rule governs with no tension. The checker now applies the literal pairing for non-repeating base choices and keeps the effective-range reading only where repetition makes it necessary.

  • particlesHa161 closed: a derived a{0,1} is not a pointwise NameAndTypeOK restriction of the required branch a{1,1} in choice{0,1}(a|b).
  • particlesM034 closed: a derived d1+ cannot map into the branch (d1*, d2+); the required d2 is unmapped and not emptiable, so the base language contains no pure-d1 string.
  • The settled-valid neighbors pin the boundary from the other side and all still pass: Ha160, M002, M003, M035 (M034's exact twin, d2+, valid because d1* is emptiable), and the Z001 shape. All are now regression tests in Tests/XSDParticleChoiceRestrictionTests.swift.

particlesIg004: excluded as spec-divergent

Base block="#all" restricted to block="substitution extension restriction", expected invalid. Section 3.3.2 maps both to the same {disallowed substitutions} set, so NameAndTypeOK clause 5's superset requirement holds; only a reading where #all is strictly larger than the complete explicit set reaches invalid, and no such value exists in the 1.0 blockSet grammar. Jointly unsatisfiable with the settled Ig001/002/003 (equal sets, valid) and Ig005 (#all covers the triple, valid). Full adjudication in Tests/XSTSSuiteExclusions.swift.

schN12: adjudicated, stays in the count as a policy deviation

Now triaged (it was the untriaged multi-document case). It is a chameleon redefine whose original no-namespace group compiles with unqualified locals while the redefining document is elementFormDefault="qualified", so src-redefine 6.2.2's restriction obligation fails NameAndTypeOK clause 1. The catch: 6.2.2 cannot be enforced uniformly, because mgO013's settled-VALID schema also violates 6.2.2 (all{e1(1,1)} redefining all{e1(0,0)}). The two settled expectations admit no single conformant policy; enforcing 6.2.2 trades this false negative for a valid-schema-rejected false positive, the hardest floor. PureXML keeps the FP-safe policy, documented in docs/xsts-deviations.md (the schA7 precedent: honest deviation, left in the count).

State

Bucket Count
Valid schemas rejected 0
Invalid schemas accepted 3
Valid instances rejected 0
Invalid instances accepted 4

The remaining three (Ik025, Ik027, schN12) are all documented divergences with recorded adjudications, no open bugs. In the strict sense this bucket has reached its principled floor: driving it below 3 means either adopting the literal cos-st-derived-ok member-chain reading (Ik025/027, a defensible spec argument either way) or enforcing 6.2.2 non-uniformly (not defensible). Gates: full XSTS release suite green at 0/3/0/4, libxml2 differential green at 244 pinned entries (Ha161 and M034 newly pinned as stricter-than-libxml2, both verified compiling under libxml2), full local gate green.

Next per the roadmap: #5, the instance-side tail (schA7.i, schU3/4/5.i, all schema-location/redefine-composition policy cases).

## Ratchet: invalid-schemas-accepted 6 -> 3, and the bucket is now fully adjudicated (2026-07-05, `6a7bf78`) Per-case triage of the remaining six, each decided by the manifest annotation plus the spec text, not by pattern-matching. One structural insight closed two cases at once. ### The fix: two occurrence regimes for RecurseAsIfGroup against a choice The effective-total-range approximation in `elementRestrictsChoice` exists for exactly one reason: the literal section 3.9.6 pairing cannot express occurrence counts accumulated across a **repeating** choice's iterations, and over-rejects W3C-read-valid schemas (particlesZ001, "ambiguous" per its own manifest). But that ambiguity is impossible when the choice cannot repeat: at `{max occurs} = 1` every accepted child comes from one traversal of one branch, so the literal rule governs with no tension. The checker now applies the literal pairing for non-repeating base choices and keeps the effective-range reading only where repetition makes it necessary. - **particlesHa161 closed**: a derived `a{0,1}` is not a pointwise NameAndTypeOK restriction of the required branch `a{1,1}` in `choice{0,1}(a|b)`. - **particlesM034 closed**: a derived `d1+` cannot map into the branch `(d1*, d2+)`; the required `d2` is unmapped and not emptiable, so the base language contains no pure-`d1` string. - The settled-valid neighbors pin the boundary from the other side and all still pass: Ha160, M002, M003, M035 (M034's exact twin, `d2+`, valid because `d1*` is emptiable), and the Z001 shape. All are now regression tests in `Tests/XSDParticleChoiceRestrictionTests.swift`. ### particlesIg004: excluded as spec-divergent Base `block="#all"` restricted to `block="substitution extension restriction"`, expected invalid. Section 3.3.2 maps both to the **same** {disallowed substitutions} set, so NameAndTypeOK clause 5's superset requirement holds; only a reading where `#all` is strictly larger than the complete explicit set reaches invalid, and no such value exists in the 1.0 blockSet grammar. Jointly unsatisfiable with the settled Ig001/002/003 (equal sets, valid) and Ig005 (`#all` covers the triple, valid). Full adjudication in `Tests/XSTSSuiteExclusions.swift`. ### schN12: adjudicated, stays in the count as a policy deviation Now triaged (it was the untriaged multi-document case). It is a chameleon redefine whose original no-namespace group compiles with unqualified locals while the redefining document is `elementFormDefault="qualified"`, so src-redefine 6.2.2's restriction obligation fails NameAndTypeOK clause 1. The catch: **6.2.2 cannot be enforced uniformly**, because mgO013's settled-VALID schema also violates 6.2.2 (`all{e1(1,1)}` redefining `all{e1(0,0)}`). The two settled expectations admit no single conformant policy; enforcing 6.2.2 trades this false negative for a valid-schema-rejected false positive, the hardest floor. PureXML keeps the FP-safe policy, documented in `docs/xsts-deviations.md` (the schA7 precedent: honest deviation, left in the count). ### State | Bucket | Count | |---|---| | Valid schemas rejected | 0 | | **Invalid schemas accepted** | **3** | | Valid instances rejected | 0 | | Invalid instances accepted | 4 | The remaining three (Ik025, Ik027, schN12) are all documented divergences with recorded adjudications, no open bugs. In the strict sense this bucket has reached its principled floor: driving it below 3 means either adopting the literal cos-st-derived-ok member-chain reading (Ik025/027, a defensible spec argument either way) or enforcing 6.2.2 non-uniformly (not defensible). Gates: full XSTS release suite green at 0/3/0/4, libxml2 differential green at 244 pinned entries (Ha161 and M034 newly pinned as stricter-than-libxml2, both verified compiling under libxml2), full local gate green. Next per the roadmap: #5, the instance-side tail (schA7.i, schU3/4/5.i, all schema-location/redefine-composition policy cases).
Sign in to join this conversation.
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: NodetteHQ/PureXML#4
No description provided.