refactor: make worker startup the only flow compilation path - #672
Conversation
Remove migration-based flow compilation ahead of queue identity (#650) and per-step private queues (#651): every flow worker now compiles or verifies its imported definition before it tracks its function or inserts its worker row, so a failed startup registers nothing. Removed surfaces: public compileFlow() and compareFlowShapes() from @pgflow/dsl, the ControlPlane edge function and its root export, the 'pgflow compile' CLI command, the ControlPlane installer step, all supabase/functions/pgflow fixtures, FlowWorkerConfig.compilation (including allowDataLoss), and the callable production data-loss overload pgflow.ensure_flow_compiled(text, jsonb, boolean) — the new Atlas migration drops it explicitly, which makes this a fenced upgrade: stop old workers before applying it. Kept: extractFlowShape() and FlowShape types, SQL shape comparator, _create_flow_from_shape, create_flow, add_step, delete_flow_and_data. Local same-slug mismatch still recompiles destructively (hot reload); production mismatch returns 'mismatch' without touching data. Docs now describe startup-only deployment everywhere current; legacy URLs redirect to /concepts/startup-compilation/ and /deploy/update-pgflow/ documents the breaking migration procedure. Plan: Plans/2026-09-07-issue-647-startup-only-compilation.md (run: Runs/issue-647-startup-only-compilation). 90 files, +501/-4683.
🦋 Changeset detectedLatest commit: 04aeed1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
View your CI Pipeline Execution ↗ for commit 58cf585
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
jumski
left a comment
There was a problem hiding this comment.
See inline comments.
| value: { status: 'blocked' }, | ||
| }, | ||
| }); | ||
| }); |
There was a problem hiding this comment.
we replaced so many removed lines in this test with a single test?
There was a problem hiding this comment.
The removed lines were the describe("compareFlowShapes") block — tests of the TypeScript comparator (compareFlowShapes(), compareSteps(), ShapeComparisonResult), which this PR deletes. Comparison now lives only in PostgreSQL, and that behavior keeps its coverage in the retained pgTAP suites: pkgs/core/supabase/tests/compare_flow_shapes/*.test.sql plus the four ensure_flow_compiled behavior tests. The one added test covers the surviving extraction side: structural modes, if/ifNot patterns, whenUnmet/whenExhausted landing in the shape that feeds the SQL comparator. So tests of deleted code were deleted; live behavior stays covered (SQL side), extraction keeps existing tests plus this one.
| }); | ||
| }); | ||
|
|
||
| describe('compileFlow includes when_exhausted parameter', () => { |
There was a problem hiding this comment.
so we have this behaviour tested for extractFlowShape and ensure_flow_compiled?
There was a problem hiding this comment.
Yes, both ends are covered:
extractFlowShape: the new test inflow-shape.test.ts("extracts structural modes and patterns for startup compilation") assertswhenUnmet: "skip-cascade",whenExhausted: "skip", and therequiredInputPattern/forbiddenInputPatternwrappers land in the extracted shape. The no-options default case is asserted in the next test (toEqualwithwhenUnmet: "skip",whenExhausted: "fail",defined: falsewrappers).ensure_flow_compiled/ SQL comparison: pgTAPcompare_flow_shapes/condition_mode_drift.test.sqlandpattern_differences.test.sqlassert drift in these fields is detected, and theensure_flow_compiledsuites cover matching/mismatching/recompile behavior end to end.
What this file keeps is its actual job: DSL acceptance — the options are accepted and stored on the step definition. The deployment chain around them is tested where it lives.
| | ✅ Adjusting retry parameters | ❌ Adding/removing steps | | ||
| | ✅ Updating timeout values | ❌ Changing step dependencies | | ||
| | ✅ Tuning delay and worker concurrency options | ❌ Changing step types | | ||
| | ✅ Behavior-preserving refactors of handler code | ❌ Changing step slug names | |
There was a problem hiding this comment.
why you replaced those?
There was a problem hiding this comment.
Two fixes from the final review round, both mechanical:
- The breaking column listed "Changing step dependencies" twice (rows 3 and 4 of the old table — a leftover duplicate). The second instance became "Changing step types", which is a genuine shape change (single ↔ map) the comparator detects.
- "Tuning delay and concurrency options" was ambiguous — the flow DSL has no concurrency option. It is now "worker concurrency", which is a worker setting (maxConcurrent), not part of the flow shape, so tuning it is safe under the same slug.
|
|
||
| The original timestamp allows pgflow to search for and detect which migrations have already been installed, while the new timestamp prefix ensures Supabase can apply the migration without timestamp ordering errors. | ||
|
|
||
| ## Remove manual flow compilation |
There was a problem hiding this comment.
this section should go to the news article about this release
There was a problem hiding this comment.
Done in d5b53c0. The full procedure now lives in the new release article /news/pgflow-0-16-0-startup-only-flow-compilation/ (what is removed, fenced-upgrade warning, HTTP and process worker procedures, legacy file/config cleanup). update-pgflow.mdx keeps a short evergreen summary plus a link to the article. Version slug follows the changeset minor bump (0.15.1 → 0.16.0) — rename if the released number differs.
PR review asked for the fenced-upgrade procedure to live in the release news article instead of the evergreen update guide. The new pgflow 0.16.0 article carries the full one-time procedure; update-pgflow keeps a short summary plus a link. Version slug follows the changeset minor bump (0.15.1 -> 0.16.0); rename if the released number differs.
The news section deploys on merge to main, but the 0.16.0 npm release only exists after the Version Packages flow runs. Keep the article as a draft until the release is cut, and make the update-guide pointer plain text so no link targets an unpublished page. Undraft (and re-date) when the release goes out; rewrite if the release ends up batching the queue epic instead.
Add a prominent danger alert to the draft 0.16.0 release article. It lists every removed surface users may still depend on and puts the stop-and-drain requirement before the migration instructions.
Make the startup-only compilation article visible when this branch lands. The danger alert now tells users which removed surfaces to check before the fenced upgrade.
🚀 Production Deployment: Website✅ Successfully deployed to production! 🔗 Production URL: https://pgflow.dev 📝 Details:
Deployed at: 2026-09-08T01:24:28+02:00 |
🔍 Preview Deployment: Website✅ Deployment successful! 🔗 Preview URL: https://pr-672.pgflow.pages.dev 📝 Details:
_Last updated: _ |

refactor: make worker startup the only flow compilation path
Implements #647 ahead of queue identity (#650) and per-step private queues (#651).
Summary
worker_functionsrow.compileFlow()andcompareFlowShapes()from@pgflow/dsl, the ControlPlane edge function and its root export, thepgflow compileCLI command, the ControlPlane installer step, allsupabase/functions/pgflow/fixtures, and the completeFlowWorkerConfig.compilationproperty (includingallowDataLoss).pgflow.ensure_flow_compiled(flow_slug, shape)keeps two arguments only. The new Atlas migration explicitly dropspgflow.ensure_flow_compiled(text, jsonb, boolean), so production can no longer authorize destructive recompilation.mismatchwithout touching data. Structural or unobserved semantic changes use a new concrete slug.extractFlowShape(),FlowShapetypes, SQL_compare_flow_shapes(),_create_flow_from_shape(),create_flow(),add_step(),delete_flow_and_data()./concepts/startup-compilation/is canonical, legacy URLs redirect there, and the pgflow 0.16.0 release notes document the fenced breaking-upgrade procedure.@pgflow/core,@pgflow/dsl,@pgflow/client,@pgflow/edge-worker,pgflow) as minor.Breaking: the migration removes the old three-argument SQL signature — stop old workers before applying it, and upgrade the pgflow package set together. See "Remove manual flow compilation" in the update guide.
Checks
All run locally on this branch:
pnpm nx verify-migrations core,pnpm nx gen-types core,pnpm nx verify-gen-types core --skip-nx-cache— passpnpm nx test:pgtap core— 1348/1348 pass (incl. new two-arg signature test)pnpm nx run-many -t lint build --projects=cli,dsl,edge-worker,client— passpnpm nx test cli/test dsl(337) /test client— passpnpm nx test:unit edge-worker(271) /test:types edge-worker/test:integration edge-worker(61/61) — passpnpm nx e2e cli/e2e edge-worker(12/12) /e2e client(47/47) — passpnpm nx verify-exports edge-worker/client, publish dry runs (npm/JSR) — passpnpm nx build website— 133 pages, starlight-links-validator passcompilecommand; packed exports contain noControlPlane/compileFlow/compareFlowShapesCloses #647