From ab54fb013af107d1bf4c5e1b7e17f6694936582c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Wed, 5 Aug 2026 06:08:45 -0400 Subject: [PATCH] docs(plan): Sql Postgres + Oracle provider design + implementation plan --- .../2026-08-05-sql-postgres-oracle-design.md | 127 +++++++++++ docs/plans/2026-08-05-sql-postgres-oracle.md | 210 ++++++++++++++++++ ...26-08-05-sql-postgres-oracle.md.tasks.json | 15 ++ 3 files changed, 352 insertions(+) create mode 100644 docs/plans/2026-08-05-sql-postgres-oracle-design.md create mode 100644 docs/plans/2026-08-05-sql-postgres-oracle.md create mode 100644 docs/plans/2026-08-05-sql-postgres-oracle.md.tasks.json diff --git a/docs/plans/2026-08-05-sql-postgres-oracle-design.md b/docs/plans/2026-08-05-sql-postgres-oracle-design.md new file mode 100644 index 00000000..cd6e25ee --- /dev/null +++ b/docs/plans/2026-08-05-sql-postgres-oracle-design.md @@ -0,0 +1,127 @@ +# Sql driver: Postgres + Oracle providers (managed drivers only) — design + +**Status:** DRAFT 2026-08-05. + +## 1. Problem + +`docs/drivers/Sql.md:15-18`: v1 constructs SQL Server only; `SqlProvider` declares `Postgres`, +`MySql`, `Odbc`, `Oracle` as reserved names that fail the driver. This branch makes **Postgres and +Oracle** working backends via fully managed ADO.NET providers — `Npgsql` and +`Oracle.ManagedDataAccess.Core` — no native/thin-client installs. `MySql`/`Odbc` stay reserved. + +The seam already exists and is proven: `ISqlDialect` exposes an abstract `DbProviderFactory` +(`ISqlDialect.cs:38`), all runtime I/O goes through `DbConnection`/`DbCommand`/`DbParameter` +(`SqlPollReader.cs:379-389`), and the test-only `SqliteDialect` is the standing falsifiability +control that the planner emits dialect SQL rather than T-SQL. The concrete gaps: + +1. **Parameter markers are hardcoded `@`** in two places: `SqlGroupPlanner.cs:21-25` (`@k`/`@w`) + and the catalog path (`SqlServerDialect.cs:56/:61` SQL text + `SqlCatalogLoader.cs:97-113` bind + names). Npgsql happens to accept `@`; Oracle requires `:`. +2. **Two provider gates** reject everything but SqlServer: `CreateDialect` + (`SqlDriverFactoryExtensions.cs:208-215`) and the browser's `DefaultDialectSelector` + (`SqlDriverBrowser.cs:126-127`). +3. **Oracle has no `INFORMATION_SCHEMA`** — catalog SQL must come from `ALL_USERS` / + `ALL_TABLES` + `ALL_VIEWS` / `ALL_TAB_COLUMNS`; liveness is `SELECT 1 FROM DUAL`; row-limit is + `FETCH FIRST 1 ROWS ONLY`. +4. **AdminUI** provider dropdown is single-option (`SqlDriverForm.razor:18-25`). +5. **Packages** — `Npgsql` and `Oracle.ManagedDataAccess.Core` are absent from + `Directory.Packages.props`. + +## 2. Decisions + +- **Both dialects live in the existing `ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect` project**, which + exists precisely because provider packages are unavoidable there (`.Dialect.csproj:20-26`). Its + csproj gains the two `PackageReference`s; consumers get them transitively exactly as + `Microsoft.Data.SqlClient` flows today. No new projects, no `.slnx` churn. +- **Dialect-owned parameter naming.** `ISqlDialect` gains three members with SqlServer-compatible + semantics implemented on every dialect: + - `string ParameterMarker(string baseName)` — the token in SQL text (`"@k0"` / `":k0"`). + - `string ParameterBindName(string baseName)` — the `DbParameter.ParameterName` value (`"@k0"` + for SqlClient/Npgsql; bare `"k0"` for ODP.NET, its documented convention). + - `void ConfigureCommand(DbCommand command)` — default no-op; **OracleDialect sets + `OracleCommand.BindByName = true`** (ODP.NET defaults to bind-by-position, which would silently + mis-bind the planner's multi-parameter `IN` lists — this is the sharpest correctness edge in + the branch). + `SqlGroupPlanner` drops its `@k`/`@w` consts and takes markers from the dialect (bind names ride + `SqlQueryPlan` alongside, keeping `SqlPollReader.cs:398-405`'s copy-don't-derive discipline). + `SqlCatalogLoader` binds `schema`/`table` through the dialect instead of the literal `"@schema"`. + Every command creation site (`SqlPollReader`, `SqlCatalogLoader`, `SqlDriverProbe`, + `SqlBrowseSession`) calls `ConfigureCommand` after `CreateCommand`. +- **`PostgresDialect`** (`NpgsqlFactory.Instance`): `@` markers; `"`-quoting with `""` escape, max + identifier 63; `LivenessSql = "SELECT 1"`; limit prefix `""` / suffix `" LIMIT 1"`; + `INFORMATION_SCHEMA` catalog SQL with `pg_catalog`/`information_schema` schemas excluded and + `DefaultSchemaSql = "SELECT current_schema()"`; type map over Postgres names (`integer`, + `bigint`, `smallint`, `real`, `double precision`, `numeric`, `boolean`, `text`, + `character varying`, `timestamp*`, `uuid`, `bytea`, …). +- **`OracleDialect`** (`OracleClientFactory.Instance`): `:` markers, bare bind names, BindByName; + `"`-quoting with `""` escape, max identifier 128 (declares Oracle 12.2+ as the supported floor — + `FETCH FIRST` needs 12c anyway); `LivenessSql = "SELECT 1 FROM DUAL"`; limit suffix + `" FETCH FIRST 1 ROWS ONLY"`; catalog SQL from `ALL_USERS` (schemas), + `ALL_TABLES` ∪ `ALL_VIEWS` (tables — matching `INFORMATION_SCHEMA.TABLES`' view inclusion), + `ALL_TAB_COLUMNS` (columns), `DefaultSchemaSql = "SELECT sys_context('userenv','current_schema') FROM dual"`; + type map over Oracle names — **`NUMBER` maps to `Float64`** (precision/scale are not visible to + `MapColumnType(string)`; the per-tag `DeclaredType` override is the documented escape hatch), + plus `BINARY_DOUBLE`/`BINARY_FLOAT`, `VARCHAR2`/`NVARCHAR2`/`CHAR`/`NCHAR`, `DATE`/`TIMESTAMP*`, + `CLOB`, `RAW`. +- **Case-folding rides the existing catalog gate.** Postgres lower-cases and Oracle upper-cases + unquoted identifiers; `SqlCatalogGate` already resolves every authored identifier to the + catalog's own spelling, so authored `widgets` binds to Oracle `WIDGETS`. The implementer verifies + the gate's comparison is case-insensitive and pins it per-dialect with tests. +- **Gates widen to exactly these two.** `CreateDialect` and `DefaultDialectSelector` add + `Postgres`/`Oracle`; `MySql`/`Odbc` keep the existing loud rejection (tests updated: the + "Postgres is rejected" assertions at `SqlDriverFactoryTests.cs:133-135` and + `SqlDriverBrowserTests.cs:188-196` flip to `MySql` as the still-reserved control). +- **AdminUI**: `SqlDriverForm.razor` dropdown gains `Postgres` and `Oracle` options; helper text + updated. No other UI change — Sql stays a single-connection driver with no device form + (`DriverFormMaps.cs:81-89`), `connectionStringRef` + `Sql__ConnectionStrings__` env + resolution is provider-agnostic already, and the `SqlConnectionStringPersisted` deploy gate needs + no change (it keys on the literal `connectionString` key, not the provider). +- **Packages**: `Directory.Packages.props` gains `Npgsql` (between `MQTTnet` and `Novell.*`) and + `Oracle.ManagedDataAccess.Core` (between the `OPCFoundation.*` block and `OpenTelemetry`), latest + stable versions on the feed. `CentralPackageTransitivePinningEnabled` is OFF by design, so if + restore surfaces an NU1903 in either package's transitive tree, the fix is the established + surgical direct-pin pattern (`Directory.Packages.props:118-145`), never a suppress. +- **Credential discipline holds automatically**: `SqlDriver` already renders endpoints via the + provider-neutral `DbConnectionStringBuilder` reading `host`/`data source` keys + (`SqlDriver.cs:47-51, :952-968`) and logs exception types only. + +## 3. Out of scope + +- `MySql`/`Odbc` dialects, write support (`IWritable` stays structurally absent, `allowWrites` + stays inert), the deferred `Query` tag model, connection pooling knobs, and Kerberos/wallet + auth schemes (connection-string concerns, owned by the operator's `Sql__ConnectionStrings__*` + env value). + +## 4. Testing + +1. **Unit.** + - `PostgresDialectTests` / `OracleDialectTests`: quoting (escape doubling, length caps, + control-char rejection — mirror `SqlServerDialectTests`), liveness/limit/catalog SQL text, + type maps, markers/bind names, and `ConfigureCommand` (Oracle: a real `OracleCommand` has + `BindByName == true` after the call; others: no-op). + - Planner/loader marker plumbing: a colon-marker fake dialect (or `SqliteDialect` extended) + proves the planner emits dialect markers and the reader binds the dialect's bind names — + the falsifiability control for gap 1. + - Gate tests updated as in §2 (MySql becomes the rejected control). + - The SQLite-backed read/poll suites keep running unchanged — they prove provider-agnosticism. +2. **Integration (env-gated, skip-clean, docker).** Two new fixtures under + `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/Docker/`, deployed to the shared + docker host per the established pattern: + - `postgres:17-alpine` (project `otopcua-sql-pg`, host port 15432) — env + `SQL_PG_ENDPOINT`/`SQL_PG_USER`/`SQL_PG_PASSWORD`. + - `gvenzl/oracle-free:23-slim` (project `otopcua-sql-oracle`, host port 11521) — env + `SQL_ORACLE_ENDPOINT`/`SQL_ORACLE_USER`/`SQL_ORACLE_PASSWORD`. + Each suite: initialize (liveness + catalog load), KeyValue + WideRow reads against a seeded + schema, catalog-gate case-folding leg (authored lower-case vs Oracle upper-case catalog), + browse-session schema/table/column walk. The integration csproj keeps its no-direct-provider- + reference discipline (`.IntegrationTests.csproj:29-33`) — packages arrive transitively. +3. **Live `/run` chrome gate — DEFERRED, run separately by the operator.** AdminUI legs (provider + dropdown authoring, Test-connect, `/raw` browse against the Postgres and Oracle fixtures, + browse-commit of a column). The executing session must NOT run this; it hands the operator the + command at the end. + +## 5. Bookkeeping + +`docs/drivers/Sql.md` (§providers table, config table, browse section), +`SqlProvider.cs` doc comments ("reserved for P2" → supported), the tracking doc's Sql row, and +`docs/research/drivers/sql-poll.md` stays as-is (research is a record, not living doc). diff --git a/docs/plans/2026-08-05-sql-postgres-oracle.md b/docs/plans/2026-08-05-sql-postgres-oracle.md new file mode 100644 index 00000000..5ec6f518 --- /dev/null +++ b/docs/plans/2026-08-05-sql-postgres-oracle.md @@ -0,0 +1,210 @@ +# Sql Postgres + Oracle providers Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development +> (opus implementers per user instruction). + +**Goal:** Make `SqlProvider.Postgres` and `SqlProvider.Oracle` working backends via managed +ADO.NET providers (Npgsql, Oracle.ManagedDataAccess.Core). + +**Architecture:** See `docs/plans/2026-08-05-sql-postgres-oracle-design.md` (this branch). Two new +dialects in the existing `.Dialect` project; dialect-owned parameter markers/bind-names + +`ConfigureCommand` (Oracle BindByName); provider gates widened; AdminUI dropdown; env-gated docker +integration suites. Chrome/AdminUI live gate is DEFERRED to the operator. + +**Tech Stack:** .NET 10, central package management (`Directory.Packages.props`), xUnit v3 + +Shouldly, SQLite falsifiability control. + +**Worktree:** `/Users/dohertj2/Desktop/OtOpcUa/.claude/worktrees/sql-multi-provider` +(branch `feature/sql-multi-provider`). All paths below relative to it. + +--- + +### Task 0: Package pins + +**Classification:** trivial +**Estimated implement time:** ~3 min +**Parallelizable with:** Task 1, Task 5 + +**Files:** +- Modify: `Directory.Packages.props` +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect.csproj` + +Add `PackageVersion` entries for `Npgsql` (alphabetical slot after `MQTTnet`) and +`Oracle.ManagedDataAccess.Core` (after the `OPCFoundation.*` block) — latest stable on the feed +(`dotnet package search --exact-match` to confirm; Npgsql 9.x line, ODP.NET Core 23.x line; +both must target net8.0+). Add both `PackageReference`s (version-less) to the `.Dialect` csproj +under the existing `Microsoft.Data.SqlClient` reference, extending that ItemGroup's +rationale comment. `dotnet restore ZB.MOM.WW.OtOpcUa.slnx` must come back clean WITH the audit +active — if NU1903 fires on a transitive dep, apply the surgical direct-pin pattern documented at +`Directory.Packages.props:118-145`, never a suppress. Commit +`build(sql): pin Npgsql + Oracle.ManagedDataAccess.Core (managed providers only)`. + +### Task 1: Dialect-owned parameter naming + `ConfigureCommand` + +**Classification:** high-risk +**Estimated implement time:** ~5 min +**Parallelizable with:** Task 0, Task 5 + +**Files:** +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect/ISqlDialect.cs` +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect/SqlServerDialect.cs` +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlGroupPlanner.cs` (:21-25 consts, + :143, :151, :188) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlQueryPlan.cs` (bind names alongside + markers if the plan record needs both) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlPollReader.cs` (:379-405 — ConfigureCommand + call + bind-name copy) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlCatalogLoader.cs` (:97-113, `Bind` :181-188) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriverProbe.cs` (ConfigureCommand) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser/SqlBrowseSession.cs` (ConfigureCommand) +- Modify: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/SqliteDialect.cs` (implement new + members; keep linked copy in Browser.Tests compiling) +- Test: `SqlGroupPlannerTests.cs`, `SqlPollReaderTests.cs`, `SqlCatalogGateDriverTests.cs` (+ a + colon-marker fake dialect in the test tree) + +Per design §2 bullet 2. `ISqlDialect` gains `ParameterMarker(string baseName)`, +`ParameterBindName(string baseName)`, `ConfigureCommand(DbCommand)`; SqlServerDialect returns +`"@"+name` / `"@"+name` / no-op — **behaviour today must be byte-identical** (existing planner +SQL-text assertions must pass untouched; if any needs editing, that is a regression, stop). +Planner takes markers from its dialect; catalog SQL text and loader bind names route through the +dialect so the two can never disagree. TDD: the colon-marker fake proves markers flow (observed +red against the `@k` consts first). Every `CreateCommand` site calls `ConfigureCommand` +immediately after creation. Commit +`refactor(sql): dialect-owned parameter markers, bind names and command configuration`. + +### Task 2: `PostgresDialect` + +**Classification:** standard +**Estimated implement time:** ~5 min +**Parallelizable with:** Task 3, Task 5 + +**Files:** +- Create: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect/PostgresDialect.cs` +- Test: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/PostgresDialectTests.cs` (new) + +Per design §2 Postgres bullet. `Factory => NpgsqlFactory.Instance`; `Provider => +SqlProvider.Postgres`; `@` markers/bind names; `"`-quoting `""`-escaped, MaxIdentifierLength 63, +same control-char/Unicode-Format rejection discipline as `SqlServerDialect.QuoteIdentifier` +(:88-114); `LivenessSql "SELECT 1"`; limit `""`/`" LIMIT 1"`; catalog SQL over +`information_schema.schemata/tables/columns` excluding `pg_catalog` + `information_schema`, +`DefaultSchemaSql "SELECT current_schema()"` (parameter markers in catalog SQL via the Task 1 +members); `MapColumnType` per design (unknown → the same fallback SqlServerDialect uses — read it +first). Tests mirror `SqlServerDialectTests` structure: quoting theory, SQL text, type-map theory, +marker members. Commit `feat(sql): PostgresDialect (Npgsql)`. + +### Task 3: `OracleDialect` + +**Classification:** standard +**Estimated implement time:** ~5 min +**Parallelizable with:** Task 2, Task 5 + +**Files:** +- Create: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Dialect/OracleDialect.cs` +- Test: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Tests/OracleDialectTests.cs` (new) + +Per design §2 Oracle bullet. `OracleClientFactory.Instance`; `:` markers, **bare** bind names; +`ConfigureCommand` casts to `OracleCommand` and sets `BindByName = true` (the test constructs a +real `OracleCommand` and asserts the flag — this is the load-bearing test of the whole branch; +also assert a foreign `DbCommand` passes through untouched). Quoting `"`/`""`, max 128; +`LivenessSql "SELECT 1 FROM DUAL"`; limit `""`/`" FETCH FIRST 1 ROWS ONLY"`; catalog SQL: +`ALL_USERS` schemas, `ALL_TABLES` ∪ `ALL_VIEWS` tables, `ALL_TAB_COLUMNS` columns, +`sys_context('userenv','current_schema')` default schema — markers via `:schema`/`:table` through +the Task 1 members; `MapColumnType`: `NUMBER`→Float64 (doc the DeclaredType escape hatch in the +XML doc comment), `BINARY_DOUBLE`/`BINARY_FLOAT`, `VARCHAR2`/`NVARCHAR2`/`CHAR`/`NCHAR`/`CLOB`, +`DATE`/`TIMESTAMP*`, `RAW`, plus `FLOAT`/`INTEGER` synonyms. Commit +`feat(sql): OracleDialect (ODP.NET managed, BindByName)`. + +### Task 4: Widen the provider gates + +**Classification:** small +**Estimated implement time:** ~4 min +**Parallelizable with:** none + +**Files:** +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql/SqlDriverFactoryExtensions.cs` (:208-215) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Browser/SqlDriverBrowser.cs` (:126-127, + :146-151 comment) +- Modify: `src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.Contracts/SqlProvider.cs` (doc comments) +- Test: `SqlDriverFactoryTests.cs` (:133-135 → MySql as rejected control; new: Postgres/Oracle + construct with their dialects), `SqlDriverBrowserTests.cs` (:188-196 same flip) + +TDD: red first — author `{"provider":"Postgres"}` and assert construction reaches the +connection-string resolver (or dialect type assertion via the binding) instead of the +not-available throw. Rejection message updated to name the three supported providers. Commit +`feat(sql): construct Postgres and Oracle providers; MySql/Odbc stay reserved`. + +### Task 5: AdminUI dropdown + docs + +**Classification:** small +**Estimated implement time:** ~4 min +**Parallelizable with:** Task 1, Task 2, Task 3 + +**Files:** +- Modify: `src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Components/Shared/Drivers/Forms/SqlDriverForm.razor` + (:18-25) +- Modify: `docs/drivers/Sql.md` (:15-18, :96 provider row, browse section) +- Test: `tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/` — extend the existing Sql driver-form + model test file (find `SqlDriverFormModelTests` or nearest equivalent) with round-trip of + `provider: Postgres` / `Oracle` + +Dropdown gains `Postgres` + `Oracle` options; helper text → "SQL Server, PostgreSQL and Oracle +are supported; MySql/Odbc are reserved." Sql.md rewritten per design §5 including the Oracle +`NUMBER`→Float64 note and the 12.2+ floor. Commit +`feat(sql-adminui): Postgres + Oracle in the provider dropdown; docs`. + +### Task 6: Integration fixtures + env-gated suites + +**Classification:** standard +**Estimated implement time:** ~5 min +**Parallelizable with:** none + +**Files:** +- Create: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/Docker/postgres/docker-compose.yml` + + `seed-pg.sql` +- Create: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/Docker/oracle/docker-compose.yml` + + `seed-oracle.sql` +- Create: `tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/PostgresReadTests.cs`, + `OracleReadTests.cs` + +Per design §4.2. Compose shapes mirror the existing mssql fixture (`Docker/docker-compose.yml`: +project label `project: lmxopcua`, one-shot seed service, healthcheck): `postgres:17-alpine` +port `15432:5432` env `POSTGRES_PASSWORD=${SQL_PG_PASSWORD}`; `gvenzl/oracle-free:23-slim` port +`11521:1521` env `ORACLE_PASSWORD=${SQL_ORACLE_PASSWORD}` (gvenzl images take `APP_USER`/ +`APP_USER_PASSWORD` for a non-SYS user — use `otopcua`/the password env). Seeds create one +KeyValue table + one WideRow table with a timestamp column, plus (Oracle) a deliberately +UPPER-CASE table authored lower-case in the test to pin catalog case-folding. Test classes follow +`SqlServerReadTests`/`SqlBlackholeTimeoutTests` conventions exactly: env vars +`SQL_PG_ENDPOINT`/`SQL_PG_USER`/`SQL_PG_PASSWORD`, `SQL_ORACLE_ENDPOINT`/`SQL_ORACLE_USER`/ +`SQL_ORACLE_PASSWORD`; `Assert.SkipWhen` absent; connection strings via each provider's builder +obtained FROM THE FACTORY (`dialect.Factory.CreateConnectionStringBuilder()`) so the csproj keeps +its no-direct-provider-reference discipline (`.IntegrationTests.csproj:29-33`). Legs per suite: +initialize (liveness + catalog), KeyValue read, WideRow topByTimestamp read, browse-session +walk, case-folding leg. Commit `test(sql): env-gated Postgres + Oracle integration suites`. + +### Task 7: Full-suite verification + bookkeeping + +**Classification:** small +**Estimated implement time:** ~5 min +**Parallelizable with:** none + +`dotnet build ZB.MOM.WW.OtOpcUa.slnx` (0 errors), full Sql unit + Browser.Tests + AdminUI.Tests + +Configuration.Tests (the SqlCredentialGuard suites must stay green), integration suites compile + +skip clean offline. Update `docs/plans/2026-07-24-driver-expansion-tracking.md` Sql row/deferral +note. Commit. + +### Task 8: Fixture deploy + live integration run (main session) + +**Classification:** standard +**Estimated implement time:** ~15 min (main session, after merge) + +Main session, not a subagent: rsync the two fixture dirs to `dohertj2@10.100.0.35:/opt/otopcua-sql-pg/` +and `/opt/otopcua-sql-oracle/`, `docker compose up -d` each over ssh, then run +`dotnet test --filter "FullyQualifiedName~PostgresRead|FullyQualifiedName~OracleRead"` with the +env vars set. (oracle-free first pull is ~1.5 GB — allow time; if the host can't take it, record +the suite as authored-but-not-yet-run.) Record results in the design doc §Outcome. + +**Chrome/AdminUI live gate — DEFERRED to the operator.** After merge the operator runs, in a +chrome-capable session: +`/superpowers-extended-cc:executing-plans docs/plans/2026-08-05-sql-postgres-oracle.md` — gate +section: provider dropdown authoring, Test-connect, `/raw` browse + browse-commit against both +fixtures at `http://localhost:9200`. diff --git a/docs/plans/2026-08-05-sql-postgres-oracle.md.tasks.json b/docs/plans/2026-08-05-sql-postgres-oracle.md.tasks.json new file mode 100644 index 00000000..c11b5396 --- /dev/null +++ b/docs/plans/2026-08-05-sql-postgres-oracle.md.tasks.json @@ -0,0 +1,15 @@ +{ + "planPath": "docs/plans/2026-08-05-sql-postgres-oracle.md", + "tasks": [ + { "id": 0, "subject": "Task 0: Package pins", "status": "pending" }, + { "id": 1, "subject": "Task 1: Dialect-owned parameter naming + ConfigureCommand", "status": "pending" }, + { "id": 2, "subject": "Task 2: PostgresDialect", "status": "pending", "blockedBy": [0, 1] }, + { "id": 3, "subject": "Task 3: OracleDialect", "status": "pending", "blockedBy": [0, 1] }, + { "id": 4, "subject": "Task 4: Widen the provider gates", "status": "pending", "blockedBy": [2, 3] }, + { "id": 5, "subject": "Task 5: AdminUI dropdown + docs", "status": "pending" }, + { "id": 6, "subject": "Task 6: Integration fixtures + env-gated suites", "status": "pending", "blockedBy": [4] }, + { "id": 7, "subject": "Task 7: Full-suite verification + bookkeeping", "status": "pending", "blockedBy": [5, 6] }, + { "id": 8, "subject": "Task 8: Fixture deploy + live integration run (main session); chrome gate deferred to operator", "status": "pending", "blockedBy": [7] } + ], + "lastUpdated": "2026-08-05T00:00:00Z" +}