2 Upstream Contract
Ulysia edited this page 2026-10-06 16:54:32 +02:00

Mirrored from upstream-contract/README.md at commit f31ec6a. Edit the file in the repository: this page is regenerated from it, and edits made here are overwritten.

Upstream contract tests

Unit tests that fail when upstream moves something this bundle depends on.

Normally you do not run these directly — scripts/check-upstream.sh runs them along with the pin invariant and the patch/typecheck pass, and CI (.github/workflows/test.yml) runs that on main, testing and every PR. To run just this suite:

pnpm --filter server exec jest --testPathPatterns upstream-contract

No database, no container, no Nest boot — they read source files off disk and finish in seconds, so they can run on every commit.

The problem they solve

This fork depends on core in three ways, and only one of them is type-checked:

seam breaks how caught by
static imports of core services build error tsc
files our patches edit patch fails to apply scripts/verify-patches.sh
require() hooks and raw SQL silently, at runtime these tests

The third row is the dangerous one. Core loads every EE feature with a bare require(<string>) wrapped in try/catch, so a path that no longer matches is not an error — it means "not bundled", and the feature simply disappears. Postgres functions named inside a sql template are resolved at execution time, so a dropped one is a 500 on one endpoint and nothing anywhere else.

What each file checks

hook-inventory.ts — every core→EE hook as data: path, export, call sites, who owns the call site, whether we implement it, and for unimplemented ones a note on what the user loses. This is the list di-check.ts keeps informally; here it is machine-readable so both halves can be checked against it.

core-ee-hooks.spec.ts — both directions:

  • their side — every call site recorded in the inventory still exists in core and still reads the same exported name;
  • their side, completeness — scans all of core for require('…ee/…') and fails on anything the inventory does not list. This is the new-feature detector: upstream adding a hook is otherwise invisible;
  • our side — implemented hooks export the symbol core reads; unimplemented ones have no file at all, because a stub makes core take the "bundled" branch and then fail per request.

core-sql-objects.spec.ts — the Postgres functions and extensions our raw SQL calls (base_cell_*, jsonb_set_many, f_unaccent, unaccent, pg_trgm) are still created by a migration, with the arity we call them with.

Relationship to di-check.ts

They are complements, not duplicates.

di-check.ts boots a real app in the container and asks whether our exports resolve through the DI container — the only way to catch a class that is exported but not registered as a provider, which is how the api-key hook once broke. It cannot run in CI per commit, and it treats core's half of each contract as a comment.

These specs check the half di-check assumes, and cost seconds instead of a container. Neither replaces the other:

di-check.ts these specs
core still calls the hook no yes
upstream added a hook no yes
our export exists yes yes
our export resolves in DI yes no
needs a running instance yes no

When one fails

A hook vanished or moved — upstream dropped or renamed it. Find the new call site, update the inventory, and check whether the feature still works.

An unknown hook appeared — upstream shipped an EE feature we do not have. Either implement it, or add it to the inventory with implemented: false and an unimplementedNote recording what users lose. Both are fine; leaving it undecided is not, because the failure mode is a feature that is quietly missing.

A SQL function changed — do not just update the expected arity. Our callers pass a fixed argument list; find them via the usedBy field and fix those too.

Worked example: what this caught

Moving the pin from 7439da2f to 5b854645 added exactly one hook, oauth/services/oauth-strategy.service, from upstream's MCP OAuth work (e56de8eb). Nothing else would have reported it — it type-checks, no patch touches it, and core's try/catch turns the miss into silence. It is recorded in the inventory as unimplemented: MCP clients cannot authenticate against this build, and nothing else regresses.