# Original 1.5.378.182 canonical adapter — partial diagnostic proof This folder compiles a standalone diagnostic against the **unchanged original 83ed880e published host/client DLLs**. It does not rebuild or patch product code, restore a substitute SDK, edit a baseline oracle, install a replacement session factory, or approve a performance denominator. Only short 1–4-cycle smoke runs are implemented. No original warmed/full/two-hour run has been performed. The immutable SDK reports `1.5.378.182+6cca32dd6d79c8b57d2e00e4d2071b991d2cf71b`. The source workload was ported from `Sdk2LifecycleSoakTests` at `75057d8c`. `evidence.json` records source/input hashes, exact SDK layout and short-run results. Raw logs and task-owned PKI stay outside the repository; credentials are not logged. ## Supported operation coverage `CanonicalOperations.cs` preserves the candidate's operation ordering, status assertions, timeouts, model, grants, event data, page size and typed-array shape. The representation changes are explicit: SDK2 Variant/ArrayOf helpers become the original Variant constructor and CLR values; ByteString becomes byte[]; and QualifiedName.From becomes its original constructor. There are no collection shims or private-state mutations. | Canonical operation per normal cycle | Original short control observation | |---|---| | Two encrypted opc.tcp / SignAndEncrypt sessions, Raw/UNS URI binding | Completed | | One subscription, three data items, notifications | Completed | | One Write service with Int32/Int16[]/UNS Int32 values, three readbacks | Completed with original Good/type assertions | | Two browse pages, one explicit release and one abandonment | Completed; active browse count 1 | | History first page, foreign-session denial, resume, explicit release, six abandoned points | All 10 HistoryRead services returned; active history count 6 | | Writable off/on, array removal/recreation/publication | Completed | | Key revocation, denied read, authority restoration | Completed | | Paused TCP read, one-second cancellation, resume and successful recovery read | Completed; original UA status 2156134400 on the canceled read | | Subscription deletion/removal and address-space rebuild/reconfiguration | Completed | | Both normal clients CloseAsync | Both returned; version-specific post-close list comparison differs | | Every-fourth-cycle restart with an active session/subscription/item, browse/history points | Separate isolated one-restart control completed | The failed normal control attempted one whole wire body. Its ledger records 2 connects, 1 subscription create/apply/delete/remove, 1 Write service (3 values), 5 ReadValue attempts (4 returned, 1 expected canceled fault), 1 revoked Read service, 2 Browse calls, 1 BrowseNext release, 10 HistoryRead calls, and 2 CloseAsync returns. Surgery, revocation, stalled-network recovery and rebuild all occurred before the post-close comparison. No normal cycle is reported as ownership-complete. The independent active-restart control does **not** stand in for a successful four-cycle sequence: it starts one client/subscription/item, abandons browse and history points, stops/reboots the same URI/port/PKI server, and checks the captured old managers/sessions/subscription items and new manager counts. Those checks completed. It returned **exit 2**, explicitly unsupported ownership parity, not a green acceptance result. ## Close and ownership differences — unresolved The first normal smoke returned **exit 1** at the copied zero-closed-list comparison: a retained server Session still had **1 browse / 6 history** points. The server SessionManager already had **zero active sessions**. A separate fixed 250 ms read-only observation saw the same lists; the original failed comparison was still enforced. The first failure artifacts were left untouched. Exact immutable-binary inspection explains the internal difference: - SessionManager.CloseSession removes the session and calls Session.Close. - Session.Close only deletes its diagnostics; it does not clear continuation lists. - Session.Dispose clears both lists and disposes their retained values. - SessionManager.Shutdown/Dispose disposes sessions still in its active dictionary. These are **version-specific diagnostics**, not proof that the original violates the wire close contract, has an unbounded leak, or retains the same objects once all strong diagnostic references are dropped. The adapter deliberately holds the old Session references while checking these fields. The SDK2 zero-list invariant cannot silently become a shared cross-version acceptance contract. The restart control follows the different active-session shutdown path: captured old session/point lists and subscription items became empty. The original validator's cached store **references** remained 1 before and after stop. A cached store reference is not a count of open handles or proof of a leak. Original ApplicationInstance and CertificateValidator expose no Dispose API, and there is no SDK2 CertificateManager/m_disposed/core-store-clear equivalent. The adapter reports those observables as **unsupported**, never as zero. Actual PKI file descriptors can be measured with the same bounded/canonical-path observer as the candidate; those observations cannot invent the missing manager ownership proof. No manual Session.Dispose, store replacement or cache clearing was added to make an assertion pass. The historical partial proof above did not test token/continuation rejection or collection after dropping its diagnostic references. Later, separately reviewed short controls established those semantics and observed the remaining server graph retention after normal shutdown. Their direct point-resource inspection, two weak-reference phases and separate terminal-disposal attribution are recorded in [OWNERSHIP.md](OWNERSHIP.md) and `semantic-evidence.json`. Both ordinary failure results remain intact. Certificate-store/native ownership gaps remain explicit; these findings do not approve a common lifecycle/acceptance protocol. The standalone adapter also lacks the candidate's xUnit/VSTest host; equal process memory overhead is not proven. Earlier original-subset managed/timer growth is an observation, not an approved RSS denominator or a proven SDK1 defect. Resource and operation parity require Astra/root review before any long run; no cap, baseline or acceptance protocol changes are included here. ## Build and short controls `build.py` selects original DLLs from the provided immutable directories (host wins duplicate names), plus Shouldly 4.3.0/assertion dependencies only. It emits SHA-256 provenance and a new external project. No production project/package file is touched. The inventory lists exact types, fields, lifecycle APIs and MVIDs without creating product instances. ```bash python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/build.py \ --host "$originalHost" --client "$originalClient" --assertions "$assertionDlls" \ --output /tmp/original-canonical-build python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/run_smoke.py \ --adapter /tmp/original-canonical-build/bin/Release/net10.0/adapter.dll \ --output /tmp/original-canonical-close --mode normal python3 scripts/validation/opcua-lifecycle-memory/baseline-canonical/run_smoke.py \ --adapter /tmp/original-canonical-build/bin/Release/net10.0/adapter.dll \ --output /tmp/original-canonical-active-restart --mode active-restart ``` Add `--inventory` to build the read-only metadata inventory instead. The runner uses a fresh private directory and a temporary environment credential with the same prefix/byte length as the canonical fixture; it preserves the original child exit. Exit 1 is an observed control failure; exit 2 is operations completed with unsupported ownership parity. Neither is a passing baseline. Normal/restart modes have a hard 120-second bound and no long-run option. The later semantic modes are `--mode semantic-retention` and the separately gated `--mode semantic-terminal-dispose` attribution control. They use the 120-second operation deadline plus bounded independent cleanup and a 180-second outer runner limit. Neither changes normal/active-restart results or authorizes a long run.