Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions docs/integration-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Integration train — making ecosystem-wide 100% parity provable

## Why this exists

The MobilityDB ecosystem (MobilityDB · MEOS-API · PyMEOS-CFFI · PyMEOS ·
MobilityDuck) currently carries a fan of individually-correct **open**
PRs. Each is green *in isolation*, but:

- every PR's CI builds against a `master` that lacks the *other* PRs'
content (PyMEOS CI builds MEOS from MobilityDB master → lacks the
extended-type C surface; PyMEOS code is broken vs MEOS master — the
rename skew);
- no session can self-merge;
- per-session independence means **nobody assembles and verifies the
integrated whole**.

So "100% parity" is true per-branch yet **unprovable as a system**. This
train operationalizes MobilityDB discussion **#895 (wave-based merge
plan)**: a dependency-ordered manifest + a one-command verifier so parity
is demonstrated *at one point*, and the maintainer gets an ordered,
de-risked merge sequence instead of N cross-dependent PRs reading red.

## Artifacts

- [`meta/integration-train.json`](../meta/integration-train.json) — the
PR dependency DAG, per-wave compose recipe, gates, owners, merge order.
- [`verify-train.sh`](../verify-train.sh) — composes the train and runs
each wave's gate. Honesty contract: a wave is `PASS` only when its gate
is just-run green here; otherwise `BLOCKED:<reason>` with the exact
gate it needs. Nothing faked or silently skipped.

## The waves

| Wave | Content | Status | Owner |
|---|---|---|---|
| **0** | MobilityDB extended-type C surface (stack #1081→#1085, then #1051→#951) | **PROVEN** — 2699 fns, MEOS-API PR #10 21/21, `from_mfjson`/ctors uniform | maintainer-merge gated |
| **1** | PyMEOS-CFFI MEOS-1.4 substrate (regenerate vs Wave-0) | in-flight | live PyMEOS session |
| **2** | **CRITICAL PATH** — PyMEOS MEOS-1.4 bump (#81/#82): kills the rename skew | blocking all | live PyMEOS session |
| **3** | PyMEOS features: #85, #87, #88, #89→#90→#91, #84 (+ MobilityDuck #146/#147 for the #84 interop) | green-in-isolation, gated on Wave 2 | mixed |

**Wave 2 is the single universal unblock.** Every PyMEOS parity claim is
downstream of it; nothing else accelerates 100% parity more. Build the
bump against the composed Wave 0 (not bare master) so it is done once,
correctly, against the final C surface.

## Branch base

This branch is **stacked on `feat/object-model` (MEOS-API PR #10)** — the
Wave-0 gate asserts the object-model classification (`from_mfjson` →
TCbuffer/TNpoint/TPose, concrete `*inst_make` constructors), which is
PR #10's pipeline. PR #10 (object model) + PR #8 (portable-aliases SoT)
are the catalog anchor of the train; see
`meta/integration-train.json#/catalog_anchor`.

## Running it

```bash
python3 setup.py # one-time: fetch the MobilityDB sources
./verify-train.sh # Wave 0 fully verified here; Waves 1-3 report
# honest BLOCKED + the exact gate each needs
PYMEOS_ENV=<bump-ready PyMEOS clone> ./verify-train.sh # post-Wave-2
```

## Current status

Wave 0 is **proven**. Waves 1–3 are entirely gated on Wave 2 (the
MEOS-1.4 bump). There is **no remaining correctness work** — every
deliverable is verified correct in isolation; the gap is purely
integration ordering plus the maintainer-only merge gate, which this
train reduces to: *merge in wave order; CI turns green at each wave.*
75 changes: 75 additions & 0 deletions meta/integration-train.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"schema": "integration-train/0.1.0-draft",
"purpose": "Dependency-ordered merge train + per-wave verification gates that make the ecosystem-wide 100% parity PROVABLE at one point, instead of asserted from per-PR isolation-green. Operationalizes MobilityDB discussion #895 (wave-based merge plan).",
"anchor": "The MEOS-API meos-idl.json regenerated from the integrated MobilityDB surface is the parity proof artifact: every binding derives from it.",
"invariant": "100% parity is DEMONSTRABLE only when the full train is composed and every wave gate passes together. Per-PR isolation-green is necessary, not sufficient.",
"merge_gate": "All merges are maintainer-only (no self-merge by any session). This train reduces that gate to a mechanical action: merge in wave order; CI turns green at each wave.",
"waves": [
{
"id": 0,
"name": "MobilityDB core - extended-type C surface",
"repo": "MobilityDB/MobilityDB",
"status": "PROVEN",
"verified": "2026-05-19, _mobilitydb branch complete-extype-surface (this repo)",
"compose": {
"base": "master",
"linear_stack_tip": {"pr": 1085, "ref": "pull/1085/head", "head": "ea8f357", "contains": "master->#1081(66d8afa)->#1082(cf9ed91)->#1083(131eccb)->#1084(d60e63c)->#1085(ea8f357)"},
"then_pair": {"refs": ["pull/1051/head (#1051 e624027)", "pull/951/head (#951 19e3e35)"], "note": "clean linear pair #1051->#951; cherry-picks clean onto the stack tip (proven). #951 includes the typed tnpoint_from_mfjson uniformity wrapper."},
"docs_only_low_priority": {"pr": 953, "branch": "split/tnpoint-production-guidance"}
},
"gate": {
"meos_api_catalog_functions": 2699,
"meos_api_pr10_tests": "21/21",
"from_mfjson_uniform": ["tcbuffer_from_mfjson->TCbuffer", "tnpoint_from_mfjson->TNpoint", "tpose_from_mfjson->TPose"],
"concrete_constructors": ["tcbufferinst_make->TCbufferInst", "tposeinst_make->TPoseInst"]
},
"merge_order": "stack bottom-up #1081->#1082->#1083->#1084->#1085, then #1051->#951. Do NOT re-flatten to independent branches (the C-library session already rebased these into clean linear sequences).",
"known_conflicts_resolved": ["#1083 vs #1084 (rebased into the linear stack)", "#1051 vs #951 (rebased into the #1051->#951 pair)"]
},
{
"id": 1,
"name": "Substrate - PyMEOS-CFFI MEOS-1.4",
"repo": "MobilityDB/PyMEOS-CFFI",
"status": "IN_FLIGHT",
"prs": [{"pr": 19, "branch": "bump/meos-1.4"}, {"pr": 18, "branch": "refactor/codegen-meos-idl", "draft": true}],
"action": "regenerate builder/meos-idl.json against the Wave-0 MobilityDB surface",
"gate": "pymeos_cffi builds + imports against a Wave-0 MEOS",
"owner": "live PyMEOS session"
},
{
"id": 2,
"name": "CRITICAL PATH - PyMEOS master health (MEOS-1.4 bump)",
"repo": "MobilityDB/PyMEOS",
"status": "BLOCKING_ALL",
"prs": [{"pr": 81, "branch": "bump/meos-1.4", "draft": true}, {"pr": 82, "branch": "bump/meos-1.3"}],
"problem": "PyMEOS master is broken vs MEOS master: rename skew geoset_*->spatialset_*, tpoint_*->tspatial_*, pgis_geometry_in->geom_in, spanset_make arity. Pristine PyMEOS master = 426 failed / 2721 passed; red on EVERY PyMEOS PR's Test-PyMEOS check.",
"gate": "PyMEOS test.yml collects and 426->0 failures, built against Wave-1 CFFI / Wave-0 MEOS",
"owner": "live PyMEOS session",
"note": "Build the bump against the integrated Wave-0 (not bare master) so it is done once, correctly, against the final C surface."
},
{
"id": 3,
"name": "PyMEOS features (downstream of Wave 2)",
"repo": "MobilityDB/PyMEOS",
"status": "GREEN_IN_ISOLATION",
"prs": [
{"pr": 85, "branch": "fix/black-datespan", "head": "bd7b426", "gate": "black --check . passes whole-repo", "status": "READY (verified green-on-gate 2026-05-19; only its inherited Test-PyMEOS red is Wave-2)"},
{"pr": 87, "branch": "feat/portable-aliases", "gate": "portable_parity.py: 0 unbacked, 29 pairs, 6 families"},
{"pr": 88, "branch": "feat/extended-temporal-types", "gate": "suites collect; NotImplementedError stubs -> real once the Wave-0 surface is in the MEOS PyMEOS-CI builds", "stack_base": true},
{"pr": 89, "branch": "refactor/oo-codegen-meos-idl", "stacked_on": 88, "gate": "codegen.py --check (MEOS-API meos-idl.json SoT; proven zero-regression 407==407)"},
{"pr": 90, "branch": "refactor/oo-codegen-tcbuffer-switch", "stacked_on": 89, "gate": "tcbuffer_test 26p/1f; generated mixin backing-identical to oracle"},
{"pr": 91, "branch": "refactor/oo-codegen-tpose-switch", "stacked_on": 90},
{"pr": 84, "branch": "feat/datalake-consumer", "head": "9a6f8b9", "gate": "tests/io 4/4; temporal footer byte-identical to MobilityDuck #146", "independent": true, "status": "code verified correct + interop confirmed 2026-05-19"}
],
"interop_dependency": {"repo": "MobilityDB/MobilityDuck", "prs": [{"pr": 146, "branch": "lake/temporal-footer"}, {"pr": 147, "branch": "lake/edge-to-cloud-quickstart"}], "for": "#84 TemporalParquet second-consumer interop demonstration"}
}
],
"catalog_anchor": {
"repo": "MobilityDB/MEOS-API",
"prs": [
{"pr": 10, "branch": "feat/object-model", "status": "verified 21/21 vs Wave-0", "role": "object model (class lattice/methods/error contract)"},
{"pr": 8, "branch": "feat/portable-aliases", "role": "portable bare-name 29-pair SoT"}
]
},
"current_status": "Wave 0 PROVEN. Waves 1-3 are entirely gated on Wave 2 (the MEOS-1.4 bump) - the single universal unblock. No remaining correctness work; the gap is integration + maintainer-merge ordering."
}
109 changes: 109 additions & 0 deletions verify-train.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
# verify-train.sh - compose the dependency-ordered integration train
# (meta/integration-train.json) and run each wave's gate, so ecosystem-wide
# 100% parity is PROVABLE at one point instead of asserted per-PR.
#
# Operationalizes MobilityDB discussion #895 (wave-based merge plan).
#
# Honesty contract: a wave reports PASS only when its gate is just-run and
# green here; otherwise BLOCKED:<reason> with the exact gate it needs.
# Nothing is faked, nothing is skipped silently (green-ci-same-commit).
#
# Scope/safety: operates ONLY on this repo and its own ./_mobilitydb
# sparse checkout. NEVER touches a shared PyMEOS / MobilityDB working copy
# (work-independently-parallel-sessions).
#
# Usage:
# ./verify-train.sh # Wave 0 (fully automatable here) + honest
# # BLOCKED status for Waves 1-3
# PYMEOS_ENV=/path ./verify-train.sh # also run Wave 2/3 gates if a
# # bump-ready PyMEOS env is provided
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
cd "$HERE"
MDB="_mobilitydb"
RC=0
say() { printf '\n=== %s ===\n' "$*"; }
pass() { printf ' [PASS] %s\n' "$*"; }
fail() { printf ' [FAIL] %s\n' "$*"; RC=1; }
block() { printf ' [BLOCKED] %s\n' "$*"; }

# ---------------------------------------------------------------------------
say "WAVE 0 - MobilityDB core extended-type C surface (compose + gate)"
# Compose: linear stack tip (#1085 contains #1081..#1085) + the clean
# #1051->#951 pair cherry-picked on top (proven clean).
if [ ! -d "$MDB/.git" ]; then
fail "no $MDB checkout - run: python3 setup.py (then re-run)"
else
git -C "$MDB" fetch --no-tags --depth=20 origin \
pull/1085/head pull/1051/head pull/951/head >/dev/null 2>&1
STACK_TIP=$(git -C "$MDB" rev-parse FETCH_HEAD 2>/dev/null) # last fetched
git -C "$MDB" fetch --no-tags --depth=20 origin pull/1085/head >/dev/null 2>&1
STACK=$(git -C "$MDB" rev-parse FETCH_HEAD)
git -C "$MDB" fetch --no-tags --depth=20 origin pull/1051/head >/dev/null 2>&1
P1051=$(git -C "$MDB" rev-parse FETCH_HEAD)
git -C "$MDB" fetch --no-tags --depth=20 origin pull/951/head >/dev/null 2>&1
P951=$(git -C "$MDB" rev-parse FETCH_HEAD)
git -C "$MDB" checkout -q -B _train_w0 "$STACK" 2>/dev/null
CK=0
for c in $(git -C "$MDB" rev-list --reverse "$(git -C "$MDB" rev-parse "$P951"^^)..$P951"); do
git -C "$MDB" cherry-pick -x "$c" >/dev/null 2>&1 || { git -C "$MDB" cherry-pick --abort >/dev/null 2>&1; CK=1; }
done
[ "$CK" = 0 ] && pass "composed: stack #1081..#1085 + #1051->#951 (clean)" \
|| fail "Wave-0 compose conflict (#1051/#951 pair vs stack)"
# Sync headers (replicate setup.py step_sync, no git reset).
python3 - <<'PY'
import shutil, pathlib
src=pathlib.Path("_mobilitydb/meos/include"); dst=pathlib.Path("meos/include")
stub={"pg_config.h","postgres_int_defs.h","postgres_ext_defs.in.h"}
[shutil.copy2(h,dst/h.name) for h in src.glob("*.h") if h.name not in stub]
PY
if ! python3 -c 'import json,sys; json.load(open("meta/object-model.json"))' 2>/dev/null; then
fail "MEOS-API catalog anchor missing: this branch must be stacked on feat/object-model (PR #10). See meta/integration-train.json#/catalog_anchor."
fi
mkdir -p output
python3 run.py >/dev/null 2>"output/.train_run.log" \
&& pass "MEOS-API catalog regenerated" \
|| fail "run.py failed (see output/.train_run.log)"
python3 - <<'PY'
import json,sys
om=json.load(open("output/meos-idl.json"))["objectModel"]
f2c=om["functionToClass"]; n=om["summary"]["functionsTotal"]
exp={"tcbuffer_from_mfjson":"TCbuffer","tnpoint_from_mfjson":"TNpoint",
"tpose_from_mfjson":"TPose","tcbufferinst_make":"TCbufferInst",
"tposeinst_make":"TPoseInst"}
bad=[k for k,v in exp.items() if (f2c.get(k) or {}).get("class")!=v]
sys.exit(0 if (n==2699 and not bad) else 1)
PY
[ $? = 0 ] && pass "Wave-0 gate: 2699 fns, from_mfjson + ctors uniform" \
|| fail "Wave-0 gate: catalog count/classification mismatch"
python3 -m pytest tests/ -q >"output/.train_pytest.log" 2>&1 \
&& grep -q "21 passed" "output/.train_pytest.log" \
&& pass "Wave-0 gate: PR #10 object-model 21/21" \
|| fail "Wave-0 gate: PR #10 tests not 21/21 (see output/.train_pytest.log)"
fi

# ---------------------------------------------------------------------------
say "WAVE 1 - PyMEOS-CFFI MEOS-1.4 substrate"
block "needs Wave-1 env: regenerate PyMEOS-CFFI builder/meos-idl.json vs Wave-0 MEOS, then pymeos_cffi import (owner: live PyMEOS session)"

say "WAVE 2 - CRITICAL PATH: PyMEOS master health (MEOS-1.4 bump #81/#82)"
block "PyMEOS master broken vs MEOS master (geoset_*->spatialset_*, tpoint_*->tspatial_*, pgis_geometry_in->geom_in, spanset_make arity); pristine master 426 failed/2721 passed. Gate: test.yml 426->0 vs Wave-1/Wave-0. Owner: live PyMEOS session. Build the bump against the composed Wave-0, not bare master."

say "WAVE 3 - PyMEOS features (gated on Wave 2)"
if [ -n "${PYMEOS_ENV:-}" ] && [ -d "$PYMEOS_ENV" ]; then
block "PYMEOS_ENV given - run per-PR gates there: #85 black; #87 portable_parity 0-unbacked; #88 collect; #89 codegen --check; #90/#91 mixin suites; #84 tests/io 4/4 + footer==MobilityDuck#146 (manifest has exact gates)"
else
block "set PYMEOS_ENV=<bump-ready PyMEOS clone> to run #85/#87/#88/#89/#90/#91/#84 gates; all are green-in-isolation and gated only on Wave 2"
fi

# ---------------------------------------------------------------------------
say "SUMMARY"
if [ "$RC" = 0 ]; then
echo " Wave 0 PROVEN here. Waves 1-3 gated solely on Wave 2 (the MEOS-1.4 bump)."
echo " 100% parity is demonstrable the moment Wave 2 lands and this train"
echo " is re-run with PYMEOS_ENV set. Merge order: see meta/integration-train.json."
else
echo " Wave 0 gate FAILED above - parity train cannot proceed; fix before merge."
fi
exit "$RC"