(TML-2397) M4 — Contract spaces: pgvector migration + monorepo example#441
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (32)
📝 WalkthroughWalkthroughAdds 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. ChangesContract-Space Extensions and Multi-Extension Composition
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/extension-arktype-json
@prisma-next/extension-cipherstash
@prisma-next/middleware-telemetry
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
9b25e12 to
d51b446
Compare
bbbab98 to
16ad925
Compare
d51b446 to
48437e5
Compare
16ad925 to
ff6b187
Compare
48437e5 to
54f296d
Compare
ff6b187 to
2340250
Compare
dcee822 to
5f131bf
Compare
2340250 to
409a6b9
Compare
5f131bf to
ef1ce6f
Compare
409a6b9 to
c67ff2a
Compare
ef1ce6f to
75e5941
Compare
c67ff2a to
d7beb3e
Compare
75e5941 to
f27e4ee
Compare
d7beb3e to
d9dbccc
Compare
7a4bd25 to
d5da911
Compare
a32d756 to
6916b0c
Compare
There was a problem hiding this comment.
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 winRemove 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 winDerive
storageHashfrom 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 winExtract the pgvector artefact materialization into one shared test helper.
This exact
contractSpacebootstrapping now exists here, intest/integration/test/authoring/psl.pgvector-dbinit.test.ts, and intest/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 winKeep invariant checks in the synthetic baseline migration.
Replacing the install op’s
precheckandpostcheckwith empty arrays means this test no longer exercises the invariant-verification path—only the execute step. A syntheticpg_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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (32)
examples/multi-extension-monorepo/README.mdexamples/multi-extension-monorepo/app/contract.tsexamples/multi-extension-monorepo/biome.jsoncexamples/multi-extension-monorepo/package.jsonexamples/multi-extension-monorepo/packages/audit/constants.tsexamples/multi-extension-monorepo/packages/audit/contract.tsexamples/multi-extension-monorepo/packages/audit/control.tsexamples/multi-extension-monorepo/packages/audit/migrations.tsexamples/multi-extension-monorepo/packages/feature-flags/constants.tsexamples/multi-extension-monorepo/packages/feature-flags/contract.tsexamples/multi-extension-monorepo/packages/feature-flags/control.tsexamples/multi-extension-monorepo/packages/feature-flags/migrations.tsexamples/multi-extension-monorepo/test/multi-space.e2e.integration.test.tsexamples/multi-extension-monorepo/tsconfig.jsonexamples/multi-extension-monorepo/vitest.config.tsexamples/prisma-next-demo/package.jsonexamples/prisma-next-demo/test/utils/control-client.tspackages/3-extensions/pgvector/package.jsonpackages/3-extensions/pgvector/src/core/contract-space-constants.tspackages/3-extensions/pgvector/src/core/contract.tspackages/3-extensions/pgvector/src/core/migrations.tspackages/3-extensions/pgvector/src/exports/control.tspackages/3-extensions/pgvector/test/descriptor.test.tspackages/3-targets/6-adapters/postgres/test/migrations/planner.case1.test.tstest/e2e/framework/package.jsontest/e2e/framework/test/utils.tstest/integration/test/authoring/psl.pgvector-dbinit.test.tstest/integration/test/extension-pgvector-scenario-a.e2e.integration.test.tstest/integration/test/family.schema-verify.dependencies.integration.test.tstest/integration/test/family.schema-verify.extensions.tstest/integration/test/family.schema-verify.helpers.tstest/integration/test/family.schema-verify.modes.test.ts
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.
…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
2997d70 to
b779cfc
Compare
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
databaseDependenciesfield from the framework entirely.What lands (7 commits)
T4.1 + T4.2 — pgvector contract-space migration (1 commit)
ac4b28fcemigrate to contract-space mechanismpgvectorContractdeclares thevectorparameterised native type (ships natively asStorageTypeInstance— no IR vocabulary expansion needed).pgvectorBaselineMigrationcarriesinstallVectorExtensionop (CREATE EXTENSION IF NOT EXISTS vector+ postcondition) withpgvector:install-vector-v1invariantId.pgvectorExtensionDescriptorpublishescontractSpace;pgvectorDatabaseDependenciesremoved from the descriptor.rg 'pgvectorDatabaseDependencies' packages/3-extensions/pgvector/returns 0 matches.T4.3 — pgvector Scenario A e2e (2 commits)
df51e9605Scenario A e2e against PGlite — three layers: pinnedops.jsonbyte-equivalence, multi-space planning against the real install op, multi-space apply against a synthetic vector-domain stub.2bc7fc0e4relocate test totest/integration/to break a Turbo dep-graph cycle (adapter-postgres ↔ pgvector dev-deps). Mirrors the precedent at8efd264c7.T4.4 — multi-extension monorepo example (1 commit)
bbbab985bexamples/multi-extension-monorepo/withpackages/audit/+packages/feature-flags/declaring contract spaces andapp/contract.tsas the aggregator. End-to-end test asserts marker rows for all three spaces, hash equivalence,applied_invariantsmatching 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 shipdatabaseDependencies.initonly — preserves coverage until M5 retires the framework field. Each carries a// removed in M5comment.b8751abbfplanner.case1.test.ts— synthetic legacy descriptor.09926f45aschema-verifydependency_missing— synthetic legacy extension.6b841fb53PSL pgvector dbInit/dbUpdate — passmigrationsDir+ materialise pinned pgvector artefacts on disk so per-space dbInit/dbUpdate works.Branch housekeeping (1 commit, not part of M4 scope)
79c5854ccuntrack project artefacts on M4 branch (kept on disk for orchestrator reference; tracked on M1/M2/M3 as before).Architectural decisions
vector(N)ships natively asStorageTypeInstance. Unlike cipherstash's enum/composite/domain types (which were stubbed undercontract.meta.cipherstashFutureIR), pgvector needs no IR vocabulary expansion —vectoris a parameterised native type already supported bySqlStorage.databaseDependencies. Each such descriptor is minimal and carries an inline// removed in M5comment so the cleanup is mechanical.test/integration/to avoid a Turbo dep-graph cycle (adapter-postgres devDeps pgvector ↔ pgvector devDeps adapter-postgres). Same pattern as the precedent at8efd264c7. The e2e consumes pgvector solely via its public/controlexport; pinnedpgvector:*constants are inlined with comments tying them todescriptor.test.tsas the source of truth.vectorextension. Same shape as cipherstash R3's synthetic-bundle variant — apply leg substitutes a vector-domain stub; tests 1+2 use the realCREATE EXTENSION vector. Real-bundle apply waits for TML-2373-style e2e infra against an extension-equipped Postgres.AC promotions
Verification
pnpm lint:depsgreen (756 modules, 0 violations).pnpm typecheckgreen (127/127 turbo tasks).pnpm --filter @prisma-next/extension-pgvector testgreen (41/41).pnpm test:packagesgreen (112/112 turbo tasks; transient PGlite resource conflict on parallel cipherstash+pgvector e2e cleared on rerun — known PGlite-under-load flake).examples/multi-extension-monorepovitest green (4/4).pnpm --filter @prisma-next/integration-tests exec vitest rungreen (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 intodocs/architecture docs/and deleteprojects/extension-contract-spaces/entirely.Test plan
pnpm --filter @prisma-next/extension-pgvector test41/41 green.Refs: TML-2397.
Summary by CodeRabbit
New Features
Documentation
Tests