feat(common): add IConfiguration.VendorExtensions for XSD extension - #223
Draft
ottobolyos wants to merge 12 commits into
Draft
feat(common): add IConfiguration.VendorExtensions for XSD extension#223ottobolyos wants to merge 12 commits into
ottobolyos wants to merge 12 commits into
Conversation
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 19, 2026 12:12
46bcdc8 to
bac6c79
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
…VendorExtensions Lands the coverage-FLOOR (§1.0d-trigies-novodecies) tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed Claude-Session: https://claude.ai/code/session_0162RfaA55VT8NX6QfU7RUVo
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
…cile-cascade 20260819T134500Z)
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
…VendorExtensions Lands the coverage-FLOOR (§1.0d-trigies-novodecies) tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed Claude-Session: https://claude.ai/code/session_0162RfaA55VT8NX6QfU7RUVo
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 19, 2026 22:05
811e6d1 to
611f482
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…VendorExtensions Lands the coverage-FLOOR (§1.0d-trigies-novodecies) tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed Claude-Session: https://claude.ai/code/session_0162RfaA55VT8NX6QfU7RUVo
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 21, 2026 06:18
611f482 to
afa0b70
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…VendorExtensions Lands the coverage-FLOOR (§1.0d-trigies-novodecies) tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed Claude-Session: https://claude.ai/code/session_0162RfaA55VT8NX6QfU7RUVo
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 21, 2026 08:33
afa0b70 to
e173d94
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 21, 2026 14:18
e173d94 to
486c7b7
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 21, 2026 16:06
486c7b7 to
c0b72bf
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…gration/up-to-pr-223
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…gration/up-to-pr-223
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 22, 2026
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 22, 2026 00:52
c0b72bf to
b6d7119
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 23, 2026
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 23, 2026 02:58
b6d7119 to
41567a2
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 23, 2026
…ns) into integration/up-to-pr-223
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 25, 2026
Explicitly pin DtdProcessing.Prohibit and XmlResolver = null when parsing each captured vendor-extension element's OuterXml back into an XElement, mirroring the repo-wide defense-in-depth convention already established in XmiDeserializer.FromXml/FromFile. .NET 6+ already defaults XmlResolver to null and disables DTD processing on the outer document read, and a DOCTYPE cannot legally appear inside a captured element's OuterXml, so this closes a consistency gap rather than an exploitable path today — but it survives a future framework downgrade or accidental restoration of XmlUrlResolver, and keeps every XML parse entry point in the repo on the same explicit hardening idiom. Found by the security-audit pass of the PR TrakHound#223 dime review cycle.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 25, 2026
…ling
IConfiguration.VendorExtensions' class remarks misquoted
MTConnectDevices_2.7.xsd's ComponentConfigurationType content model as
minOccurs="0" on the AbstractConfiguration ref; the schema
(tests/Compliance/.../v2_7/MTConnectDevices_2.7.xsd:8168) declares
minOccurs='1' — once <Configuration> is present it must contain at
least one AbstractConfiguration-substitution child.
Also sweeps the handful of BrE spellings ("unrecognised", "behaviour",
"serialise(s/d)", "deserialiser") introduced by this PR's own commits
into AmE, including two test method names, matching the repo-wide
AmE convention already applied everywhere else in the codebase.
Found by the code-review pass of the PR TrakHound#223 dime review cycle.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 25, 2026
…ns) into up-to-pr-223
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
Explicitly pin DtdProcessing.Prohibit and XmlResolver = null when parsing each captured vendor-extension element's OuterXml back into an XElement, mirroring the repo-wide defense-in-depth convention already established in XmiDeserializer.FromXml/FromFile. .NET 6+ already defaults XmlResolver to null and disables DTD processing on the outer document read, and a DOCTYPE cannot legally appear inside a captured element's OuterXml, so this closes a consistency gap rather than an exploitable path today — but it survives a future framework downgrade or accidental restoration of XmlUrlResolver, and keeps every XML parse entry point in the repo on the same explicit hardening idiom. Found by the security-audit pass of the PR TrakHound#223 dime review cycle.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…ling
IConfiguration.VendorExtensions' class remarks misquoted
MTConnectDevices_2.7.xsd's ComponentConfigurationType content model as
minOccurs="0" on the AbstractConfiguration ref; the schema
(tests/Compliance/.../v2_7/MTConnectDevices_2.7.xsd:8168) declares
minOccurs='1' — once <Configuration> is present it must contain at
least one AbstractConfiguration-substitution child.
Also sweeps the handful of BrE spellings ("unrecognised", "behaviour",
"serialise(s/d)", "deserialiser") introduced by this PR's own commits
into AmE, including two test method names, matching the repo-wide
AmE convention already applied everywhere else in the codebase.
Found by the code-review pass of the PR TrakHound#223 dime review cycle.
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 26, 2026 04:42
f0d1147 to
a7495a5
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…tegration/up-to-pr-223 Cascade rebuild after upstream/master advanced to 0ddc3c6 (PR TrakHound#220 merge).
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…ng PR test surfaces integration-branch-only glue fix. PR TrakHound#221, TrakHound#223/TrakHound#224 (via TrakHound#233's consolidation), and TrakHound#226 each add or touch test files written against NUnit 3, whose bare-lambda Assert.Throws/DoesNotThrow calls become ambiguous once PR TrakHound#239's NUnit 3->4 upgrade lands in the same tree. Cast to Action explicitly at each affected call site and replace the two CollectionAssert.AreEqual calls (removed in NUnit 4) with Assert.That(..., Is.EqualTo(...)). None of the individual PR branches need this fix standalone — each builds clean against current upstream/master without NUnit 4 present.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
Explicitly pin DtdProcessing.Prohibit and XmlResolver = null when parsing each captured vendor-extension element's OuterXml back into an XElement, mirroring the repo-wide defense-in-depth convention already established in XmiDeserializer.FromXml/FromFile. .NET 6+ already defaults XmlResolver to null and disables DTD processing on the outer document read, and a DOCTYPE cannot legally appear inside a captured element's OuterXml, so this closes a consistency gap rather than an exploitable path today — but it survives a future framework downgrade or accidental restoration of XmlUrlResolver, and keeps every XML parse entry point in the repo on the same explicit hardening idiom. Found by the security-audit pass of the PR TrakHound#223 dime review cycle.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…ling
IConfiguration.VendorExtensions' class remarks misquoted
MTConnectDevices_2.7.xsd's ComponentConfigurationType content model as
minOccurs="0" on the AbstractConfiguration ref; the schema
(tests/Compliance/.../v2_7/MTConnectDevices_2.7.xsd:8168) declares
minOccurs='1' — once <Configuration> is present it must contain at
least one AbstractConfiguration-substitution child.
Also sweeps the handful of BrE spellings ("unrecognised", "behaviour",
"serialise(s/d)", "deserialiser") introduced by this PR's own commits
into AmE, including two test method names, matching the repo-wide
AmE convention already applied everywhere else in the codebase.
Found by the code-review pass of the PR TrakHound#223 dime review cycle.
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 26, 2026 16:24
a7495a5 to
e810192
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…tegration/up-to-pr-223 on top of integration/up-to-pr-233
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 26, 2026
…tegration/up-to-pr-223 on top of previous cascade tip
Adds `Devices.Configurations.Configuration` to the switch that flips `ClassModel.IsPartial = true` in the CSharp renderer, matching the pattern already applied to `Devices.Component`, `Devices.Composition`, `Devices.DataItem`, and other SysML classes that carry hand-authored partial companion files in `libraries/MTConnect.NET-Common/`. Motivates: the follow-up regen emits `partial class Configuration` and `partial interface IConfiguration`, allowing a hand-authored partial to supply the vendor-extension surface (`VendorExtensions`) that lives outside the SysML model but is required by the MTConnect v2.7 XSD extension mechanism (`substitutionGroup='AbstractConfiguration'`).
Regenerated output of the sibling `feat(sysml-import): mark Configuration partial for extension` commit. The two `.g.cs` files now declare `partial class Configuration` and `partial interface IConfiguration`, unlocking the hand-authored companion partials that supply the vendor-extension surface. Diff scope is intentionally narrowed to the two Configuration files that carry the semantic change. A full regen also surfaces pre-existing trailing-whitespace drift across ~60 other `.g.cs` files caused by `Model.scriban` emitting an 8-space blank line between properties; that drift is out of scope for this PR and belongs to a separate `chore` PR that fixes the whitespace-trim markers in the templates.
Adds `IEnumerable<XElement> VendorExtensions` to `IConfiguration` via hand-authored partial companions to the generated `.g.cs` files. The property carries fully-formed, vendor-namespaced XML elements that are written verbatim into the on-the-wire `<Configuration>` element by the XML formatter (wired in the sibling `feat(xml)` commit). Standard basis. `MTConnectDevices_2.7.xsd` declares `ComponentConfigurationType` with `<xs:element ref="AbstractConfiguration" minOccurs="0" maxOccurs="unbounded"/>` and marks every standard child (`SensorConfiguration`, `Specifications`, `Relationships`, `CoordinateSystems`, `Motion`, `SolidModel`, `ImageFiles`, `PowerSources`) with `substitutionGroup='AbstractConfiguration'`. Vendors extend by publishing their own XSD declaring a vendor-namespaced element that likewise substitutes for `AbstractConfiguration`; `VendorExtensions` is the surface that carries those instances end to end. Non-goal. This is not a raw-string passthrough — the type is `XElement`, which forces vendors to author well-formed XML rather than a raw string that would bypass namespace hygiene.
Wires the `VendorExtensions` surface through the XML formatter's `XmlConfiguration` surrogate: - **Read path.** `[XmlAnyElement] XmlElement[] VendorExtensions` on the surrogate captures every child of `<Configuration>` that the strongly-typed slots (`CoordinateSystems`, `Motion`, `Relationships`, `SensorConfiguration`, `SolidModel`, `Specifications`) do not bind. Each captured element is projected to `System.Xml.Linq.XElement` (with whitespace preserved) and added to `Configuration.VendorExtensions`. - **Write path.** `XmlConfiguration.WriteXml` iterates `configuration.VendorExtensions` after the standard children and writes each `XElement` verbatim via `WriteRaw` using `SaveOptions.DisableFormatting`, so vendor-namespaced elements preserve their prefixes, attributes, and mixed content exactly as authored. The formatter is agnostic to whether a vendor XSD is loaded at validation time — that responsibility sits with the caller of the strict-load XSD gate. When the vendor XSD is present, the emitted document validates against `MTConnectDevices_2.7.xsd`'s `ComponentConfigurationType` via the `AbstractConfiguration` substitution mechanism (schema element declared at `Schemas/v2_7/MTConnectDevices_2.7.xsd` line 8161).
Locks the vendor-extension surface behavior on `XmlConfiguration`: - Emit: a single vendor-namespaced element serializes verbatim inside `<Configuration>`; multiple entries preserve author order; vendor extensions coexist with standard children such as `<Motion>`. - Read: an unrecognized child element is captured onto `IConfiguration.VendorExtensions` with its namespace and payload intact; a full write-then-read round trip preserves the vendor element's local name, namespace, attributes, and text content. - Null / empty: `null` and empty collections emit no extra child element inside `<Configuration>`; a `<Configuration>` composed only of standard children yields `null` `VendorExtensions` (no false positives via the `[XmlAnyElement]` capture). Sources cited: MTConnect v2.7 XSD `ComponentConfigurationType` + `substitutionGroup='AbstractConfiguration'`, SysML XMI `Configuration` UML class, Part 2 (Devices) prose on Configuration extensibility.
Adds two edge-case rows on the `XmlConfiguration` write path: - A collection containing one or more `null` `XElement` entries writes the non-null entries verbatim and skips the null slot -- no stray empty tag emitted between real extensions. - A collection whose entries are ALL null writes no extension element at all; the emitted `<Configuration>` element is empty (self-closing or expanded end tag). Both rows pin the guard `if (extension == null) continue;` on the write path against a future refactor that would emit an empty tag or throw.
Adds a workflow fixture that boots an in-process
`MTConnectAgentBroker` plus embedded `MTConnectHttpServer`, seeds a
Device whose Linear component's Configuration carries a
vendor-namespaced `XElement`, and asserts:
- The emitted /probe response body contains the vendor element
verbatim inside the `<Configuration>` envelope -- local name,
namespace prefix, attributes, and child text preserved.
- A round trip through the strongly-typed `MTConnectHttpClient`
arrives at the client model with the vendor element intact on
`linear.Configuration.VendorExtensions` -- namespace, attributes,
child element text preserved.
Both tests are tagged `[Trait("Category", "E2E")]` and use ephemeral
ports allocated from a base outside the other workflow fixtures'
ranges so parallel workers do not collide.
Source: MTConnectDevices_2.7.xsd line 8161 declares
`ComponentConfigurationType` with
`<xs:element ref="AbstractConfiguration" minOccurs="0"
maxOccurs="unbounded"/>`; vendor extensions substitute into the
`AbstractConfiguration` group.
Add four new assertions to the vendor-extension round-trip pinning surface: - Interface contract — IConfiguration.VendorExtensions getter reflects the value set through the concrete Configuration.VendorExtensions setter, pinning the polymorphic projection. - Mixed content — a Configuration carrying a standard Motion child AND a vendor extension round-trips through write + read with both slots populated. Exercises the branch of XmlConfiguration.ToConfiguration where strongly-typed children and vendor-namespaced children coexist. - Nested attribute preservation — extensions with attributes on both the root element AND nested descendants round-trip verbatim through the WriteRaw / XElement.Parse(LoadOptions.PreserveWhitespace) pipeline. - Distinct vendor namespaces — two extensions from different vendor namespaces keep their bindings and are distinguished by fully-qualified XName, not local name alone.
Lands the coverage-FLOOR tests missing from the initial PR TrakHound#223 surface: - XmlConfigurationBranchCoverageTests: pins the WriteXml null-configuration guard (both outputComments values), the outputComments=true comment path, and the ToConfiguration zero-length + all-null + read-side mixed branches on the XmlElement[] projection. - ConfigurationVendorExtensionsEdgeCaseTests: default-namespace (unprefixed) extensions, attribute-only extensions, text-only extensions, unicode payloads (combining marks + RTL + astral surrogate pairs), predefined entity round-tripping, dense Configuration with Motion + SensorConfiguration + vendor extension coexistence, three-vendor namespaces preserved, and three-level nesting depth. - ConfigurationVendorExtensionsXsdStrictValidationTests: crown-jewel XSD-1.0 strict validation — emits a probe envelope via the production XmlDevicesResponseDocument pipeline, loads the v2.7 Devices XSD (via XsdPreprocessor for XSD 1.1 stripping) alongside a synthetic vendor XSD declaring the element as substitutionGroup='mtc:AbstractConfiguration', and asserts zero validation errors. Negative twin rejects a foreign element that lacks a substitution declaration. - ConfigurationVendorExtensionsJsonKnownLimitTests + ConfigurationVendorExtensionsCppAgentJsonKnownLimitTests: pin the current known limitation that both JSON dialects silently drop VendorExtensions (the XML formatter is the sole surface honoring the vendor-substitution contract today), so any future first-class JSON representation must be a deliberate edit to these tests plus the interface docblock. Verified green on bluefin: - MTConnect.NET-XML-Tests: 131 passed / 0 failed - MTConnect.NET-JSON-Tests: 68 passed / 0 failed - MTConnect.NET-JSON-cppagent-Tests: 367 passed / 0 failed
Explicitly pin DtdProcessing.Prohibit and XmlResolver = null when parsing each captured vendor-extension element's OuterXml back into an XElement, mirroring the repo-wide defense-in-depth convention already established in XmiDeserializer.FromXml/FromFile. .NET 6+ already defaults XmlResolver to null and disables DTD processing on the outer document read, and a DOCTYPE cannot legally appear inside a captured element's OuterXml, so this closes a consistency gap rather than an exploitable path today — but it survives a future framework downgrade or accidental restoration of XmlUrlResolver, and keeps every XML parse entry point in the repo on the same explicit hardening idiom. Found by the security-audit pass of the PR TrakHound#223 dime review cycle.
…ling
IConfiguration.VendorExtensions' class remarks misquoted
MTConnectDevices_2.7.xsd's ComponentConfigurationType content model as
minOccurs="0" on the AbstractConfiguration ref; the schema
(tests/Compliance/.../v2_7/MTConnectDevices_2.7.xsd:8168) declares
minOccurs='1' — once <Configuration> is present it must contain at
least one AbstractConfiguration-substitution child.
Also sweeps the handful of BrE spellings ("unrecognised", "behaviour",
"serialise(s/d)", "deserialiser") introduced by this PR's own commits
into AmE, including two test method names, matching the repo-wide
AmE convention already applied everywhere else in the codebase.
Found by the code-review pass of the PR TrakHound#223 dime review cycle.
ottobolyos
force-pushed
the
feat/configuration-vendor-extensions
branch
from
August 27, 2026 02:49
e810192 to
f9bb2fc
Compare
Per §1.0d-trigies-vicies-octies (AmE mandatory across every PR-associated surface), the residual BrE 'Serialise' in the VendorExtensions serialization comment is converted to 'Serialize'. No behavior change; comment-only edit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a standard-compliant vendor-extension surface on
IConfiguration— a newIEnumerable<XElement> VendorExtensionsproperty — that carries fully-formed, vendor-namespaced XML elements verbatim through the MTConnect.NET XML formatter. The mechanism mirrors the MTConnect v2.7 XSD's own extension design: every standard child ofComponentConfigurationTypeis asubstitutionGroup='AbstractConfiguration'declaration, and vendors extend the type by publishing their own XSD that likewise substitutes forAbstractConfiguration.VendorExtensionsis the strongly-typed slot that carries those substitutions end-to-end.Standard citations
MTConnectDevices_2.7.xsd:ComponentConfigurationType(line 8161) declares its content model as<xs:element ref="AbstractConfiguration" minOccurs="1" maxOccurs="unbounded"/>— the sequence permits any element that substitutes for the abstractAbstractConfigurationelement and rejects unknown children otherwise (noxs:any).SensorConfiguration,Specifications,Relationships,CoordinateSystems,Motion,SolidModel,ImageFiles,PowerSources— withsubstitutionGroup='AbstractConfiguration'. Vendors extend the type by publishing an XSD declaring a vendor-namespaced element with the samesubstitutionGroup.The MTConnect.NET XML formatter writes each
VendorExtensionsentry verbatim inside the<Configuration>sequence viaXmlWriter.WriteRaw. Downstream strict-XSD validation continues to work when the vendor's XSD is loaded alongside the MTConnect schemas — the vendor's XSD supplies the substitution declaration that makes the element valid at theAbstractConfigurationslot.What this PR does
feat(sysml-import)— marksDevices.Configurations.ConfigurationasIsPartial = truein the CSharp renderer, matching the pattern already applied toComponent,Composition,DataItem, and other classes that carry hand-authored companion partials.feat(common)— regeneratesConfiguration.g.csandIConfiguration.g.csfrom the v2.7 XMI to declarepartial class/partial interface.feat(common)— adds hand-authoredConfiguration.csandIConfiguration.cspartials that declareIEnumerable<XElement> VendorExtensions { get; }on the interface and{ get; set; }on the concrete.feat(xml)— wires the surface throughXmlConfiguration:[XmlAnyElement]capture on the read path,WriteRawemission on the write path, ordered after the standard children.Non-goals
This is not a raw-string
RawXmlpassthrough — a string-typed slot bypasses namespace hygiene and would produce documents that fail strict XSD validation at theComponentConfigurationTypesequence. TheXElementtyping forces well-formed XML at author time and preserves namespace declarations verbatim through the formatter.Breaking
Adds a new member (
VendorExtensions { get; }) to the publicIConfigurationinterface — a source-compatibility break for any external implementer ofIConfigurationoutside the library. Acceptable on the next major version boundary.Supersedes #214.
Dime review cycle 1
Retroactive backfill (2026-08-20). The initial 6-agent Ultrareview cycle exercised the XML round-trip surface end-to-end. Ledger reconstruction from commit history:
[TEST]test-coverage-audit — XML round-trip pin forConfiguration.VendorExtensions(empty, single-entry, multi-entry) landed astest(xml-tests): pin XML round trip for Configuration.VendorExtensions(d3d0d88).[TEST]test-coverage-audit — null-element handling on the interface surface pinned bytest(xml-tests): pin null-element handling on IConfiguration.VendorExtensions(fe095bf).[TEST]test-coverage-audit — HTTPProbeend-to-end coverage added viatest(integration): pin HTTP Probe end-to-end for IConfiguration.VendorExtensions(e8582e8).[TEST]test-coverage-audit — coverage widening for the interface implementation matrix added intest(xml-tests): widen IConfiguration.VendorExtensions coverage on interface(fe18487).Dime review cycle 2
Retroactive backfill (2026-08-20). Second cycle after the 2026-08-19 BrE→AmE message sweep reconciliation, dispatched to close coverage FLOOR gaps surfaced by re-running
test-coverage-auditon the reconciled tip. Ledger:[TEST]test-coverage-audit — coverage FLOOR gaps onIConfiguration.VendorExtensions(5 test files, +1165 LOC) closed atomically intest(xml-tests): close coverage FLOOR gaps on VendorExtensions(611f482). This commit was cherry-picked on top of the new AmE tip after the BrE→AmE swept in patch content and message (behaviour→behavior, serialise→serialize, honour→honor).nohuptest on bluefin with--blame-hang-timeout 15min: all individual test suites green (Common 3987, XML 131, JSON 68, MqttRelay 62, Docs 67 = 4315 tests, 0 failed). Test host crashed at 15-min inactivity timeout during concurrent Docs-Tests execution — bluefin-flake pattern documented in fix: clean every Release-pack warning and add multi-TFM CI gate #219 + fix(xml): dispatch v2.6/v2.7 namespaces + default unknown to Max #229 verify reports (docs-tests hang under concurrent bluefin load, uncorrelated with PR content since PR doesn't touch docs surface).(Zero unfixed findings — Ready-eligible.)
Dime review cycle 3
6-agent Ultrareview pass (2026-08-25) ahead of the Testing integration → Completed flip: bug-detector, code-review, security-audit, test-coverage-audit, documentation-audit, simplification.
[FIXED]security-audit MEDIUM —XElement.Parseon each captured vendor-extension element'sOuterXmlused implicitXmlReaderdefaults instead of the repo's explicit XXE-hardening idiom (XmiDeserializer.FromXml/FromFile: pinnedDtdProcessing.Prohibit+XmlResolver = null). Not currently exploitable — a DOCTYPE cannot legally appear inside a captured element'sOuterXml— but closed for defense-in-depth consistency.fix(xml): harden VendorExtensions XElement parse against XXE(13c9af7).[FIXED]code-review MEDIUM — class remarks onIConfiguration.VendorExtensionsmisquotedComponentConfigurationType'sAbstractConfigurationref asminOccurs="0"; the schema (MTConnectDevices_2.7.xsd:8168) declaresminOccurs='1'. Corrected in both this PR description (above) and the doc comment.fix(common,xml-tests): correct XSD minOccurs citation, sweep AmE spelling(f0d1147).[FIXED]code-review LOW — BrE spellings (unrecognised,behaviour,serialise(s/d),deserialiser) introduced by this PR's own commits, including two test method names, swept to AmE in the same commit as above.[SKIP-rationale]security-audit MEDIUM — no depth/size ceiling on vendor-extension capture. DTD is already prohibited on both the outer document read and (after the fix above) the per-element re-parse, so no entity-expansion amplification vector exists; a large-but-linear nested payload is bounded by the same document-size limits as any other XML content in the ingestion pipeline, and no other slot onXmlConfigurationenforces a bespoke depth/size cap either. Not an inconsistency worth a one-off guard here.[SKIP-rationale]security-audit LOW — vendor extension's own namespace-prefix declarations could locally shadow a prefix already open in the writer's ancestor scope.XElement.ToString()always emits well-formed, self-contained XML (not a raw-string injection risk); a non-namespace-aware downstream consumer choosing to key off prefix text rather than the resolved namespace URI is off-spec on its own terms.[TRACKED]bug-detector MEDIUM —Configuration.ImageFiles/Configuration.PowerSource(both cited as standardAbstractConfigurationsubstitution children in this PR's own "Standard citations" section) have noXmlConfigurationwiring at all — pre-existing, repo-wide gap (confirmed absent onupstream/masterbefore this PR; noXmlImageFile/XmlPowerSourcesurrogate exists anywhere in the tree). Before this PR such elements were silently dropped on read; after this PR's[XmlAnyElement]catch-all, they're captured intoVendorExtensionsinstead — still functionally absent from the typed properties, but now mislabeled as vendor content. Filed as XML wire format missing for Configuration.ImageFiles / Configuration.PowerSource #262 rather than fixed in-PR: closing it properly means adding two new XML surrogate type families from scratch, a distinct feature from theVendorExtensionsmechanism this PR delivers.[SKIP-rationale]bug-detector LOW — vendor-extension write order doesn't preserve original document-order interleave with standard children (always appended afterSpecifications). Consistent with every other slot onXmlConfiguration, which already serializes each standard child in a fixed field order regardless of source order — not a regression this PR introduces.[SKIP-rationale]bug-detector LOW —WriteRawbypasses the writer's indent-tracking state, so raw-inserted extension content can look inconsistently indented relative to siblings whenSettings.Indent = true. Cosmetic only.[SKIP-rationale]documentation-audit LOW — no VitePress page documents<Configuration>'s per-element wire format at all (pre-existing gap predating this PR;docs/wire-formats/xml.mdis envelope-level, not per-element, for every otherConfigurationchild too).[SKIP-rationale]simplification LOW × 2 — test-fixture scaffolding duplicated against siblingConfigurationPolymorphicHttpProbeWorkflowTests.cs, and structurally parallel JSON/JSON-cppagent known-limit test files. Both consistent with this repo's existing one-fixture-per-file convention for independently-movable test files; not accidental duplication.[CLEAN]test-coverage-audit — coverage substantively complete (enum-arm N/A, all failure/branch paths covered, no test smells). Noted a TDD-ordering process gap (feat commits landed before test commits in cycle 1) — historical, not a content gap, not re-litigated.All local verification re-run green after the fix commits:
dotnet build -warnaserror0/0,dotnet format --verify-no-changesclean,MTConnect.NET-XML-Tests131/131,MTConnect.NET-Common-Tests4102/4102,MTConnect.NET-HTTP-Tests112/112.(Zero unfixed MEDIUM+ findings — Completed-eligible.)
Depends on