Files

131 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.