Skip to content

Bring all fixes from HMI demos into OPC Foundation UWP branch. - #8

Merged
barnstee merged 3 commits into
masterfrom
Publishing_Prototype
May 31, 2016
Merged

Bring all fixes from HMI demos into OPC Foundation UWP branch.#8
barnstee merged 3 commits into
masterfrom
Publishing_Prototype

Conversation

@barnstee

Copy link
Copy Markdown
Member

Removed popups from sample apps (not supported in UWP apps).
Added missing JSON definitions.
Replaced all instances of XmlElement with XElement (XmlElement not serializable).
Replaced all instances of XmlAttribute with string (XmlAttribute not serializable).
Removed all pInvoke calls not supported in UWP apps (DNS Server lookups, HTTP access rules and X509 extensions no longer supported).

marcschier and others added 3 commits May 11, 2016 08:39
a. Changed to 10.0.586 SDK target
b. Updated nuget packages to latest version (.net core 5.1.0 and winrtxamltools to 2.0)
c. Added Publisher sample with documented sample config.xml. Single AmqpConnection class that is configured from config.xml, and supports Open, Send, Close, including internal Message Queue object to queue messages across disconnect/reconnect. Compare to Sample Client and the changes are simple and obvious.
d. Added JSONEncoder/Decoder to Stack (Types/Encoders). This is the only addition to the stack itself and at this point "experimental".
e. Ported Buffer/Leak fixes into this branch as well (Stack/Core/Stack/Tcp)
f. Added disclaimer paragraph into readme.md about publisher.
Added missing JSON definitions.
Replaced all instacnes of XmlElement with XElement (XmlElement not serializable).
Replaced all instances of XmlAttribute with string (XmlAttribute not serializable).
Removed all pInvoke calls not supported in UWP apps (DNS Server lookups, HTTP access rules and X509 extentions no longer supported).
@CLAassistant

CLAassistant commented May 13, 2016

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@barnstee
barnstee merged commit af822ce into master May 31, 2016
barnstee pushed a commit that referenced this pull request Nov 29, 2016
marcschier added a commit that referenced this pull request Jun 2, 2026
Address 7 of the 16 unresolved review comments from round 3:

- #6 Tools/Opc.Ua.SourceGeneration/ModelCompilation.cs:300 - add
  `using System.Collections.Generic` and
  `using Opc.Ua.SourceGeneration.Dependency` so the file no longer
  spells fully-qualified type names inline. Same sweep for the
  `System.Collections.Generic.`/`Opc.Ua.SourceGeneration.Dependency.`
  prefixes at other call sites in the file.
- #7 Tools/Opc.Ua.SourceGeneration.Core/Generators.cs:634 (and 3 other
  sites in FluentBuilderGenerator + GeneratorOptions + ModelCompilationOptions)
  - drop `FB-3 phase 3` / phase / track references from comments and
  xmldoc; describe the feature, not the rollout.
- #9 Libraries/Opc.Ua.Di.Client/Hosting/OpcUaClientDiBuilderExtensions.cs:69
  - re-wrap the long `<item><description>...</description></item>` doc
  list to fit inside the 140-char editorconfig limit.
- #12 Strip `global::` qualification from all hand-written PR code
  (Libraries/Opc.Ua.Di.*, Applications/PumpDeviceIntegrationServer).
  Only kept where required to disambiguate (test-side `Pumps.PumpNodeManager`
  / `Pumps.PumpNodeManagerFactory` collide with the source-generated
  `Opc.Ua.Pumps` namespace; restored `global::` on those refs).
  Hand-written code now matches the .editorconfig convention of letting
  `using` directives carry the namespace burden; source-generated code
  still uses `global::` (unchanged).
- #15 Remove `// -----` / `// =====` ASCII-banner `#region`-style
  comments from SoftwareUpdateClient.StateMachine.cs,
  SoftwareUpdateClient.Upload.cs, SoftwareUpdateFacetWiring.cs and
  SoftwareUpdateFileTransferManager.cs. Inline section comments
  remain unchanged.
- #16 Expand 344 one-line `/// <summary>text</summary>` declarations
  across 72 files in Applications/, Libraries/, Stack/, Tests/ and
  Tools/ into the three-line
  `/// <summary>\n/// text\n/// </summary>` form.
- Carry-over from FB-3 phase 3: while sweeping `global::` also
  un-qualify the hand-written Pumps server callsites
  (Pump #1 wiring in PumpNodeManager.cs / PumpNodeManager.Configure.cs)
  so the new code matches the reviewer's convention.

Validation:
- `dotnet build UA.slnx` — clean (0 errors, 15 unrelated warnings
  reported transiently by the build engine).
- `Opc.Ua.SourceGeneration.Core.Tests` — 3535 passed / 8 skipped.
- `Opc.Ua.Di.Tests` (Pump + DI client + SoftwareUpdate filter) —
  85/85 passed.

The remaining 9 round-3 items (architecture relocation #8,
`=>`→`{}` body sweep #10, ObjectType-proxy refactor #11/#14,
brace-formatting sweep #13, pump server Configure refactor #1/#2,
DeviceHealth + functional-group exposure #3/#5 plus DI doc
disambiguation #4) are tracked separately and will land in
subsequent commits.
marcschier added a commit that referenced this pull request Jun 5, 2026
- Move all channel-related types into `Stack/Opc.Ua.Core/Stack/Client/Channels/`
  subfolder (#13).
- Un-obsolete `SessionReconnectHandler` and the SessionExtensions
  `ReconnectAsync(connection, ct)` / `(channel, ct)` extensions; remove
  all SRH-related `#pragma warning disable CS0618` suppressions from
  applications and tests. AttachChannel/DetachChannel remain obsolete (#1, #4).
- Remove the shared-budget section from MigrationGuide.md (#3).
- Mark `ManagedSession.AttachChannel`/`DetachChannel` `[Obsolete]` so the
  warning surfaces all the way up the supported API surface (#9).
- Break the `<see cref=…>` line in `Session.ChannelManager.cs` to stay under
  140 chars (#10).
- Move `ChannelManagerSessionFactory` from `Opc.Ua.Gds.Client.Common` into
  `Opc.Ua.Client` as a public, documented session-factory option (#11).
- Audit other client classes for `IClientChannelManager`-aware overloads;
  add `ISessionFactory`-accepting overloads on `GlobalDiscoveryServerClient`,
  `LocalDiscoveryServerClient`, `ServerPushConfigurationClient` so any
  session factory (including `ChannelManagerSessionFactory`) works (#12).
- Refactor MCP server (`OpcUaSessionManager`, `Program`) to use
  `ManagedSession` + DI-resolved `IClientChannelManager`; remove
  `SessionReconnectHandler` and manual keep-alive reconnect (#2).
- ConnectionStateMachine code-style fixes: use named delegate types,
  collapse multi-line callback properties to single lines, swap
  `timeProvider`/`maxTotalReconnectTime` constructor param order,
  fix multi-line declaration (#6, #7, #8).
- HTTPS resilience: confirmed `Microsoft.Extensions.Http` and
  `Microsoft.Extensions.Http.Resilience` 10.6.0 support
  `net472`/`net48`/`netstandard2.1`; removed `#if NET8_0_OR_GREATER`
  gating from `ManagedSessionBuilder` and `OpcUaClientBuilderExtensions`
  and HTTPS csproj package refs (#5).

Verification:
- `Opc.Ua.Client` + `Opc.Ua.Core` + `Opc.Ua.Gds.Client.Common` build
  clean (0 warnings, 0 errors).
- 56/56 core channel-manager / retry-budget / HTTPS factory tests pass.
- 129/129 client (ManagedSession + legacy SRH back-compat + SessionExtensions)
  tests pass.
marcschier added a commit that referenced this pull request Jun 18, 2026
…ion-guide trim, SKS/GDS clarification

R1 (review #2/#3) — migrate modern public PubSub API collections to ArrayOf<T>
(repo-preferred): EventPublishedDataSet result, IPubSubApplication.ReplaceConfiguration
Async -> ArrayOf<StatusCode>, snapshot/message Fields, JsonNetworkMessage.ReplyTo,
UadpApplicationInformation.*, UADP discovery lists, validation Issues, SksKeyResponse/
SksSecurityGroup lists, WriterGroup/ReaderGroup/connection group views, metadata/key-ring/
policy registries. Kept PubSubApplication.Connections as a live IReadOnlyList view (ArrayOf
would copy on each access; commented). [Obsolete] shims + internal transport lists untouched.

R2 (review #4) — replace public-API tuples with named record structs: PubSubErrorEntry
(PubSubDiagnostics.RecentErrors -> ArrayOf<PubSubErrorEntry>, LastError -> PubSubErrorEntry?),
AesCtrNonceComponents (AesCtrNonceLayout.Parse), UadpHeaderByteParts (UadpFlagsEncodingMask
.Split), and WriterGroup/DataSetWriter/ReaderGroup/DataSetReader key record structs for
PubSubConfigurationSnapshot composite dictionary keys (equality/hashing preserved).

R3 (review #8) — trim Docs/migrate/2.0.x/pubsub.md to breaking changes only (keep
obsoletion, AMQP removal, STJ encoder swap, enum rename, RawData padding, content-mask,
compat matrix; remove additive sections, renumber 1-7); relocate KeepAlive + DataSetReader
filter/timeout detail to PubSub.md; fix inbound cross-links in README/WhatsNewIn2.0/migrate
README.

R4 (review #13) — investigation (files/sks-gds-dedup.md): Part 14 SKS and Part 12 Key
Credential are legitimately separate; broad unification not worth the coupling/risk. Applied
the recommended PubSub.md SKS wording clarification; deferred optional narrow helpers.

Verification: all 4 PubSub libs build net10 + net48 0/0; samples + fuzz build clean;
PubSub.Tests 1250, Udp 140, Mqtt 133, Server 141 - all pass.
marcschier referenced this pull request in marcschier/UA-.NETStandard Jun 27, 2026
…eview feedback

Rename (full identity: folders, csproj, AssemblyName, PackageId, RootNamespace,
C# namespaces, references, UA.slnx, InternalsVisibleTo):
- Opc.Ua.Server.Distributed        -> Opc.Ua.Server.Redundancy
- Opc.Ua.Server.Distributed.Crdt   -> Opc.Ua.Server.Redundancy.Crdt
- Opc.Ua.Server.Distributed.Kubernetes -> Opc.Ua.Server.Redundancy.K8s (identity only;
  the word "Kubernetes" and k8s client types are preserved in prose/code)
- Tests mirror the rename (.Tests/.Crdt.Tests/.K8s.Tests/.Integration.Tests)
- Application HighAvailabilityServer -> RedundantServer
- Documentation updated to the new names (present tense, merged-to-master state)

Review feedback (verified previously-resolved PR comments are satisfied; fixed gaps):
- async node-manager base, A/A sample README, REQ-UA tags, CA2213, central
  CryptoUtils polyfills, ILocalAddressSpace namespace - all confirmed in current code

Roadmap findings implemented:
- OPCFoundation#28 ServerUriArray-only peers are now failover candidates (connect + read live level)
- #8 FetchRedundancyInfo follow-up reads merged into one ReadValuesAsync
- OPCFoundation#29 ContinuationPoint mirroring documented as envelope-only (partial-SHALL boundary)
- OPCFoundation#30 Server.ServerRedundancy typed to NonTransparent/Transparent subtype by mode
- OPCFoundation#20 Add* (standard) vs Use* (extension) convention documented + ServiceLevel cross-ref
- OPCFoundation#24 transparent-mode shared application-key blast-radius/rotation guidance expanded

CI green (fixes pre-existing all-TFM build break, unrelated to the rename):
- Opc.Ua.Sessions.Tests redundancy test doubles: implement IServerRedundancyHandler.
  ShouldFailover and use RedundancySupport (RedundancyMode was removed)
- Integration tests: add RestrictForLegacyTfm + CustomTestTarget fallback so legacy
  TFM CI passes no-op instead of failing with empty TargetFrameworks
- Integration Maintenance assertion updated to spec-aligned behavior (Maintenance with
  an available peer warrants failover, Part 4 Table 105)

Validated: full UA.slnx builds on net10 + net48; Redundancy 178, Crdt 30, K8s 27,
Integration 3, Client redundancy 76, Sessions failover 4 - all pass.
marcschier added a commit that referenced this pull request Aug 6, 2026
Review of spec-drafts PR #8 rejected the ViewVersion wording I contributed, and it was right on all three counts. It required "a deterministic function ... in a canonical order" while naming neither, so two conforming servers could still disagree - which is the single property the requirement exists to guarantee. It ignored that OPC 10000-3 clause 5.4 requires ViewVersion to be greater than zero. And "changes when, and only when, the membership changes" is unsatisfiable for a 32-bit value: collisions break the "when" direction, so I had asserted a property no implementation can hold.

WoT Binding clause 12.6 now specifies the function outright, which is what makes it conformance-testable, and this implements it exactly: each resolved member's ExpandedNodeId in the portable nsu= form of clause 5.1.1, sorted ascending by Unicode code point, joined each followed by U+000A, encoded as UTF-8, and the first four octets of the SHA-256 digest read as a big-endian UInt32, with zero reported as one.

The previous implementation matched none of that - FNV-1a over length-prefixed session-local NodeId strings, hashing groups as well as members, and free to return zero. Only members are hashed now; the clause says ViewVersion records what a View contains and not how it is arranged, so group names and nesting no longer contribute. The builder takes the server namespace table because the portable form needs it.

A test pins the result to a vector computed independently of this code rather than to whatever the code produces: two members serialize to "nsu=urn:test:pump;i=1001\nnsu=urn:test:pump;s=Alpha\n", whose SHA-256 begins 1C A7 69 28, so ViewVersion is 480733480.

The test that asserted two different memberships never collide is replaced. The specification now states that a UInt32 cannot separate every membership and that collisions may occur, so that assertion no longer describes the contract. What replaces it records something sharper and is raised against the specification: the specified join uses U+000A and does not escape it, while an ExpandedNodeId string identifier may contain U+000A, so one member embedding a newline serializes byte-for-byte as the two members it imitates. That is a structural collision an author can construct, not the statistical one the clause knowingly accepts.

Also from the same review round:
- The NodeSet is re-adopted at PR #8 head. ModelVersion is typed SemanticVersionString, whose syntax OPC 10000-5 fixes as major.minor.patch, so the value moves from 1.1 to 1.1.0. Nothing else in the 203 KB file differs.
- Clause 11 now requires the endpoint allowlist, trust policy and size limits that guard a caller-supplied AssetEndpoint to apply to the endpoints DiscoverAssets probes. ConnectionTest and CreateAssetForEndpoint already validated theirs; DiscoverAssets returned whatever the provider found, which handed a caller the result of a probe it could not have made itself. Discovered endpoints are now filtered through the same policy and the withheld count is logged.

Checked and already conformant: DiscoverAssets is in the role-based access list; the channel floor is a configurable minimum applied to every management operation, so it is at least as strict as the clause requires of the mutating ones; and legacy data points already attach to the asset by HasWoTComponent.

Opc.Ua.WotCon.Tests 1054 pass on net10.0 and net48; the server builds 0 warnings on all six target frameworks.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
marcschier added a commit that referenced this pull request Aug 6, 2026
WoT Binding 12.6 originally joined the portable member identities with
U+000A and escaped nothing, but a NodeId string identifier may itself
contain U+000A. A single member embedding a newline therefore serialized
byte-for-byte as the two members it imitates, so two different
memberships shared a ViewVersion. That is a structural collision an
author can construct deliberately, distinct from the statistical 32-bit
collision the clause knowingly accepts, so the paragraph admitting
collisions did not cover it.

Raised and fixed in spec-drafts PR #8 (e86e833): each member is now
written as its length in UTF-8 octets, a colon, the string, and U+000A.
This realigns the implementation with that text, re-derives the
independently computed conformance vector, and flips the collision test
from characterizing the defect to asserting the guarantee. Both are
mutation-verified: removing the length prefix fails exactly those two.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
marcschier added a commit that referenced this pull request Aug 8, 2026
…ion 12) (#4182)

Follow-up to #4128, which merged the WoT Connectivity 1.1 work against
**draft2** of the companion specifications. Both drafts have moved
since, and this branch tracks them through to **draft3**, adds the
projection feature they introduced, and closes the gaps a bidirectional
re-audit found.

The specifications are the private
[`OPCF-Members/spec-drafts`](https://github.com/OPCF-Members/spec-drafts)
drafts of *OPC UA — WoT Connectivity 1.1* and *OPC UA — WoT Binding
1.1*. They are still drafts, which is why this is opened as a draft PR —
see [Status](#status) at the bottom.

## What changed in the specifications

Two revisions landed upstream while #4128 was in flight.

**draft2** replaced the `eventGroup` / `actionGroup` grouping constructs
with a single **projection / view** concept, added fifteen Section 6
vocabulary terms, and made two identifier rules normative. **draft3**
([spec PR #8](OPCF-Members/spec-drafts#8))
un-deprecated the incorporated OPC 10100-1 v1.02 surface, restructured
the conformance clause into three profiles, and tightened two security
obligations.

The draft3 NodeSet is **structurally identical** to draft2 — I diffed
all 286 nodes node-by-node. Every change is metadata: 96
`ReleaseStatus="Deprecated"` attributes removed, `PublicationDate`
advanced, and `WOTC-ProjectionMaterialization` claimed by three Type
Nodes that previously implemented it in name only.

## Projections (WoT Binding Section 12)

The substantial addition. A projection document selects affordances from
other Thing Descriptions or Thing Models and resolves to a document of
its own; materialized, it becomes an OPC UA `View` that **creates no
affordance Node** and only `Organizes` what its sources already
materialized.

| Piece | Where |
|---|---|
| Document model and validation | `Opc.Ua.Types/Wot/WotProjection.cs`,
`WotProjectionModel.cs` |
| Four-stage resolution engine |
`Opc.Ua.Types/Wot/WotProjectionResolver.cs` |
| Materialization plan |
`Opc.Ua.WotCon.Server/Materialization/WotViewProjectionPlan.cs`,
`WotProjectionViewBuilder.cs` |
| Live address-space host | `WotProjectionViewNodeManager.cs`,
`LifecycleWotViewProjectionHost.cs`, `IWotViewProjectionHost.cs` |
| Node lookup seam | `IWotMaterializedNodeIndex.cs` |

Groups reached by `ua:Organizes` become organizational Objects rather
than nested Views, only the outermost materialization is a `View`, and a
member whose source is served by another server is omitted and reported
rather than recreated — the federation case the clause calls out.

### How this is validated

The specification publishes twenty worked examples, two of which are a
**golden pair**: a projection document and the resolved view it is
defined to produce. `WotSpecExampleTests` embeds all twenty and runs the
pair through the resolver, asserting against *the specification's own
expected output* rather than against my reading of the prose. That
single document exercises all three selection forms, the bulk naming
rule, the security closure naming and the provenance term.

This is the strongest evidence in the PR, and it is worth more than the
unit tests around it: it is the same check the upstream
`validate_local.py` performs.

## Other spec work

- **Section 6 vocabulary** — fifteen missing model and platform terms
with their validation rules (`WotNodeSetConverter.ModelVocabulary.cs`).
- **Portable identifiers** — the two rules release 1.1 made normative,
with `WotNodeSetConverterOptions.AllowNonPortableIdentifiers` as a
documented downgrade-to-warning switch for documents authored against
v1.00. Defaults to `false`.
- **xRegistry §6.9 identifier construction** (`XRegistryIdentifier.cs`)
— reverse-authority symbolic identifiers; all seven published worked
examples are test cases.

## Gaps found by re-auditing against draft3

I audited in both directions — every normative requirement to code, and
every `uav:` term in code back to a spec that defines it. Three
findings, all fixed:

1. **`Wot-Con 1.02` accepted anything well-formed.** draft3 makes format
validation part of the unit, because *WoT-Con Minimal* is one unit wide
and materialization projects a *valid* document. The 1.02 upload path
only deserialized into the `ThingDescription` POCO, which happily
accepts JSON that is not a WoT document at all — every member of that
type is optional, so `{}` deserialized and was materialized. It now
parses via `WotDocument.Parse` and requires the document to identify
itself by a non-empty `name` or `title`, returning `Bad_DecodingError`
and materializing nothing otherwise. The rule lives in
`ThingDescriptionFormatValidator` and is shared with
`CreateAssetForEndpoint`, whose auto-generated Thing Descriptions §11
also treats as untrusted input.
2. **Two `WoTFile` operations were ungated.** draft3 §11 names `Write`
and `CloseAndUpdate` alongside the five management methods; both reached
the materializer without an access check. `EnforceManagementAccess` is
now wired into `WotAssetFileManager`.
3. **Two invented `uav:` terms.** `uav:NodeSet2Preservation` was
declared and never used — removed. `uav:eventFields` is read by
`OpcUaBindingPlanner` and defined in *neither* spec, while a comment
claimed it came from OPC 10101. It still works for compatibility but is
now documented as a non-standard extension at both sites rather than
misattributed.

The SSRF prohibition is satisfied by construction: there is no HTTP
client anywhere in these assemblies, and all resolution goes through the
injected `IWotThingResolver`.

## Two ambiguities fixed in the specifications themselves

The audit turned up two places where a conforming implementer had to
invent an answer. Rather than pick silently, I took both upstream — they
are pushed to [spec-drafts PR
#8](OPCF-Members/spec-drafts#8) (all checks
green) and are now normative in *Binding* §12.6 and *Connectivity*
§7.13:

- **`ViewVersion`** had one sentence saying only *that* it changes. It
now changes iff the resolved membership changes, is updated at commit
time alongside the `NodeVersion` stamping, and is a deterministic
function of the membership alone in a canonical order — so two servers
that resolved the same membership agree, which a per-server counter
would not across a redundant pair. Explicitly **not monotonic**: compare
for inequality only.
- **"Type-level view"** was used once in §12.2 and defined nowhere,
while every other statement of materialization was universal. §12.6 now
*defines* it rather than contradicting §12.2: the NodeClass is `View` in
both cases.

Both were already what this code did, so neither changed behaviour — but
the order-insensitivity the clause added was **not** covered by any
test, and `ViewVersionIsUnchangedWhenOnlyTheOrderOfTheMembershipChanges`
now holds it. It is mutation-verified: removing the ordinal sort turns
it red.

`docs/WotBindings.md` no longer lists these as points "the specification
leaves open".

## Validation

| Suite | Result |
|---|---|
| `Opc.Ua.Types.Tests` | 8525 ✅ (8518 on net48) |
| `Opc.Ua.WotCon.Tests` | 1057 ✅ (net10.0 and net48) |
| `Opc.Ua.WotCon.Bindings.Tests` | 559 ✅ |
| `Opc.Ua.XRegistry.Tests` | 196 ✅ |
| `Opc.Ua.Server.Tests` | 4027 ✅ |
| `Opc.Ua.SourceGeneration.Core.Tests` | 3771 ✅ |
| `Opc.Ua.WotCon.Samples.Tests` | 9 ✅ |

The full `UA.slnx` builds with **0 errors and 0 warnings attributable to
this PR** on all 6 TFMs (`TreatWarningsAsErrors` is on).
`Opc.Ua.Server.Tests` and the source-generation suites are listed
because this PR now also touches `MasterNodeManager` and the WoT-Con
NodeSet.

## Compatibility

No public API is removed or broken. Everything added is new surface
using `ArrayOf<T>` / `ByteString` per the repo conventions, wired for DI
with a direct-construction fallback, and the one behavioural tightening
(`Wot-Con 1.02` format validation) rejects input that was never valid to
begin with.

One pre-existing issue is **not** addressed here because it came in with
#4128 and is out of scope: `WotAssetFileManager.OnCloseAndUpdate` uses
`.GetAwaiter().GetResult()`, which the repo rules ban. Worth a
follow-up.

## Landed since this PR was opened

- **DI upgraded to 1.05.0.** `ConnectsTo` was declared a subtype of
`HierarchicalReferences`, contradicting OPC 10000-100 §5.5 Table 48.
That is an upstream error in every official DI NodeSet up to 1.04,
corrected by the OPC Foundation in 1.05.0, so the fixture is replaced
with the verbatim 1.05.0 file rather than hand-patched. A guard test
pins the corrected form. This also closed a second gap: six DI types the
WoT model was missing are 1.05 additions.
- **The sample Pump has the type definition OPC 40223 declares for it.**
`Pump1.Identification` carried DI's `FunctionalGroupType` instead of
`PumpIdentificationType`. Every test, the sample documents and the
README agreed with each other about it, which is why it survived.
- **`WotPumpAddressSpaceComparisonTests`** compares this server against
`PumpDeviceIntegrationServer`, which builds the same OPC 40223 Pump from
generated NodeSets and hand-written wiring. An independent oracle rather
than a restatement of the same assumptions - it is what found the defect
above.
- **Error containment in `MaterializeProjectionViewsAsync`.** An
exception mid-loop discarded the applied View handles before they were
published, leaving View Nodes in the address space that nothing could
remove.
- **`HasWoTProjection`** is now created; it was documented and plumbed
but never wired.
- **The default in-memory View host** no longer deletes the View it has
just applied.
- **An undeclared NodeSet alias now says so.** It previously surfaced as
`IdentifierMissing`, naming neither the value nor the fact that an alias
was expected.
- **`ViewVersion` follows the algorithm the specification now states.**
My earlier spec text was rejected on review, correctly: it named neither
the function nor the canonical order it demanded, ignored that OPC
10000-3 §5.4 requires a value greater than zero, and its "when, and only
when" is unsatisfiable for a 32-bit value. §12.6 now specifies the
algorithm outright and this implementation follows it exactly, pinned by
an independently computed test vector. Implementing it surfaced a
further defect in the clause - joining members on U+000A is not
injective, because a NodeId string identifier may itself contain U+000A
- which is fixed in the specification by length-prefixing each member.
- **An auto-generated Thing Description is treated as untrusted input.**
§11 requires it; `CreateAssetForEndpoint` validated the endpoint but
handed whatever the discovery provider returned straight to the
materializer. The `Wot-Con 1.02` format rule now gates both paths from
one place.
- **Document bytes moved out of the registry snapshot** into a
content-addressed store, so a commit no longer rewrites the whole corpus
to edit one document. Writes stage and are promoted during the commit,
which preserves both durability-first ordering and the fail-closed rule
that a blob directory without a manifest means operator recovery.
Restoring streaming digest verification along the way closed a real gap:
content integrity had degenerated to a length comparison.
- **External references now reach a NodeManager registered later.** A
reference to a Node that does not exist yet is dropped rather than
queued, and the dynamic-registration path had no second phase to apply
them in, so registering a NodeSet before the one it points into lost the
forward edge permanently. Review of that change then found a state leak
in its rollback path, fixed with a red-to-green regression test.
- **`WOTC-Legacy` is renamed `Wot-Con 1.02`** in the specification and
here. Editorial: the NodeSet diff is `<Category>` values only.

## Status

Open for review. Two things may still move:

1. **The specifications are unpublished drafts.** Anything here can
change if the working group decides differently. The `ViewVersion`
definition has since been settled - §12.6 specifies the algorithm and
this follows it - but the same could happen to any other clause tracked
here.
2. **`uav:eventFields`** is kept as a documented non-standard extension.
That is a judgement call the WG may want to reverse, either by adopting
the term or removing it.

Two known items are recorded rather than fixed:

- `Pump1` has no hierarchical parent, so it is reachable by NodeId but
not by browsing down from `Objects`. The cross-NodeSet ordering defect
this was originally attributed to is fixed above, and cross-manager
references are now symmetric in both registration orders. The Pump
itself is still blocked on a different cause found while verifying that
fix: with the reference added, `DeviceSet` exists but the Pump has
**zero** inverse references, so the edge is dropped at *import* - before
any external-reference machinery runs. That is a separate defect in the
materialization path, not the one that was fixed, and it is left
recorded rather than guessed at.
- `WotAssetFileManager.OnCloseAndUpdate` uses
`.GetAwaiter().GetResult()`, which the repo rules ban. Pre-existing from
#4128 and out of scope here.

Review of the projection resolver and the materialization host is the
most valuable thing to look at; the vocabulary and identifier commits
are mechanical by comparison.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants