Remove AGENTS.md superseded by CLAUDE.md
The operational rules from AGENTS.md (parity contract, source-update verification matrix, MXAccess/Galaxy analysis sources, dashboard constraints, gateway-doesn't-reattach-orphans) are now in CLAUDE.md. Architecture details remain in gateway.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,481 +0,0 @@
|
||||
# MXAccess Gateway Agent Guide
|
||||
|
||||
Repository: https://gitea.dohertylan.com/dohertj2/mxaccessgw
|
||||
|
||||
This project builds a gateway that gives modern clients full MXAccess parity
|
||||
without requiring those clients to load MXAccess COM, run as x86, or own an STA
|
||||
message pump. Treat the installed MXAccess COM component as the compatibility
|
||||
baseline.
|
||||
|
||||
Toolchain paths, versions, and external analysis locations are recorded in
|
||||
`docs/toolchain-links.md`. Use that file before searching for compilers,
|
||||
runtimes, protobuf tools, MXAccess notes, or Galaxy Repository SQL notes.
|
||||
|
||||
Implementation planning is recorded in `docs/implementation-plan-index.md`.
|
||||
Follow the order there unless the user explicitly reprioritizes: gateway first,
|
||||
MXAccess worker instance second, clients third.
|
||||
|
||||
## Core Contract
|
||||
|
||||
Preserve MXAccess behavior first:
|
||||
|
||||
- public MXAccess command semantics,
|
||||
- native MXAccess event families,
|
||||
- STA/message-pump delivery behavior,
|
||||
- installed-provider quirks,
|
||||
- HRESULT/status/value marshaling,
|
||||
- per-client isolation.
|
||||
|
||||
Do not simplify, normalize, or "fix" MXAccess behavior unless an explicit
|
||||
non-parity mode is being implemented and tested. `MxAsbClient` and managed NMX
|
||||
are future acceleration paths only; they do not define the parity contract.
|
||||
|
||||
## Architecture
|
||||
|
||||
The intended split is:
|
||||
|
||||
```text
|
||||
client
|
||||
-> gRPC over TCP
|
||||
-> .NET 10 x64 gateway
|
||||
-> session manager
|
||||
-> per-session .NET Framework 4.8 x86 worker process
|
||||
-> dedicated STA thread
|
||||
-> MXAccess COM instance
|
||||
-> Windows/COM message pump
|
||||
-> command queue
|
||||
-> event sink
|
||||
```
|
||||
|
||||
The gateway must never instantiate or call MXAccess directly. All MXAccess COM
|
||||
interaction belongs in the worker process on its dedicated STA thread.
|
||||
|
||||
The worker must not host public gRPC. Gateway-to-worker communication should use
|
||||
a small local IPC protocol, with named pipes and protobuf-framed messages as the
|
||||
default design.
|
||||
|
||||
## Runtime Targets
|
||||
|
||||
- Gateway: .NET 10, C#, x64 preferred, ASP.NET Core gRPC.
|
||||
- Worker: .NET Framework 4.8, C#, x86 by default.
|
||||
- Worker IPC: one bidirectional named pipe per worker.
|
||||
- Worker process model: one external client session maps to one worker by
|
||||
default.
|
||||
|
||||
## Style Guides
|
||||
|
||||
Follow the project documentation guide and the language guide for every changed
|
||||
area:
|
||||
|
||||
| Area | Style guide |
|
||||
|------|-------------|
|
||||
| Documentation | `StyleGuide.md` |
|
||||
| Gateway, worker, .NET client, and C# tests | `docs/style-guides/CSharpStyleGuide.md` |
|
||||
| Public gRPC and worker IPC contracts | `docs/style-guides/ProtobufStyleGuide.md` |
|
||||
| Go client | `docs/style-guides/GoStyleGuide.md` |
|
||||
| Rust client | `docs/style-guides/RustStyleGuide.md` |
|
||||
| Python client | `docs/style-guides/PythonStyleGuide.md` |
|
||||
| Java client | `docs/style-guides/JavaStyleGuide.md` |
|
||||
|
||||
When a change crosses languages, apply every affected style guide. Generated
|
||||
code follows its generator output; do not hand-edit it to match handwritten
|
||||
style.
|
||||
|
||||
## Expected Layout
|
||||
|
||||
Prefer this structure unless there is a strong reason to adjust it:
|
||||
|
||||
```text
|
||||
src/MxGateway.Contracts/
|
||||
Protos/
|
||||
mxaccess_gateway.proto
|
||||
mxaccess_worker.proto
|
||||
Generated/
|
||||
|
||||
src/MxGateway.Server/
|
||||
Program.cs
|
||||
Sessions/
|
||||
Workers/
|
||||
Grpc/
|
||||
Dashboard/
|
||||
Metrics/
|
||||
|
||||
src/MxGateway.Worker/
|
||||
Program.cs
|
||||
Ipc/
|
||||
Sta/
|
||||
MxAccess/
|
||||
Conversion/
|
||||
|
||||
src/MxGateway.Tests/
|
||||
contract tests
|
||||
gateway session tests
|
||||
fake worker tests
|
||||
|
||||
src/MxGateway.Worker.Tests/
|
||||
value/status conversion tests
|
||||
STA queue tests
|
||||
|
||||
src/MxGateway.IntegrationTests/
|
||||
optional live MXAccess tests
|
||||
|
||||
clients/dotnet/
|
||||
.NET 10 C# client library, test CLI, and tests
|
||||
|
||||
clients/go/
|
||||
Go client module, test CLI, and tests
|
||||
|
||||
clients/rust/
|
||||
Rust client crate, test CLI, and tests
|
||||
|
||||
clients/python/
|
||||
Python client package, test CLI, and tests
|
||||
|
||||
clients/java/
|
||||
Java client library, test CLI, and tests
|
||||
```
|
||||
|
||||
The contracts project may multi-target, or the `.proto` files may be shared as
|
||||
source inputs to both gateway and worker builds.
|
||||
|
||||
## Public API Shape
|
||||
|
||||
The external API should be session-oriented. Initial rollout should prefer
|
||||
unary `OpenSession`, `CloseSession`, and `Invoke`, plus server-streaming
|
||||
`StreamEvents`. Add a bidirectional `Session` stream after the command and event
|
||||
model is stable.
|
||||
|
||||
Do not compress MXAccess into generic verbs too early. Use a command enum with
|
||||
method-specific payloads so parity can be tested method by method.
|
||||
|
||||
Core MXAccess commands to represent:
|
||||
|
||||
- `Register`
|
||||
- `Unregister`
|
||||
- `AddItem`
|
||||
- `AddItem2`
|
||||
- `RemoveItem`
|
||||
- `Advise`
|
||||
- `UnAdvise`
|
||||
- `AdviseSupervisory`
|
||||
- `AddBufferedItem`
|
||||
- `SetBufferedUpdateInterval`
|
||||
- `Suspend`
|
||||
- `Activate`
|
||||
- `Write`
|
||||
- `Write2`
|
||||
- `WriteSecured`
|
||||
- `WriteSecured2`
|
||||
- `AuthenticateUser`
|
||||
- `ArchestrAUserToId`
|
||||
|
||||
Diagnostics may include `Ping`, `GetSessionState`, `GetWorkerInfo`,
|
||||
`DrainEvents`, and `ShutdownWorker`.
|
||||
|
||||
## Event Requirements
|
||||
|
||||
Represent every public MXAccess event family:
|
||||
|
||||
- `OnDataChange`
|
||||
- `OnWriteComplete`
|
||||
- `OperationComplete`
|
||||
- `OnBufferedDataChange`
|
||||
|
||||
Preserve per-worker event order. The gateway must not reorder events emitted by
|
||||
the same MXAccess instance.
|
||||
|
||||
Event DTOs should carry event family, session id, server handle, item handle,
|
||||
value, quality, timestamp, `MXSTATUS_PROXY[]` equivalent, raw HRESULT/status
|
||||
fields when available, event sequence, worker timestamp, and gateway receive
|
||||
timestamp.
|
||||
|
||||
## Value And Status Rules
|
||||
|
||||
Use a protobuf value union that can represent COM `VARIANT` values and arrays.
|
||||
When a value cannot be losslessly converted, preserve both the best typed
|
||||
projection and enough raw diagnostic metadata to reproduce the case.
|
||||
|
||||
Represent `MXSTATUS_PROXY` explicitly. Do not collapse status arrays into a
|
||||
single success flag.
|
||||
|
||||
Command replies should include protocol status, COM HRESULT if available,
|
||||
MXAccess return values, method-specific out parameters, and status arrays where
|
||||
the MXAccess method emits them.
|
||||
|
||||
## Galaxy Repository SQL Discovery
|
||||
|
||||
Galaxy tags, hierarchy, and attribute details can be queried from the AVEVA /
|
||||
Wonderware System Platform Galaxy Repository SQL Server database. Use this as a
|
||||
discovery and metadata path only; runtime MXAccess parity still belongs to the
|
||||
MXAccess-backed worker unless an explicit non-parity backend is being designed.
|
||||
|
||||
Full notes, schema details, screenshots, and query examples are in:
|
||||
|
||||
```text
|
||||
C:\Users\dohertj2\Desktop\lmxopcua\gr
|
||||
```
|
||||
|
||||
Important files in that notes directory:
|
||||
|
||||
- `connectioninfo.md` - SQL Server connection details and `sqlcmd` usage.
|
||||
- `layout.md` - hierarchy vs `tag_name` relationship.
|
||||
- `build_layout_plan.md` - extraction plan for hierarchy and attributes.
|
||||
- `schema.md` and `ddl/` - Galaxy Repository schema reference.
|
||||
- `queries/hierarchy.sql` - deployed object hierarchy.
|
||||
- `queries/attributes.sql` - user-defined dynamic attributes.
|
||||
- `queries/attributes_extended.sql` - system plus user-defined attributes.
|
||||
- `queries/change_detection.sql` - deployment-change polling via
|
||||
`galaxy.time_of_last_deploy`.
|
||||
|
||||
Current documented connection is SQL Server `localhost`, database `ZB`, Windows
|
||||
Auth. Example:
|
||||
|
||||
```powershell
|
||||
sqlcmd -S localhost -d ZB -E -Q "SELECT time_of_last_deploy FROM galaxy;"
|
||||
```
|
||||
|
||||
Key tables from the notes are `gobject`, `template_definition`,
|
||||
`dynamic_attribute`, `attribute_definition`, `primitive_instance`, and
|
||||
`galaxy`. The hierarchy uses contained names for human-readable browsing, while
|
||||
runtime tag references use globally unique `tag_name` values such as
|
||||
`<tag_name>.<AttributeName>`.
|
||||
|
||||
## MXAccess Analysis Source
|
||||
|
||||
Use the local MXAccess analysis project when answering questions about installed
|
||||
MXAccess classes, interfaces, fields, events, HRESULT/status behavior, value
|
||||
projection, captures, and parity gaps:
|
||||
|
||||
```text
|
||||
C:\Users\dohertj2\Desktop\mxaccess
|
||||
```
|
||||
|
||||
Primary files:
|
||||
|
||||
- `README.md` - overview of available analysis and capture artifacts.
|
||||
- `docs/MXAccess-Public-API.md` - COM class, ProgID, CLSID, method list,
|
||||
event signatures, `MxDataType`, `MxStatus`, and `MXSTATUS_PROXY`.
|
||||
- `docs/MXAccess-Reverse-Engineering.md` - installed runtime path and x86 COM
|
||||
constraints.
|
||||
- `docs/Current-Sprint-State.md` and `docs/DotNet10-Native-Library-Plan.md` -
|
||||
current parity gaps and managed native-client research status.
|
||||
- `src/MxTraceHarness/` - x86 MXAccess harness examples using the real COM
|
||||
interop assembly.
|
||||
- `captures/` and `analysis/` - observed native behavior and generated
|
||||
reverse-engineering artifacts.
|
||||
|
||||
Concrete MXAccess COM target from the analysis:
|
||||
|
||||
- class: `ArchestrA.MxAccess.LMXProxyServerClass`
|
||||
- CLSID: `{C30B52F5-2CB5-4760-AF0A-3A344A7EB5DC}`
|
||||
- ProgID: `LMXProxy.LMXProxyServer.1`
|
||||
- version-independent ProgID: `LMXProxy.LMXProxyServer`
|
||||
- registered server: `C:\Program Files (x86)\ArchestrA\Framework\Bin\LmxProxy.dll`
|
||||
- interop assembly:
|
||||
`C:\Program Files (x86)\ArchestrA\Framework\Bin\ArchestrA.MXAccess.dll`
|
||||
- threading model: `Apartment`
|
||||
|
||||
## Worker Rules
|
||||
|
||||
Each worker owns:
|
||||
|
||||
- one process,
|
||||
- one MXAccess session,
|
||||
- one dedicated STA thread,
|
||||
- one MXAccess COM object,
|
||||
- one inbound command queue,
|
||||
- one outbound event queue.
|
||||
|
||||
All MXAccess operations must run on the STA. A plain blocking queue is not
|
||||
enough for the STA; the STA loop must pump Windows/COM messages and service
|
||||
queued commands.
|
||||
|
||||
Do not block the STA on pipe writes, gRPC calls, or slow consumers. Event
|
||||
handlers should convert event args, enqueue outbound events, and return to
|
||||
pumping messages.
|
||||
|
||||
On graceful shutdown, reject new commands, optionally clean up active MXAccess
|
||||
handles, detach events, release the COM object, uninitialize COM, and exit. If
|
||||
graceful shutdown exceeds the configured timeout, the gateway may kill the
|
||||
worker.
|
||||
|
||||
## IPC Rules
|
||||
|
||||
Default pipe name shape:
|
||||
|
||||
```text
|
||||
mxaccess-gateway-{gatewayProcessId}-{sessionId}
|
||||
```
|
||||
|
||||
Frame messages as:
|
||||
|
||||
```text
|
||||
uint32 little-endian payload_length
|
||||
payload_length bytes protobuf WorkerEnvelope
|
||||
```
|
||||
|
||||
Every envelope should include protocol version, session id, monotonic sender
|
||||
sequence, correlation id, and a typed body. Protocol version mismatch should
|
||||
fail session creation.
|
||||
|
||||
Pipe security should be local-machine only, with ACLs restricted to the gateway
|
||||
identity and launched worker identity. Prefer a per-session nonce handshake.
|
||||
|
||||
## Gateway Rules
|
||||
|
||||
The gateway is responsible for:
|
||||
|
||||
- public TCP/gRPC API,
|
||||
- Blazor Server dashboard using Bootstrap CSS/JS only,
|
||||
- authn/authz when needed,
|
||||
- session creation and teardown,
|
||||
- worker launch and lifecycle management,
|
||||
- command routing,
|
||||
- event streaming,
|
||||
- leases, heartbeats, timeouts, and quotas,
|
||||
- worker kill/restart policy,
|
||||
- metrics and structured logs.
|
||||
|
||||
The gRPC layer should stay thin: validate request, find session, call the
|
||||
session worker client, map worker replies to public replies, and stream events.
|
||||
Keep MXAccess-specific translation logic testable outside the gRPC handlers.
|
||||
|
||||
Dashboard code should also stay thin and read-only for v1. Use a snapshot
|
||||
service over session/worker/metrics state; do not let Razor components mutate
|
||||
gateway sessions or workers directly. Do not use MudBlazor or other Blazor UI
|
||||
component libraries.
|
||||
|
||||
Gateway restart should not try to reattach old workers in the first version.
|
||||
Terminate orphaned workers on startup if that behavior is implemented.
|
||||
|
||||
## Command, Timeout, And Cancellation Semantics
|
||||
|
||||
Command lifecycle:
|
||||
|
||||
```text
|
||||
client gRPC command
|
||||
gateway validates session and payload
|
||||
gateway assigns correlation id
|
||||
gateway writes WorkerCommand to pipe
|
||||
worker queues command to STA
|
||||
STA executes MXAccess method
|
||||
worker captures return/out/status/HRESULT
|
||||
worker sends WorkerCommandReply
|
||||
gateway completes gRPC response
|
||||
```
|
||||
|
||||
Canceling a gRPC call should stop waiting in the gateway, but it cannot safely
|
||||
abort an in-flight COM call on the STA. Hard cancellation means killing the
|
||||
worker process.
|
||||
|
||||
If a command wedges the STA beyond a configured grace period, the gateway should
|
||||
kill the worker and fail the session.
|
||||
|
||||
## Backpressure Policy
|
||||
|
||||
Worker outbound events must use a bounded queue. For parity testing, prefer
|
||||
fail-fast behavior over silent drops. Production coalescing or drop policies
|
||||
must be explicit and observable.
|
||||
|
||||
The gateway should preserve per-session event order, apply backpressure from
|
||||
slow gRPC streams, and disconnect or coalesce only according to an explicit
|
||||
policy.
|
||||
|
||||
## Security And Logging
|
||||
|
||||
Use TLS for remote gRPC when crossing machine boundaries. Authentication may be
|
||||
Windows auth, mTLS, or a deployment-specific token.
|
||||
|
||||
Commands that write, authenticate users, or alter runtime state need explicit
|
||||
authorization design.
|
||||
|
||||
Never log passwords or raw credential values for `AuthenticateUser`,
|
||||
`WriteSecured`, or related secured operations. Do not log full values by
|
||||
default; make value logging opt-in and redacted.
|
||||
|
||||
## Testing Expectations
|
||||
|
||||
Use focused tests for:
|
||||
|
||||
- contract/protobuf compatibility,
|
||||
- gateway session state and worker lifecycle,
|
||||
- gateway behavior with a fake worker,
|
||||
- worker value/status conversion,
|
||||
- STA queue and message-pump behavior.
|
||||
|
||||
Live MXAccess integration tests are optional but should be isolated because they
|
||||
depend on installed COM components and provider behavior.
|
||||
|
||||
Parity tests should compare direct MXAccess behavior against the gateway:
|
||||
|
||||
- return values,
|
||||
- HRESULTs and exceptions,
|
||||
- event sequence,
|
||||
- value projection,
|
||||
- quality/status arrays,
|
||||
- invalid handle behavior,
|
||||
- cross-server handle behavior,
|
||||
- cleanup behavior.
|
||||
|
||||
Known important parity areas:
|
||||
|
||||
- `WriteSecured` may fail before a value-bearing NMX body is emitted.
|
||||
- `WriteSecured2` can succeed in observed native paths.
|
||||
- `OperationComplete` is distinct from write completion.
|
||||
- `OnBufferedDataChange` has a distinct public event shape.
|
||||
- Invalid handles and cross-server handles have specific exception/status
|
||||
behavior.
|
||||
- STA message pumping is required for event delivery.
|
||||
|
||||
## Source Update Workflow
|
||||
|
||||
When source code changes, build the affected component before handing work
|
||||
back. If the change crosses component boundaries, build each affected component
|
||||
instead of relying on a single top-level build.
|
||||
|
||||
Use the native build and test command for each changed area:
|
||||
|
||||
| Changed area | Required verification |
|
||||
|--------------|-----------------------|
|
||||
| Contracts or `.proto` files | regenerate generated code, then build gateway, worker, and every generated client touched by the contract |
|
||||
| Gateway server, sessions, workers, gRPC, dashboard, or metrics | build the .NET 10 gateway project and run affected gateway or fake-worker tests |
|
||||
| Worker IPC, STA, MXAccess, or conversion code | build the .NET Framework 4.8 x86 worker project and run affected worker tests |
|
||||
| Shared test infrastructure | run every test suite that consumes the changed helpers |
|
||||
| .NET client | build the .NET client library, CLI, and tests |
|
||||
| Go client | run Go formatting, build, and tests for the Go module |
|
||||
| Rust client | run Rust formatting, build or check, and tests for the Rust crate |
|
||||
| Python client | run Python formatting or linting if configured, package/build checks, and tests |
|
||||
| Java client | build the Java client library, CLI, and tests |
|
||||
| Integration tests | run them only when the required MXAccess COM component, provider state, and external services are available; otherwise document why they were skipped |
|
||||
|
||||
Update affected documentation in the same change as the source update. This
|
||||
includes `gateway.md`, component design docs under `docs/`, client docs, API
|
||||
contract notes, test instructions, and operational guidance. Documentation must
|
||||
follow `StyleGuide.md`: write technical present-tense prose, explain the reason
|
||||
for non-obvious choices, use exact code names, specify languages on code
|
||||
blocks, use relative links for internal docs, and avoid stale temporary notes.
|
||||
Source code and contract changes must also follow the relevant language guide
|
||||
from the Style Guides section.
|
||||
|
||||
Do not leave documentation describing old behavior after changing public APIs,
|
||||
contracts, configuration, build steps, security behavior, event shapes, value
|
||||
conversion, status mapping, lifecycle rules, or client semantics.
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
Build the smallest end-to-end slice first:
|
||||
|
||||
1. .NET 10 gateway starts.
|
||||
2. Client calls `OpenSession`.
|
||||
3. Gateway launches .NET Framework 4.8 x86 worker.
|
||||
4. Worker creates STA and MXAccess COM object.
|
||||
5. Client calls `Register`.
|
||||
6. Client calls `AddItem`.
|
||||
7. Client calls `Advise`.
|
||||
8. Worker forwards one `OnDataChange` event to the gateway.
|
||||
9. Gateway streams the event to the client.
|
||||
10. Client calls `CloseSession`.
|
||||
11. Gateway shuts down the worker.
|
||||
|
||||
That slice proves the high-risk requirements: process isolation, STA ownership,
|
||||
message pumping, command routing, and event streaming.
|
||||
Reference in New Issue
Block a user