Skip to content

Add schema-bound transactional runtime mutations (PR B) [skip ci] - #113

Merged
Dastari merged 10 commits into
mainfrom
feat/runtime-mutations-20261007
Oct 12, 2026
Merged

Dastari merged 10 commits into
mainfrom
feat/runtime-mutations-20261007

Conversation

@Dastari

@Dastari Dastari commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Runtime-schema hosts can create, patch and delete one exact-key record inside an ORM state-machine transaction, with explicit host authority and atomic typed repository journal writes. Dropping a runtime mutation or guarded journal hook poisons the transaction: catching the timeout and returning Ok cannot commit staged work.

Dependency and review checkpoint

B1 correction and exact-head verification

Digibase's fresh-database SQLite suffix-CAS reproduction failed before this correction: the four-bind budget invoked preimage authorization before the five-bind UPDATE was rejected. The regression reproduces that failure without schema drift or a host workaround.

RuntimePredicate::mutation_bind_count now takes the selected backend and counts values emitted by the existing predicate renderer. Both CAS and authority preflight paths use it, including the key-plus-authority read budget. This adds no separate operator-count table and does not count repeated PostgreSQL SQL occurrences as distinct native binding slots. Public APIs, schema/hash/storage semantics and package versions remain unchanged.

Verification at 04a1216aff86141ff6500b1dd0bf9adb4b0c6447:

  • SQLite: 32 library + 20 runtime mutation tests passed, zero ignored.
  • PostgreSQL: 31 library + 21 runtime mutation tests passed, zero ignored; real execution uses labelled disposable servers with verified ownership before cleanup.
  • New valid-database cases exercise CAS suffixes, authority suffixes, nested AND/OR/NOT, and both combined. SQLite executes 12 below/exact/above-budget cases: rejection occurs before preimage authorization, and score remains 7 on rejection or becomes 8 on success.
  • PostgreSQL executes four below-budget rejection cases now. The eight exact/above-budget successful string/CAS cases are deliberately not executed pending reviewed Fix runtime string predicates and PostgreSQL native parameter binding #112/A/B reconciliation; they are not passing evidence. A shared-renderer unit test checks ordinary/empty/Unicode literals, nested native-slot reuse and offsets on both dialects. Repeated PostgreSQL $n references consume one slot, not one per SQL occurrence.
  • Warnings-denied scoped Clippy/Rustdoc on SQLite/PostgreSQL/MSSQL, formatting, governed documentation, inventory, dependency architecture, package release policy, release state/manifest checks: passed. MSSQL remains compilation-only; no live runtime writes.
  • Strict source compatibility against accepted A: 223 passed, 30 inapplicable checks skipped.
  • Hosted checks: not run. Actions remain paused; commit uses [skip ci]. No owner merge/publication.

Focused commands (with CARGO_BUILD_JOBS=2, disk scratch and sequential backends):

for backend in sqlite postgres; do
  cargo test -p graphql-orm --locked --no-default-features --features "$backend" \
    --lib --test runtime_mutations -- --test-threads=1
done

The Clippy/Rustdoc, format, documentation, dependency, release-policy and compatibility commands below were rerun for B1. Prior broader migration/static/external-consumer evidence remains labelled with its earlier head. C's predecessor acceptance remains gated on B1 re-review. #112 does not resolve B1's accounting defect and is not copied into this branch.

Public contract

ValidatedRuntimeSchema builds runtime_key, runtime_create_request, runtime_update_request and runtime_delete_request. Inputs are owned and schema-bound; omitted fields, SQL NULL and JSON null remain distinct. Required non-generated keys, composite ordering, immutable update keys, generated UUIDs, append-only restrictions and resource bounds are checked. Unsupported generation and modifying incoming delete actions reject explicitly.

MutationContext::mutate_runtime requires RuntimeMutationEnvironment, RuntimeWriteAuthority<B> and TransactionMode::StateMachine. It reuses runtime handles, predicates, values, projections, row decoding and the existing pinned backend transaction. No raw SQL escape hatch or second canonical IR is added.

The authority separately approves complete intent, authoritative preimage and actual staged result. Host policy-only revisions are checked in intent against the host's pinned policy generation; ORM fingerprints are separate. Internal projections union policy fields, keys, changed/CAS fields and requested outputs but never escape in the effect. SQL-renderable row restrictions apply to preimages and actual results. Missing/invisible/preimage-denied rows return not_found; CAS conflicts are distinguished only after authorizing the preimage. Return grants must exactly match the requested projection, including None.

The operation guard begins before the first await. Failure/drop poisons the context; unfinished/forgotten work separately blocks commit. The outer runner and commit_and_emit check both invariants. run_runtime_before_commit separately guards host hooks and their repository journal writes. Success remains pending until the outer runner commits; the engine emits no events. TransactionError::commit_outcome conservatively retains unknown outcomes. Static-only inner-timeout behavior is unchanged.

A's environment gains a private backend binding and a read-only accessor; its physical target, planner, hash/storage semantics and approved FK/DateTime corrections are preserved. Third-party backend traits gain no required methods; RuntimeMutationBackend is additive. MSSQL reports unsupported capability.

Executable consumer example

crates/graphql-orm/examples/runtime_mutation_journal.rs runs create, CAS update and delete with typed journal inserts on each backend. SQLite uses owned memory; PostgreSQL starts an identity-verified disposable Docker database. The same example runs through tests/fixtures/repository-aggregate-consumer with no direct async-graphql dependency.

let request = schema.runtime_update_request(
    schema.runtime_key(&collection, &[(id, RuntimeValue::Integer(7))])?,
    &[(status, RuntimeValue::String("published".into()))],
    Some(expected_draft), Some(public_projection), RuntimeMutationLimits::default(),
)?;
let committed = database.transaction_with_auth(
    TransactionMode::StateMachine, db_auth.as_ref(),
    |tx| Box::pin(async move {
        let pending = tx.mutate_runtime(&environment, &request, &authority).await?;
        tx.run_runtime_before_commit(&journal_hook, &pending).await?;
        Ok(pending)
    }),
).await?;

Prior B verification at a3fc34c76e9e62f34f989d3bb9767493fc339035

  • SQLite combined migrations/mutations/static regressions: 136 passed, 0 ignored; 19 runtime mutation tests passed in the final SQLite rerun, including the additional policy-revision case.
  • PostgreSQL combined lane: 128 passed, 0 ignored, including 20 runtime mutation tests on identity-verified disposable PostgreSQL servers.
  • Existing SQLite static transaction/context/bounded/composite regressions: 10 passed.
  • Public create/CAS-update/delete/journal example and the external framework-neutral consumer example: compiled and executed successfully on both SQLite and PostgreSQL.
  • Legacy external migration API fixtures: 1 passed per SQLite/PostgreSQL lane.
  • SQLite/PostgreSQL/MSSQL compile, scoped warnings-denied Clippy and Rustdoc, dependency trees, SQLite+MSSQL coexistence: passed. MSSQL runtime mutation execution was not run and is unsupported.
  • Formatting, documentation, generated inventory, dependency architecture, package release policy, release state and manifest checks: passed.
  • Strict patch-level source compatibility against accepted A: 223 passed, 30 inapplicable checks skipped; no source break.
  • Hosted checks: not run; Actions remain paused. No merge or release publication.

The first SQLite development run exposed a nullable record-state handling mistake in B, corrected before these successful runs. PostgreSQL string-prefix/suffix CAS inherits the pre-#112 renderer on this branch; combined string/CAS verification must follow the owner-controlled #112/A/B reconciliation before adoption. No fix is copied into B.

Commands use CARGO_BUILD_JOBS=2, Rust 1.97.1, disk-backed scratch/targets and sequential backend lanes. No workspace --all-features, ambient application database, hosted workflow dispatch or release publication was used.

cargo test -p graphql-orm --locked --no-default-features --features sqlite --test runtime_mutations -- --test-threads=1
cargo test -p graphql-orm --locked --no-default-features --features sqlite \
  --test transaction_api --test mutation_context_reads \
  --test bounded_mutation_sentinels --test composite_mutations -- --test-threads=1
for backend in sqlite postgres; do
  cargo test -p graphql-orm --locked --no-default-features --features "$backend" \
    --lib --test runtime_mutations --test runtime_migrations --test runtime_schema_ir \
    --test migration_planner --test migration_apply --test legacy_migration_history \
    --test host_timestamps --test portable_group_pages --test projection_visibility \
    --test repository_counts -- --include-ignored --test-threads=1
  cargo run -p graphql-orm --locked --no-default-features --features "$backend" \
    --example runtime_mutation_journal
  cargo run --manifest-path crates/graphql-orm/tests/fixtures/repository-aggregate-consumer/Cargo.toml \
    --locked --no-default-features --features "$backend" --example runtime_mutation_journal
  cargo test --manifest-path crates/graphql-orm/tests/fixtures/migration-api-compatibility/Cargo.toml \
    --locked --no-default-features --features "$backend"
done
for backend in sqlite postgres mssql; do
  cargo check -p graphql-orm -p graphql-orm-macros --locked --no-default-features --features "$backend"
  cargo clippy -p graphql-orm -p graphql-orm-macros --locked --no-default-features \
    --features "$backend" --lib --test runtime_mutations --example runtime_mutation_journal -- -D warnings
  RUSTDOCFLAGS=-Dwarnings cargo doc -p graphql-orm -p graphql-orm-macros \
    --locked --no-default-features --features "$backend" --no-deps
  cargo tree -p graphql-orm --locked --no-default-features --features "$backend"
done
cargo check -p graphql-orm --locked --no-default-features --features sqlite,mssql
cargo fmt --all -- --check
python3 scripts/check-documentation.py --base f2665ce68ed7fdbccb272cb8a783b6379ae5fd82
python3 scripts/generate-workspace-inventory.py --check
scripts/check-workspace-dependencies.sh
scripts/check-package-release-policy.sh f2665ce68ed7fdbccb272cb8a783b6379ae5fd82
python3 scripts/check-release-state.py
scripts/check-release-manifest.sh
cargo semver-checks -p graphql-orm --manifest-path crates/graphql-orm/Cargo.toml \
  --baseline-rev f2665ce68ed7fdbccb272cb8a783b6379ae5fd82 --default-features --release-type patch

Coverage includes staged-DML result-authorization cancellation, staged-journal hook cancellation, cancellation before DML, whole-runner cancellation, caught denial followed by successful work, forgotten guard state, connection reuse and suppressed events. Tests also cover current host policy revisions, field/row/output denial, bound-value limits, composite keys, nullable omission/NULL, generated UUIDs, JSON-null distinction, exact integer/date/byte values, defaults, CAS concurrency, cascade rejection/RESTRICT, PostgreSQL RLS and transaction-local auth. A's no-op, FK, RLS, legacy DateTime/Integer and rollback regressions remain selected.

Compatibility and limitations

No data migration, static API/SDL/cursor or timestamp-unit changes. Ordinary repository consumers remain framework-neutral. Bulk/nested writes and upserts are outside this slice. Runtime mutation support is SQLite/PostgreSQL; MSSQL evidence is compile-only, not live mutation execution. Driver decoding allocates complete selected values before applying record byte limits; limits are not streaming memory guarantees.

The host must pin the physical environment and complete policy revision under its external-DDL/schema fence. A certificate is not a lease protecting against arbitrary external DDL. Invoke hooks through the guarded wrapper; propagate errors from direct journal writes. Static-only transactions retain their prior cancellation limitation. Unknown commit outcomes require host reconciliation, not blind retries; no durable replay guarantee is claimed.

Downstream handoff

After owner merge/release of #112 and final A/B reconciliation/review, pin the exact reviewed immutable source with aligned ORM/macros and default-features = false, selecting sqlite or postgres. Compose runtime/system metadata, verify/apply A, retain its environment with the host schema generation, supply explicit runtime authority and current policy-revision checks, and execute through auth-aware state-machine transactions. Use guarded hooks or propagated typed journal inserts; publish success only after the runner returns. Run downstream SQLite/PostgreSQL contracts before adoption. No release identity is assigned by this PR.

Documentation impact

  • Documentation updated
  • No documentation impact

New canonical runtime-mutation reference and index link, compiled/external host example, active A–D checkpoint, changelog/migration notes, aligned manifests/fixture locks and generated inventory. Future CI/release test selections include the new suite; Actions remain paused and all commits use [skip ci].

Dastari and others added 2 commits October 11, 2026 17:03
Owner-authorized A reconciliation. Exact tested A tree retained; final combined source accepted at 78ad237. Actions remain paused.
Exact tested B reconciliation tree 77aaef7; no source deviation from accepted combined source.
@Dastari
Dastari changed the base branch from feat/runtime-migration-targets-20260930 to main October 12, 2026 00:04
@Dastari
Dastari merged commit 747c71e into main Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant