Skip to content

(TML-2397) M4 — Contract spaces: pgvector migration + monorepo example#441

Merged
wmadden merged 15 commits into
mainfrom
tml-2397-pgvector-contract-space
May 10, 2026
Merged

(TML-2397) M4 — Contract spaces: pgvector migration + monorepo example#441
wmadden merged 15 commits into
mainfrom
tml-2397-pgvector-contract-space

Conversation

@wmadden
Copy link
Copy Markdown
Contributor

@wmadden wmadden commented May 8, 2026

Summary

Milestone 4 of TML-2397 — Contract spaces. Stacked on top of #439 (M3).

Migrates pgvector to the contract-space mechanism (the second real consumer after cipherstash) and ships a multi-extension monorepo example exercising three contract spaces (audit + feature-flags + app) in one transaction. With both pgvector and cipherstash on the new mechanism, M5 will be free to remove the legacy databaseDependencies field from the framework entirely.

What lands (7 commits)

T4.1 + T4.2 — pgvector contract-space migration (1 commit)

  • ac4b28fce migrate to contract-space mechanism
    • pgvectorContract declares the vector parameterised native type (ships natively as StorageTypeInstance — no IR vocabulary expansion needed).
    • pgvectorBaselineMigration carries installVectorExtension op (CREATE EXTENSION IF NOT EXISTS vector + postcondition) with pgvector:install-vector-v1 invariantId.
    • pgvectorExtensionDescriptor publishes contractSpace; pgvectorDatabaseDependencies removed from the descriptor.
    • rg 'pgvectorDatabaseDependencies' packages/3-extensions/pgvector/ returns 0 matches.

T4.3 — pgvector Scenario A e2e (2 commits)

  • df51e9605 Scenario A e2e against PGlite — three layers: pinned ops.json byte-equivalence, multi-space planning against the real install op, multi-space apply against a synthetic vector-domain stub.
  • 2bc7fc0e4 relocate test to test/integration/ to break a Turbo dep-graph cycle (adapter-postgres ↔ pgvector dev-deps). Mirrors the precedent at 8efd264c7.

T4.4 — multi-extension monorepo example (1 commit)

  • bbbab985b examples/multi-extension-monorepo/ with packages/audit/ + packages/feature-flags/ declaring contract spaces and app/contract.ts as the aggregator. End-to-end test asserts marker rows for all three spaces, hash equivalence, applied_invariants matching pinned head ref, and structural tables (app_user, audit_event, feature_flag) all created in one transaction.

Downstream regression fixes (3 commits)

T4.2 (pgvector descriptor no longer carries databaseDependencies) silently degraded three test groups from positive to vacuous assertions. Fixed via minimal synthetic legacy descriptors that ship databaseDependencies.init only — preserves coverage until M5 retires the framework field. Each carries a // removed in M5 comment.

  • b8751abbf planner.case1.test.ts — synthetic legacy descriptor.
  • 09926f45a schema-verify dependency_missing — synthetic legacy extension.
  • 6b841fb53 PSL pgvector dbInit/dbUpdate — pass migrationsDir + materialise pinned pgvector artefacts on disk so per-space dbInit/dbUpdate works.

Branch housekeeping (1 commit, not part of M4 scope)

  • 79c5854cc untrack project artefacts on M4 branch (kept on disk for orchestrator reference; tracked on M1/M2/M3 as before).

Architectural decisions

  • vector(N) ships natively as StorageTypeInstance. Unlike cipherstash's enum/composite/domain types (which were stubbed under contract.meta.cipherstashFutureIR), pgvector needs no IR vocabulary expansion — vector is a parameterised native type already supported by SqlStorage.
  • Synthetic legacy descriptors keep three downstream test groups honest until M5 retires databaseDependencies. Each such descriptor is minimal and carries an inline // removed in M5 comment so the cleanup is mechanical.
  • Test relocation for cycle-break. pgvector's e2e lives under test/integration/ to avoid a Turbo dep-graph cycle (adapter-postgres devDeps pgvector ↔ pgvector devDeps adapter-postgres). Same pattern as the precedent at 8efd264c7. The e2e consumes pgvector solely via its public /control export; pinned pgvector:* constants are inlined with comments tying them to descriptor.test.ts as the source of truth.
  • Synthetic-vector apply variant for PGlite. PGlite's WASM contrib doesn't ship the vector extension. Same shape as cipherstash R3's synthetic-bundle variant — apply leg substitutes a vector-domain stub; tests 1+2 use the real CREATE EXTENSION vector. Real-bundle apply waits for TML-2373-style e2e infra against an extension-equipped Postgres.

AC promotions

  • AC10 / TC-15 (vector type as contract-space type, byte-equivalent install op on disk) — PASS.
  • AC5 / TC-16 (per-space marker row written for pgvector + app under one tx) — PASS.
  • TC-17 (pgvector apply succeeds) — PASS at framework + synthetic-vector layer; full real-bundle apply gated on TML-2373-style infra.
  • TC-8 (monorepo: app + ≥2 internal contract-space extensions plan/apply per-space artefacts and write per-space markers) — PASS.

Verification

  • pnpm lint:deps green (756 modules, 0 violations).
  • pnpm typecheck green (127/127 turbo tasks).
  • pnpm --filter @prisma-next/extension-pgvector test green (41/41).
  • pnpm test:packages green (112/112 turbo tasks; transient PGlite resource conflict on parallel cipherstash+pgvector e2e cleared on rerun — known PGlite-under-load flake).
  • examples/multi-extension-monorepo vitest green (4/4).
  • pnpm --filter @prisma-next/integration-tests exec vitest run green (551/551).

Branch housekeeping

This branch untracks the project artefacts (projects/extension-contract-spaces/{spec.md, plan.md, specs/*}) — they remain on disk for orchestrator reference and remain tracked on M1/M2/M3 where they served as the source of truth. M5's close-out will migrate long-lived docs into docs/architecture docs/ and delete projects/extension-contract-spaces/ entirely.

Test plan

  • CI green.
  • No regressions in M1/M2/M3 framework or cipherstash.
  • pnpm --filter @prisma-next/extension-pgvector test 41/41 green.
  • Multi-extension monorepo example builds + e2e passes.

Refs: TML-2397.

Summary by CodeRabbit

  • New Features

    • Added a complete multi-extension monorepo example demonstrating composing multiple internal extensions and atomic multi-space migrations; pgvector moved to a contract-space workflow.
  • Documentation

    • Included a full README for the multi-extension example with repository layout and test/run instructions.
  • Tests

    • Added and expanded end-to-end and integration tests exercising multi-space init/plan/apply flows, pgvector scenarios, and legacy dependency coverage.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 6f0a20b1-8121-41c7-bbc3-9e9d587d1b4d

📥 Commits

Reviewing files that changed from the base of the PR and between 2997d70 and b779cfc.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • examples/multi-extension-monorepo/README.md
  • examples/multi-extension-monorepo/app/contract.ts
  • examples/multi-extension-monorepo/biome.jsonc
  • examples/multi-extension-monorepo/package.json
  • examples/multi-extension-monorepo/packages/audit/constants.ts
  • examples/multi-extension-monorepo/packages/audit/contract.ts
  • examples/multi-extension-monorepo/packages/audit/control.ts
  • examples/multi-extension-monorepo/packages/audit/migrations.ts
  • examples/multi-extension-monorepo/packages/feature-flags/constants.ts
  • examples/multi-extension-monorepo/packages/feature-flags/contract.ts
  • examples/multi-extension-monorepo/packages/feature-flags/control.ts
  • examples/multi-extension-monorepo/packages/feature-flags/migrations.ts
  • examples/multi-extension-monorepo/test/multi-space.e2e.integration.test.ts
  • examples/multi-extension-monorepo/tsconfig.json
  • examples/multi-extension-monorepo/vitest.config.ts
  • examples/prisma-next-demo/package.json
  • examples/prisma-next-demo/test/utils/control-client.ts
  • packages/3-extensions/pgvector/package.json
  • packages/3-extensions/pgvector/src/core/contract-space-constants.ts
  • packages/3-extensions/pgvector/src/core/contract.ts
  • packages/3-extensions/pgvector/src/core/migrations.ts
  • packages/3-extensions/pgvector/src/exports/control.ts
  • packages/3-extensions/pgvector/test/descriptor.test.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/planner.case1.test.ts
  • test/e2e/framework/package.json
  • test/e2e/framework/test/utils.ts
  • test/integration/test/authoring/psl.pgvector-dbinit.test.ts
  • test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts
  • test/integration/test/family.schema-verify.dependencies.integration.test.ts
  • test/integration/test/family.schema-verify.extensions.ts
  • test/integration/test/family.schema-verify.helpers.ts
  • test/integration/test/family.schema-verify.modes.test.ts

📝 Walkthrough

Walkthrough

Adds contract-space constants/contracts and baseline migrations for pgvector, audit, and feature-flags; updates control descriptors to publish ContractSpace; provides helpers to emit/materialize pinned artefacts; adds a multi-extension monorepo example and tests; and adds legacy-compat shims and package/config updates.

Changes

Contract-Space Extensions and Multi-Extension Composition

Layer / File(s) Summary
Schema & Constants
packages/3-extensions/pgvector/src/core/contract-space-constants.ts, examples/multi-extension-monorepo/packages/{audit,feature-flags}/constants.ts, examples/multi-extension-monorepo/README.md
Exports stable identifiers and storage schema shape constants for pgvector, audit, feature-flags, and the example README/documentation.
Contracts (Contract)
packages/3-extensions/pgvector/src/core/contract.ts, examples/multi-extension-monorepo/packages/{audit,feature-flags}/contract.ts, examples/multi-extension-monorepo/app/contract.ts
Defines pgvector, audit, feature-flags, and app contracts with postgres/sql targetFamily and exports computed storage hash/profile constants.
Baseline migrations & invariants
packages/3-extensions/pgvector/src/core/migrations.ts, examples/multi-extension-monorepo/packages/{audit,feature-flags}/migrations.ts
Publishes baseline MigrationPackage objects with invariant-tagged ops, computes migration hashes, and exports head refs containing storage hash and invariants.
Control descriptors & ContractSpace wiring
packages/3-extensions/pgvector/src/exports/control.ts, examples/multi-extension-monorepo/packages/{audit,feature-flags}/control.ts
Extension descriptors now publish a ContractSpace (contract JSON, baseline migrations, headRef) and expose create() factories; pgvector descriptor omits legacy databaseDependencies.
Artifact materialization helpers (test utilities)
examples/prisma-next-demo/test/utils/control-client.ts, test/e2e/framework/test/utils.ts, test/integration/test/authoring/psl.pgvector-dbinit.test.ts
Adds helpers to emit contract-space artefacts and materialize baseline migration packages into a migrations dir used by db init flows; used across demo, e2e, and integration tests.
Multi-extension example docs & config
examples/multi-extension-monorepo/{README.md,package.json,tsconfig.json,vitest.config.ts,biome.jsonc}
Adds the monorepo example workspace manifest and configs plus README documenting layout and test command.
Multi-extension example integration test
examples/multi-extension-monorepo/test/multi-space.e2e.integration.test.ts
Sequential E2E test that emits pinned per-space artefacts for audit and feature-flags, runs multi-space plan/apply, asserts extension-before-app ordering, marker persistence, DML round-trips, and declaration-order independence.
pgvector Scenario A integration & e2e tests
test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts
Integration suite validating pinned install SQL byte-equivalence, multi-space planning order, synthetic apply with a PGlite-compatible stub, marker persistence, and insert/select round-trips.
pgvector descriptor validation & regression tests
packages/3-extensions/pgvector/test/descriptor.test.ts
Vitest suite validating pgvector descriptor's contract-space wiring, baseline install op, invariants, headRef/hash consistency, and absence of databaseDependencies.
Planner & schema-verify legacy compatibility
packages/3-targets/6-adapters/postgres/test/migrations/planner.case1.test.ts, test/integration/test/family.schema-verify.{extensions,helpers,dependencies,modes}.test.ts
Adds synthetic legacyDatabaseDependencyExtension and updates planner and schema-verify tests to exercise legacy databaseDependencies paths for backward-compatible coverage.
Examples/test utils changes
examples/prisma-next-demo/test/utils/control-client.ts, test/e2e/framework/test/utils.ts
initTestDatabase now creates a temp migrations dir when omitted, materializes pinned artefacts into it, passes it to dbInit, and cleans up the directory.
Package manifests & config updates
packages/3-extensions/pgvector/package.json, examples/prisma-next-demo/package.json, test/e2e/framework/package.json, examples/multi-extension-monorepo/package.json
Adds @prisma-next/migration-tools to relevant manifests and moves @prisma-next/sql-contract into runtime deps for pgvector where applicable.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • prisma/prisma-next#441: Directly related work adding the multi-extension monorepo example, pgvector contract-space migration model, and tests.
  • prisma/prisma-next#438: Related contract-space artefact emission and multi-space planner changes used by these examples/tests.
  • prisma/prisma-next#439: Related migration materialization and pinned-artifact workflows exercised by the new tests.

Poem

A rabbit hops through code with glee, 🐰
Extensions lined up—three, not free—
Audit, flags, and vector play,
Pinned artefacts lead the way.
One monorepo, tests that shine—hop, merge, deploy, align. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main changes: migrating pgvector to contract spaces and adding a multi-extension monorepo example that demonstrates contract-space functionality across multiple extensions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tml-2397-pgvector-contract-space

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 8, 2026

Open in StackBlitz

@prisma-next/mongo-runtime

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-runtime@441

@prisma-next/family-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/family-mongo@441

@prisma-next/sql-runtime

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-runtime@441

@prisma-next/family-sql

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/family-sql@441

@prisma-next/extension-arktype-json

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-arktype-json@441

@prisma-next/extension-cipherstash

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-cipherstash@441

@prisma-next/middleware-telemetry

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/middleware-telemetry@441

@prisma-next/mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo@441

@prisma-next/extension-paradedb

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-paradedb@441

@prisma-next/extension-pgvector

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-pgvector@441

@prisma-next/postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/postgres@441

@prisma-next/sql-orm-client

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-orm-client@441

@prisma-next/sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sqlite@441

@prisma-next/target-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-mongo@441

@prisma-next/adapter-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-mongo@441

@prisma-next/driver-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-mongo@441

@prisma-next/contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/contract@441

@prisma-next/utils

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/utils@441

@prisma-next/config

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/config@441

@prisma-next/errors

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/errors@441

@prisma-next/framework-components

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/framework-components@441

@prisma-next/operations

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/operations@441

@prisma-next/ts-render

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/ts-render@441

@prisma-next/contract-authoring

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/contract-authoring@441

@prisma-next/ids

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/ids@441

@prisma-next/psl-parser

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/psl-parser@441

@prisma-next/psl-printer

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/psl-printer@441

@prisma-next/cli

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/cli@441

@prisma-next/emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/emitter@441

@prisma-next/migration-tools

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/migration-tools@441

prisma-next

npm i https://pkg.pr.new/prisma/prisma-next@441

@prisma-next/vite-plugin-contract-emit

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/vite-plugin-contract-emit@441

@prisma-next/mongo-codec

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-codec@441

@prisma-next/mongo-contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract@441

@prisma-next/mongo-value

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-value@441

@prisma-next/mongo-contract-psl

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract-psl@441

@prisma-next/mongo-contract-ts

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract-ts@441

@prisma-next/mongo-emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-emitter@441

@prisma-next/mongo-schema-ir

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-schema-ir@441

@prisma-next/mongo-query-ast

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-query-ast@441

@prisma-next/mongo-orm

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-orm@441

@prisma-next/mongo-query-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-query-builder@441

@prisma-next/mongo-lowering

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-lowering@441

@prisma-next/mongo-wire

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-wire@441

@prisma-next/sql-contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract@441

@prisma-next/sql-errors

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-errors@441

@prisma-next/sql-operations

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-operations@441

@prisma-next/sql-schema-ir

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-schema-ir@441

@prisma-next/sql-contract-psl

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-psl@441

@prisma-next/sql-contract-ts

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-ts@441

@prisma-next/sql-contract-emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-emitter@441

@prisma-next/sql-lane-query-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-lane-query-builder@441

@prisma-next/sql-relational-core

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-relational-core@441

@prisma-next/sql-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-builder@441

@prisma-next/target-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-postgres@441

@prisma-next/target-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-sqlite@441

@prisma-next/adapter-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-postgres@441

@prisma-next/adapter-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-sqlite@441

@prisma-next/driver-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-postgres@441

@prisma-next/driver-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-sqlite@441

commit: 2997d70

@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from 9b25e12 to d51b446 Compare May 8, 2026 16:11
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from bbbab98 to 16ad925 Compare May 8, 2026 16:17
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from d51b446 to 48437e5 Compare May 8, 2026 22:28
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from 16ad925 to ff6b187 Compare May 8, 2026 22:28
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from 48437e5 to 54f296d Compare May 10, 2026 00:05
@wmadden wmadden requested a review from a team as a code owner May 10, 2026 00:05
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from ff6b187 to 2340250 Compare May 10, 2026 00:16
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from dcee822 to 5f131bf Compare May 10, 2026 01:12
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from 2340250 to 409a6b9 Compare May 10, 2026 01:16
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from 5f131bf to ef1ce6f Compare May 10, 2026 08:24
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from 409a6b9 to c67ff2a Compare May 10, 2026 08:24
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from ef1ce6f to 75e5941 Compare May 10, 2026 12:38
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from c67ff2a to d7beb3e Compare May 10, 2026 12:42
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch from 75e5941 to f27e4ee Compare May 10, 2026 14:46
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from d7beb3e to d9dbccc Compare May 10, 2026 14:46
@wmadden wmadden force-pushed the tml-2397-cipherstash-contract-space branch 3 times, most recently from 7a4bd25 to d5da911 Compare May 10, 2026 16:27
Base automatically changed from tml-2397-cipherstash-contract-space to main May 10, 2026 16:27
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch 2 times, most recently from a32d756 to 6916b0c Compare May 10, 2026 17:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
test/integration/test/family.schema-verify.modes.test.ts (1)

54-58: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove milestone/task references from the inline comment.

This comment includes transient milestone/task identifiers, which will age quickly and make long-term maintenance/docs grep noisier. Keep the rationale, but describe behavior without milestone labels.

As per coding guidelines, "Source-code comments must not reference transient project artifacts including milestone-task IDs ...".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/integration/test/family.schema-verify.modes.test.ts` around lines 54 -
58, In the test near expect(result).toMatchObject({ ok: false }) (in the same
block that sets extensions: [legacyDatabaseDependencyExtension]), remove any
inline comment text that contains milestone/task IDs and replace it with a
concise rationale describing the expected behavior (e.g., why the result should
be ok: false) without referencing transient project artifacts; keep the intent
and steps-to-reproduce explanation but use stable language (behavior,
conditions, expected outcome) instead of milestone labels.
🧹 Nitpick comments (3)
examples/multi-extension-monorepo/app/contract.ts (1)

17-38: ⚡ Quick win

Derive storageHash from the storage payload to prevent drift.

Using a hardcoded hash string here can silently diverge from schema changes later. Prefer the same computed-hash pattern used in sibling contract-space packages.

Suggested refactor
+import { computeStorageHash } from '@prisma-next/contract/hashing';
 import { type Contract, coreHash, profileHash } from '@prisma-next/contract/types';
 import type { SqlStorage } from '@prisma-next/sql-contract/types';

 export const APP_USER_TABLE = 'app_user' as const;

-const APP_CONTRACT_HASH = coreHash('sha256:multi-extension-monorepo-app-v1');
 const APP_PROFILE_HASH = profileHash('sha256:multi-extension-monorepo-app-profile-v1');
+const storageBody = {
+  tables: {
+    [APP_USER_TABLE]: {
+      columns: {
+        id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
+        email: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
+      },
+      primaryKey: { columns: ['id'] },
+      uniques: [],
+      indexes: [],
+      foreignKeys: [],
+    },
+  },
+};
+const APP_CONTRACT_HASH = computeStorageHash({
+  target: 'postgres',
+  targetFamily: 'sql',
+  storage: storageBody,
+});

 export const appContract: Contract<SqlStorage> = {
   target: 'postgres',
   targetFamily: 'sql',
   profileHash: APP_PROFILE_HASH,
   storage: {
-    storageHash: APP_CONTRACT_HASH,
-    tables: {
-      [APP_USER_TABLE]: {
-        columns: {
-          id: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
-          email: { codecId: 'pg/text@1', nativeType: 'text', nullable: false },
-        },
-        primaryKey: { columns: ['id'] },
-        uniques: [],
-        indexes: [],
-        foreignKeys: [],
-      },
-    },
+    ...storageBody,
+    storageHash: coreHash(APP_CONTRACT_HASH),
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/multi-extension-monorepo/app/contract.ts` around lines 17 - 38, The
storageHash is hardcoded via APP_CONTRACT_HASH which can drift from the actual
storage payload; update appContract so storage.storageHash is computed from the
storage payload (the tables/columns object) instead of using
APP_CONTRACT_HASH—follow the same computed-hash pattern used in sibling packages
(compute hash via coreHash/profileHash or the project's storage-hash helper) and
assign that computed value to storage.storageHash (update or remove
APP_CONTRACT_HASH constant and ensure appContract and its storage object use the
computed hash).
test/e2e/framework/test/utils.ts (1)

107-128: ⚡ Quick win

Extract the pgvector artefact materialization into one shared test helper.

This exact contractSpace bootstrapping now exists here, in test/integration/test/authoring/psl.pgvector-dbinit.test.ts, and in test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts. Keeping it in one place will prevent the emitted artefacts, space directory, and baseline-migration assumptions from drifting apart.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/e2e/framework/test/utils.ts` around lines 107 - 128, Extract the
duplicated pgvector contract-space bootstrapping into a single helper function
(materialisePgvectorPinnedArtefacts) and replace the inline copies in
test/integration/test/authoring/psl.pgvector-dbinit.test.ts and
test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts with
calls to that helper; ensure the helper uses pgvector,
emitContractSpaceArtefacts and materialiseMigrationPackage (as in the provided
utils implementation) and export/import materialisePgvectorPinnedArtefacts from
test/e2e/framework/test/utils.ts so both tests call the shared function instead
of duplicating the logic.
test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts (1)

204-242: ⚡ Quick win

Keep invariant checks in the synthetic baseline migration.

Replacing the install op’s precheck and postcheck with empty arrays means this test no longer exercises the invariant-verification path—only the execute step. A synthetic pg_type-based check would keep the PGlite workaround while still covering the same contract-space lifecycle as the real migration.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts`
around lines 204 - 242, In buildSyntheticBaselineMigration, don’t drop the
install operation’s invariant checks; when mapping pgvectorBaselineMigration.ops
for the PGVECTOR_INSTALL_INVARIANT_ID, replace the empty precheck/postcheck with
synthetic checks that assert the presence of the stub vector type (e.g., a small
SELECT against pg_type or information_schema using the same PGlite-compatible
name you create in buildSyntheticVectorInstallSql), keeping the invariantId and
execute step as-is so the synthetic op still models install+verification; update
the syntheticOps used to computeMigrationHash
(computeMigrationHash(baseMetadata, syntheticOps)) accordingly so the migration
hash still reflects these synthetic pre/post checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/multi-extension-monorepo/README.md`:
- Line 7: Replace the transient milestone/spec labels in the README
text—specifically remove the fragment "(project: `extension-contract-spaces`,
**M4 / TC-8** / spec **AC4**)" and instead describe the stable feature or
behavior being documented (e.g., state that this pertains to the
extension-contract-spaces feature and summarize its intended behavior or
requirement in plain language). Ensure the README uses durable
architecture/behavior wording rather than ephemeral project milestone tags.
- Around line 19-20: The sentence mixes execution models by calling PGlite "real
Postgres"; update the README wording to be precise: either replace "against a
real Postgres (via PGlite)" with "against PGlite" if the example uses the
lightweight PGlite runtime, or change it to "against a real PostgreSQL server"
if it actually runs against a full Postgres instance; search for the term
"PGlite" in the README and adjust the one-line description accordingly so it
clearly states which backend is used.

In `@examples/multi-extension-monorepo/test/multi-space.e2e.integration.test.ts`:
- Around line 1-51: The top-of-file header block comment contains transient
milestone/spec labels (e.g. "M4", "T4.4", "TC-8", "AC4/AC5", "NFR6"); remove
those hard-coded IDs while preserving the intent and structure of the
description (keep the multi-extension purpose, three layers of coverage and
order-independence test narrative). Edit the header comment at the start of the
test (the top block in multi-space.e2e.integration.test.ts) to replace explicit
milestone/task/acceptance IDs with generic wording such as "relevant
milestone/acceptance criteria" or omit them entirely, ensuring the prose still
documents the test goals, layers of coverage, and ordering property.

In `@packages/3-extensions/pgvector/src/core/contract-space-constants.ts`:
- Around line 8-15: Edit the comment in contract-space-constants.ts to remove
transient project references ("FR3", "FR11", "sub-spec § 1") while preserving
the behavioral description: state that the space identifier 'pgvector' is
written to migrations/pgvector/ and used in the marker table's space column for
pgvector-owned rows, and that the invariantId namespace 'pgvector:*' is
locked/immutable; remove any milestone or spec shorthand but keep the
explanatory text and the quoted symbols ('pgvector' and 'pgvector:*') intact.

In `@packages/3-extensions/pgvector/src/core/contract.ts`:
- Around line 2-4: Remove the transient task/milestone identifiers from the
header docblock in the pgvector contract description: edit the comment that
describes the "pgvector contract space" and the parameterised native type
`vector(N)` to delete the shorthand references like `FR9 / TC-15`, leaving only
the behavioral explanation of the contract and how `vector(N)` may be used as a
`nativeType`.

In `@packages/3-extensions/pgvector/src/core/migrations.ts`:
- Around line 4-5: Update the doc comment above contractSpace.migrations in
migrations.ts to remove transient IDs and duplicated wording: replace references
like "FR1", "M3", and "FR11" with plain behavior descriptions (e.g., describe
that an extension's contractSpace.migrations is a list of in-memory migration
functions applied in order and their expected semantics) and remove the
duplicated "legacy legacy" typo so the comment reads clearly; ensure the revised
comment conveys the same behavioral requirements without mentioning
milestone/task IDs or acceptance-criteria codes.

In
`@packages/3-targets/6-adapters/postgres/test/migrations/planner.case1.test.ts`:
- Around line 16-27: The block comment references transient planning IDs (e.g.
"TML-2397", "M4", "T4.2", "M5"); update the comment to remove those
task/milestone identifiers and rephrase it in behavior-focused terms: describe
that this file provides a synthetic replacement for the legacy pgvector
`databaseDependencies.init` descriptor so planner tests can exercise the
planner's `extension.vector` op-emission path, note that pgvector migrated to a
contract-space mechanism and the descriptor is temporary until that mechanism
replaces the old API, and remove any specific milestone/task markers while
keeping the intent and lifecycle notes clear.

In `@test/integration/test/family.schema-verify.dependencies.integration.test.ts`:
- Around line 54-58: Replace the transient milestone/task references in the test
comment (the "M4 of TML-2397" and "M5" mentions) with durable, behavior-based
wording: describe that pgvector moved to the contract-space mechanism in an
earlier migration and that this test deliberately uses a synthetic legacy
"databaseDependencies" extension to exercise the legacy dependency_missing path
until the contract migration is fully rolled out; keep the rest of the comment
about building the contract with an extension pack and the stand-in extension
for "databaseDependencies" and "pgvector" unchanged.

In `@test/integration/test/family.schema-verify.extensions.ts`:
- Around line 5-14: Rewrite the block comment to remove milestone/project labels
(remove "M4" and "M5") and keep it focused on implementation behavior: explain
that pgvector exposes the package descriptor, that after migrating pgvector off
databaseDependencies.init the descriptor no longer participates in the legacy
dependency_missing schemaVerify path, and that the synthetic
legacyDatabaseDependencyExtension preserves the databaseDependencies shape so
existing dependency missing tests still exercise the schemaVerify path; also
note that the databaseDependencies mechanism and this synthetic are temporary
and will be removed in a future change without referencing milestone IDs.

---

Outside diff comments:
In `@test/integration/test/family.schema-verify.modes.test.ts`:
- Around line 54-58: In the test near expect(result).toMatchObject({ ok: false
}) (in the same block that sets extensions:
[legacyDatabaseDependencyExtension]), remove any inline comment text that
contains milestone/task IDs and replace it with a concise rationale describing
the expected behavior (e.g., why the result should be ok: false) without
referencing transient project artifacts; keep the intent and steps-to-reproduce
explanation but use stable language (behavior, conditions, expected outcome)
instead of milestone labels.

---

Nitpick comments:
In `@examples/multi-extension-monorepo/app/contract.ts`:
- Around line 17-38: The storageHash is hardcoded via APP_CONTRACT_HASH which
can drift from the actual storage payload; update appContract so
storage.storageHash is computed from the storage payload (the tables/columns
object) instead of using APP_CONTRACT_HASH—follow the same computed-hash pattern
used in sibling packages (compute hash via coreHash/profileHash or the project's
storage-hash helper) and assign that computed value to storage.storageHash
(update or remove APP_CONTRACT_HASH constant and ensure appContract and its
storage object use the computed hash).

In `@test/e2e/framework/test/utils.ts`:
- Around line 107-128: Extract the duplicated pgvector contract-space
bootstrapping into a single helper function (materialisePgvectorPinnedArtefacts)
and replace the inline copies in
test/integration/test/authoring/psl.pgvector-dbinit.test.ts and
test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts with
calls to that helper; ensure the helper uses pgvector,
emitContractSpaceArtefacts and materialiseMigrationPackage (as in the provided
utils implementation) and export/import materialisePgvectorPinnedArtefacts from
test/e2e/framework/test/utils.ts so both tests call the shared function instead
of duplicating the logic.

In `@test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts`:
- Around line 204-242: In buildSyntheticBaselineMigration, don’t drop the
install operation’s invariant checks; when mapping pgvectorBaselineMigration.ops
for the PGVECTOR_INSTALL_INVARIANT_ID, replace the empty precheck/postcheck with
synthetic checks that assert the presence of the stub vector type (e.g., a small
SELECT against pg_type or information_schema using the same PGlite-compatible
name you create in buildSyntheticVectorInstallSql), keeping the invariantId and
execute step as-is so the synthetic op still models install+verification; update
the syntheticOps used to computeMigrationHash
(computeMigrationHash(baseMetadata, syntheticOps)) accordingly so the migration
hash still reflects these synthetic pre/post checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e6415284-e6dd-42b5-99b1-ee415c0d7b4d

📥 Commits

Reviewing files that changed from the base of the PR and between 01154cf and 6916b0c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (32)
  • examples/multi-extension-monorepo/README.md
  • examples/multi-extension-monorepo/app/contract.ts
  • examples/multi-extension-monorepo/biome.jsonc
  • examples/multi-extension-monorepo/package.json
  • examples/multi-extension-monorepo/packages/audit/constants.ts
  • examples/multi-extension-monorepo/packages/audit/contract.ts
  • examples/multi-extension-monorepo/packages/audit/control.ts
  • examples/multi-extension-monorepo/packages/audit/migrations.ts
  • examples/multi-extension-monorepo/packages/feature-flags/constants.ts
  • examples/multi-extension-monorepo/packages/feature-flags/contract.ts
  • examples/multi-extension-monorepo/packages/feature-flags/control.ts
  • examples/multi-extension-monorepo/packages/feature-flags/migrations.ts
  • examples/multi-extension-monorepo/test/multi-space.e2e.integration.test.ts
  • examples/multi-extension-monorepo/tsconfig.json
  • examples/multi-extension-monorepo/vitest.config.ts
  • examples/prisma-next-demo/package.json
  • examples/prisma-next-demo/test/utils/control-client.ts
  • packages/3-extensions/pgvector/package.json
  • packages/3-extensions/pgvector/src/core/contract-space-constants.ts
  • packages/3-extensions/pgvector/src/core/contract.ts
  • packages/3-extensions/pgvector/src/core/migrations.ts
  • packages/3-extensions/pgvector/src/exports/control.ts
  • packages/3-extensions/pgvector/test/descriptor.test.ts
  • packages/3-targets/6-adapters/postgres/test/migrations/planner.case1.test.ts
  • test/e2e/framework/package.json
  • test/e2e/framework/test/utils.ts
  • test/integration/test/authoring/psl.pgvector-dbinit.test.ts
  • test/integration/test/extension-pgvector-scenario-a.e2e.integration.test.ts
  • test/integration/test/family.schema-verify.dependencies.integration.test.ts
  • test/integration/test/family.schema-verify.extensions.ts
  • test/integration/test/family.schema-verify.helpers.ts
  • test/integration/test/family.schema-verify.modes.test.ts

Comment thread examples/multi-extension-monorepo/README.md Outdated
Comment thread examples/multi-extension-monorepo/README.md Outdated
Comment thread packages/3-extensions/pgvector/src/core/contract-space-constants.ts
Comment thread packages/3-extensions/pgvector/src/core/contract.ts Outdated
Comment thread packages/3-extensions/pgvector/src/core/migrations.ts Outdated
Comment thread test/e2e/framework/test/utils.ts
Comment thread test/integration/test/family.schema-verify.dependencies.integration.test.ts Outdated
Comment thread test/integration/test/family.schema-verify.extensions.ts Outdated
wmadden added 5 commits May 10, 2026 19:38
Mirrors `packages/3-extensions/test-contract-space/` (the contract-space
fixture) and `packages/3-extensions/pgvector/` (the production reference
shape) so the new `@prisma-next/extension-cipherstash` package boots
with the standard tsdown / tsconfig / vitest / biome layout. Public
package (not `private`); single `./control` export entry; deps mirror
the closest workspace neighbour (test-contract-space) so the contract
space the next slice adds can compose `@prisma-next/family-sql/control`,
`@prisma-next/migration-tools/{hash,spaces}`, and the framework
contract types without further dependency churn.

R1 (TML-2397, project: extension-contract-spaces) is the skeleton-in
for cipherstash on the per-space planner / runner / verifier landed in
M1 + M2; the codec runtime and codec lifecycle hook are intentionally
deferred to M3 R2 (T3.4).

Refs: TML-2397
…T4.2)

Replaces pgvector's legacy `databaseDependencies.init` install path
with a first-class `contractSpace` block on the descriptor (project:
extension-contract-spaces, M4 / project AC10).

T4.1 — author the contract-space contents:
  - `contract.ts` declares the parameterised native `vector` type under
    `storage.types`. Unlike CipherStash's composite/enum/domain
    objects (deferred to `meta.cipherstashFutureIR` until IR vocabulary
    expansion), `vector(N)` IS representable in today's
    `StorageTypeInstance` shape; sub-spec § 1 / project FR9 / TC-15.
  - `migrations.ts` ships a single baseline migration whose only op is
    `installVectorExtension` — carries
    `CREATE EXTENSION IF NOT EXISTS vector` execute SQL plus the same
    pre/post checks the legacy `databaseDependencies` entry carried.
    Carries the stable `pgvector:install-vector-v1` invariantId
    (project FR11).

T4.2 — wire the descriptor + drop the legacy field:
  - `src/exports/control.ts` exposes
    `contractSpace = { contractJson, migrations, headRef }` and removes
    the `pgvectorDatabaseDependencies` definition. Per the project's
    Shipping Strategy, presence of `contractSpace` is the gate that
    selects the new mechanism — the framework loads the contract space
    and ignores any `databaseDependencies` block. M5 removes the
    framework-level field.
  - `test/descriptor.test.ts` pins the new shape: contractSpace
    declared, baseline op carries the `pgvector:*` invariantId,
    `assertDescriptorSelfConsistency` passes, and the legacy
    `databaseDependencies` is absent at all three layers (property,
    duck-type, `collectInitDependencies`).

Audited downstream consumers: `rg pgvectorDatabaseDependencies\\|databaseDependencies
packages/3-extensions/pgvector/ examples/` returned only the descriptor
itself; no other site references the field.

Refs: TML-2397.
Mirrors `packages/3-extensions/cipherstash/test/scenario-a.e2e.integration.test.ts`
in three layers, with the pgvector-specific simplification that there
is no codec lifecycle hook to assert on (pgvector's codec
contributes only `expandNativeType` + runtime behaviour):

  1. **Pinned `ops.json` byte-equivalence (disk).** Locks
     project AC10 / TC-15 at the on-disk shape level — the
     `CREATE EXTENSION IF NOT EXISTS vector` SQL flows through
     `installVectorExtension.execute[0].sql` and is serialised to
     `migrations/pgvector/<dirName>/ops.json` byte-for-byte.

  2. **Multi-space planning (real DDL).** `executePerSpaceDbApply`
     with `mode: plan` against the real descriptor produces a plan
     that includes the pgvector baseline op AND the app-space
     `CREATE TABLE Doc` op, with the extension op ordered first per
     `concatenateSpaceApplyInputs` cross-space ordering. The app
     contract uses `typeParams.length: 3` so the codec's
     `expandNativeType` hook lifts the column type to `vector(3)` in
     the emitted DDL.

  3. **Multi-space apply (synthetic vector stub).** PGlite does not
     ship the `vector` extension (verified by enumerating
     `node_modules/.../@electric-sql/pglite/dist/contrib/`); the
     synthetic-stub variant replaces the install op's SQL with a
     `CREATE DOMAIN vector AS text` stub so the framework + per-space
     wiring runs against a real DB. Asserts marker rows for both `app`
     and `pgvector` (project AC5 / AC10 / TC-16), the `Doc` table
     exists with the `embedding` column, and an insert + select
     round-trip works. The synthetic apply schema deliberately omits
     `typeParams.length` because text-domains do not accept type
     modifiers; the `vector(N)` rendering path is exercised by test
     (2) and by `test/codec-render-output-type.test.ts`.

Real-bundle apply against a vector-equipped Postgres is deferred to
e2e infra (consistent with cipherstash's synthetic-bundle approach
for `pgcrypto`).

Refs: TML-2397.
wmadden added 10 commits May 10, 2026 19:38
…tion to break Turbo dep-graph cycle

Adding the T4.3 Scenario A end-to-end test inside the
extension-pgvector package introduced a cycle in the Turbo dep graph:

  adapter-postgres ──devDeps──▶ extension-pgvector
  extension-pgvector ──devDeps──▶ adapter-postgres / cli /
                                   driver-postgres / target-postgres

adapter-postgres has long depended on extension-pgvector (cast-policy /
planner-storage-types tests), so introducing the back-edge from
extension-pgvector closed the cycle and broke pnpm test:packages.

This mirrors the precedent in `8efd264c7 refactor(target-postgres):
break Turbo dep cycle by relocating runtime-dep tests`.

Changes:

- Relocate the Scenario A e2e from
  `packages/3-extensions/pgvector/test/` to
  `test/integration/test/` (the home of cross-package integration
  tests; already declares all four needed packages as devDeps).
- Refactor the relocated test to consume pgvector solely via its
  public `/control` export. The few `pgvector:*` string constants the
  test needs are inlined with comments tying them to the descriptor
  self-consistency test as their source of truth (FR11 invariantId
  immutability is the load-bearing one and is asserted there first).
- Drop the now-unneeded devDeps from extension-pgvector
  (`adapter-postgres`, `cli`, `driver-postgres`, `target-postgres`).
- Doc-fix the legacy `databaseDependencies` reference in pgvector
  `migrations.ts` to drop the now-removed `pgvectorDatabaseDependencies`
  identifier from prose.

Refs: TML-2397
…ynthetic legacy descriptor

planner.case1 contains tests that exercise the Postgres planner's
handling of an extension-supplied `databaseDependencies.init` block
(the path that emits `extension.<name>` ops alongside the structural
plan). Until M4 of the extension-contract-spaces project, these tests
used the real pgvector descriptor as the source of `databaseDependencies`.

T4.2 of that project moved pgvector off `databaseDependencies` and onto
the new `contractSpace` mechanism, so the real pgvector descriptor no
longer carries a `databaseDependencies.init` block. Using it here
silently degraded the planner-side coverage from a positive assertion
("the planner emits `extension.vector` for declared deps") into a
trivially-passing one (no deps, no op).

Introduce a local synthetic `pgvectorLegacyDescriptor` that ships only
the legacy `databaseDependencies.init` block, and use it everywhere
those tests assert behaviour against an extension-with-deps. The one
test that needs the real pgvector codec (`renders pgvector vector(N)
column types in DDL`) keeps the real descriptor, since it is asserting
codec hooks for parameterised native types — not the deps-install
path.

The `databaseDependencies` mechanism itself is removed in M5 of the
project; this synthetic + its consumers go away with it.

Refs: TML-2397
…c legacy extension

The schemaVerify "dependency_missing" tests rely on at least one
extension declaring `databaseDependencies.init` so the family-instance
verify path emits a `dependency_missing` issue when the corresponding
database object is absent.

T4.2 of the extension-contract-spaces project moved pgvector off
`databaseDependencies` and onto the new `contractSpace` mechanism, so
the real pgvector descriptor no longer triggers that path. The tests
were silently degrading from a positive assertion to a vacuous one
(no deps declared, no issue emitted, ok=true instead of ok=false).

Add a local synthetic `legacyDatabaseDependencyExtension` to the
schema-verify test extensions module and rewire the dependency_missing
tests in `family.schema-verify.dependencies.integration.test.ts` and
`family.schema-verify.modes.test.ts` to consume it. Keep the real
pgvector descriptor for the type-metadata-registry tests, which assert
the registry contains `pg/vector@1` — that part of pgvector is
unchanged by M4.

The whole `databaseDependencies` mechanism is removed in M5; this
synthetic and its consumers go away with it.

Refs: TML-2397
…vector dbInit/dbUpdate

Now that pgvector publishes a `contractSpace` (project T4.2), the
control-client `dbInit` and `dbUpdate` flows fan out across declared
extension contract spaces and require a `migrationsDir` so the
per-space runner can read each space's pinned `refs/head.json` and
baseline migration package off disk.

Update the PSL → emit → dbInit/dbUpdate authoring integration tests
to:

- Materialise pgvector's pinned contract-space artefacts under
  `<testDir>/migrations/pgvector/...` via `emitPinnedSpaceArtefacts`
  + `writeExtensionMigrationPackage`.
- Pass `migrationsDir` through to every `client.dbInit` /
  `client.dbUpdate` call.

Same coverage (PSL emit → contract validation → dbInit/dbUpdate
plan/apply against a real Postgres dev DB), now compatible with the
per-space code path the contract-space project introduced.

Refs: TML-2397
Demonstrates a monorepo where two internal packages each declare a
contract space and an aggregator app composes both extensions. The
framework treats them uniformly with the application's own contract
space, walking three migration graphs and writing three marker rows
in one transaction.

Layout:

- `packages/audit/` — declares an `audit` contract space (single
  `audit_event` table, baseline migration with one structural
  `CREATE TABLE` op).
- `packages/feature-flags/` — declares a `feature-flags` contract
  space (single `feature_flag` table, baseline migration).
- `app/contract.ts` — application contract for `app_user`.
- `test/multi-space.e2e.integration.test.ts` — end-to-end against
  PGlite via `executePerSpaceDbApply`. Materialises pinned artefacts
  for both extensions, runs `mode=apply` against an empty DB, then
  asserts:
    * marker rows exist for `app`, `audit`, and `feature-flags` (AC5);
    * each marker's `core_hash` matches the corresponding pinned
      contract's `storageHash`;
    * each marker's `applied_invariants` matches the pinned head
      ref's invariants;
    * the three structural tables (`app_user`, `audit_event`,
      `feature_flag`) all exist post-apply.

Closes TC-8 — "monorepo: app + ≥2 internal contract-space extensions
plan/apply per-space artefacts and write per-space markers".

Refs: TML-2397
…example)

- Rename PinnedSpace identifiers to ContractSpace (emit/list/read/Record)
- Replace ExtensionContractRef/ExtensionMigrationPackage/ExtensionContractSpace
  with canonical ContractSpaceHeadRef/MigrationPackage/ContractSpace from
  @prisma-next/framework-components/control (no longer re-exported from
  family-sql/control after M2.5).
- Replace removed writeExtensionMigrationPackage with materialiseMigrationPackage.
…criptors

ContractSpace from @prisma-next/framework-components/control is
generic over the contract; SQL extension descriptors require
ContractSpace<Contract<SqlStorage>>. Apply the explicit
specialisation in pgvector and the multi-extension-monorepo
example so the descriptor type-checks.
executePerSpaceDbApply was retired in M2.5 in favour of the aggregate-pipeline CLI surface (executeDbInit / executeDbUpdate / executeDbVerify). Two e2e integration suites still imported the old symbol after the cascade rebase: extension-pgvector-scenario-a (test/integration) and multi-space.e2e (examples/multi-extension-monorepo).

Migrate both to executeDbInit:

* drop the per-space {extensionContractSpaces:[{id}], policy, action} options;

* pass the actual extension descriptors via extensionPacks; the loader resolves their declared spaces from the descriptor surface, so the test no longer enumerates space ids manually;

* add the required targetId option (M2.5 aggregate pipeline pins the target rather than inferring from the migrations registry);

* drop the now-unused INIT_ADDITIVE_POLICY import in pgvector (db init's policy is fixed by executeDbInit internally);

* refresh docblocks that named the old symbol.

Mirrors the same migration applied to packages/3-extensions/cipherstash/test/scenario-a.e2e.integration.test.ts on M3.
…sses

The demo app contract and e2e framework contracts both declare pgvector
in their `extensionPacks`. After pgvector moved to the contract-space
mechanism (T4.1/T4.2), the per-space `db init` flow requires pgvector's
head ref + baseline migration to be present under
`<migrationsDir>/pgvector/...` or it fails the layout check with
`declaredButUnmigrated`.

Update both harnesses to materialise pgvector's pinned contract-space
artefacts (head ref + baseline migration package) into a temp
migrationsDir before each dbInit call, mirroring the helper already
used by `psl.pgvector-dbinit.test.ts` in `test/integration/`.

Refs: TML-2397
…nit cleanup leak

Address CodeRabbit feedback on the M4 contract-space PR:

- Strip transient project labels (M4, T4.4, TC-8, FR1, FR3, FR9, FR11,
  sub-spec § N) from source comments in pgvector core, the multi-
  extension-monorepo README + test, the postgres planner.case1 test,
  and two test/integration helpers. Stable references (TML-2397) kept.
- Clarify the multi-extension README/test wording: PGlite is the
  embedded engine the framework uses for tests, not "real Postgres
  via PGlite".
- Fix duplicated "legacy legacy" wording in pgvector migrations
  docblock.

Bug fix in test/e2e/framework/test/utils.ts:

- runDbInit / getPlannedDdlSql allocated controlClient before
  withE2eMigrationsDir(); after the prior commit added a
  materialisePgvectorPinnedArtefacts step that can throw inside
  withE2eMigrationsDir, the inner finally never ran and the client
  leaked. Move close() to an outer finally that owns the controlClient
  for the full call.

Refs: TML-2397
@wmadden wmadden force-pushed the tml-2397-pgvector-contract-space branch from 2997d70 to b779cfc Compare May 10, 2026 17:38
@wmadden wmadden merged commit 16e2ba1 into main May 10, 2026
9 of 10 checks passed
@wmadden wmadden deleted the tml-2397-pgvector-contract-space branch May 10, 2026 17:38
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