89d5d0f2da7d7bba0602176b65b9b644a2954cd3
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
317a4ce410 |
fix: ADR 0050 callback-error precedence, and the honest limits of transaction control
CI / verify (pull_request) Successful in 1m4s
CI / infrastructure-smoke (pull_request) Successful in 38s
CI / migration-smoke (pull_request) Successful in 1m54s
CI / image-build (pull_request) Successful in 22s
CI / deploy-smoke (same-origin) (pull_request) Successful in 51s
CI / deploy-smoke (split) (pull_request) Successful in 1m37s
Narrow contract and implementation adjustments from review. No SDK, manifest, packaging, or Signage work. Callback-error precedence: - A transaction or savepoint callback that rejects keeps its own error, even while sibling work it started is still settling (one failed half of a Promise.all). Core drains that work, rolls back, and never commits or releases. - A callback that resolves with work outstanding (a forgotten await) still fails with MODULE_DATABASE_SCOPE_WORK_OUTSTANDING and rolls back. Transaction-control SQL: - ADR 0050 forbids it through query() as a conformance rule for trusted code, not a security guarantee. Core does not parse SQL, so PostgreSQL has run the statement before Core sees it. Core detects the violation from the command tag or the transaction status, and claims nothing it cannot do: a module's own COMMIT has committed. - After a violation, the transaction's scopes refuse every later statement without sending it (it would otherwise autocommit). Core never commits the transaction itself, and the connection is destroyed rather than reused, for top-level violations too. - The commit and rollback guarantees apply to conforming use. Duplicate object-mode columns: documented as found only after execution, so a mutation can have taken effect; atomicity needs an ABI transaction. Tests prove each of these against PostgreSQL: Promise.all failures in a transaction and a savepoint keep 23505; a forgotten await still rolls back; a module COMMIT's prior work is committed while nothing after it runs; a backend PID changes after every violation; duplicate RETURNING columns persist at the top level and roll back in a transaction. ADR 0050 stays Accepted: the executor conforms to the revised text. Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
|
|
6db50516b7 |
feat: Core's Module Database ABI v1 executor, proven against PostgreSQL (ADR 0050)
CI / verify (pull_request) Successful in 59s
CI / infrastructure-smoke (pull_request) Successful in 27s
CI / migration-smoke (pull_request) Successful in 1m53s
CI / image-build (pull_request) Successful in 23s
CI / deploy-smoke (same-origin) (pull_request) Successful in 41s
CI / deploy-smoke (split) (pull_request) Successful in 1m29s
Implements the first ADR 0050 slice: Core's ORM-neutral executor and its
PostgreSQL evidence. Nothing is wired to modules yet; manifest v8, SDK
adapters, schema metadata, packaging, and Signage are later slices. The
legacy ModuleDatabase facade is unchanged.
Contracts (@ekklesia/contracts, no imports at all):
- ABI version 1; query { text, values, rowMode } returning
{ command, rowCount, rows, fields: [{ name, typeOid }] };
- executor, transaction, and savepoint types with exactly the ABI's
members; parameter and result value types;
- ModuleDatabaseError { code, sqlState? }: eight stable codes, and a
SQLSTATE only when PostgreSQL supplied one; static messages, no cause,
frozen; isModuleDatabaseError recognises it by shape.
Core (createModuleDatabaseExecutor over the API's existing pool):
- one statement per query, enforced by forcing the extended query
protocol so PostgreSQL itself refuses a second statement (42601);
no SQL parsing;
- parameters: null, boolean, number, bigint, string, Uint8Array, and
rectangular arrays of them; everything else refused before sending;
- a Core-owned decoding table that never consults pg's global parser
registry: int8, numeric, temporal, and JSON as exact text; bytea as a
plain Uint8Array; built-in arrays element by element;
- every operation pins TimeZone, DateStyle, IntervalStyle, bytea_output,
and extra_float_digits, then resets with DISCARD ALL before returning
the connection, or destroys it when it cannot be reset cleanly;
- callback-scoped transactions with explicit isolation and access mode;
a COMMIT that PostgreSQL answers with ROLLBACK is reported as failed;
- savepoints named by Core; only the innermost scope is active; scopes
are revoked when their callback settles;
- a callback that settles with work outstanding is rolled back, never
committed or released;
- transaction-control statements sent through query are refused by
command tag and transaction status, and fail the transaction;
- the module ID is carried for Core's attribution only.
Tests:
- 83 integration tests on a database with hostile defaults: queries,
the one-statement rule, parameters, decoding, errors, transactions,
savepoints, forgotten awaits, pool safety after every failure class,
and session state;
- unit tests for encoding, decoding, options, sanitization, and scope
bookkeeping;
- type-level and source checks that no ORM or driver object crosses
the boundary.
ADR 0050 is Accepted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
|