You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add stable logical aliases for immutable concrete flow versions, and represent deployment metadata with a DeployedFlow wrapper.
Callers start a stable alias such as greetUser. Startup deployment resolves that alias to one concrete version such as greetUserV2. Runs, task state, broadcasts, and history store the resolved concrete flow_slug.
alias is deployment metadata, not DAG behavior. It therefore belongs on DeployedFlow, not Flow.
A plain Flow remains supported and resolves to a default deployment whose alias and queue are both flow.slug.
This issue does not add named queue routing. The wrapper is the ownership boundary that a later routing issue will extend.
Motivation
Changing flow topology correctly requires a new concrete slug. Changing every application call, trigger, cron job, and SQL function to that slug is repetitive and error-prone.
The design separates:
flow_slug
immutable concrete version and persisted runtime identity
flow_alias
stable dispatch name with one active concrete target
DeployedFlow
code declaration that connects a Flow to deployment metadata
Worker startup becomes the only supported deployment path.
Remove compilation: false; no supported alternate compilation path remains.
The public compileFlow() helper may remain, but it must accept the same Flow | DeployedFlow contract or be deprecated. It must not become a second deployment model.
defineDeployedFlow() must preserve the exact Flow type. It must not add another generic parameter to Flow or weaken handler, dependency, condition, input, or output inference.
Data model
Add immutable alias membership and a separate active pointer:
pgflow.flows
flow_slug primary key
flow_alias not null
unique (flow_slug, flow_alias)
pgflow.flow_aliases
flow_alias primary key
flow_slug not null
created_at timestamptz not null
updated_at timestamptz not null
foreign key (flow_slug, flow_alias)
references pgflow.flows (flow_slug, flow_alias)
Invariants:
Every concrete flow has exactly one immutable alias membership.
Omitted aliases resolve to the concrete slug.
Multiple concrete flows may share one alias.
Exactly one concrete flow is active for each alias.
An existing concrete slug can never move to another alias.
Existing installations backfill every flow as a self-alias.
The upgrade must not infer version families from slug suffixes.
Startup deployment and activation
Worker startup deploys or verifies the complete DeployedFlow before worker registration.
Compile or verify the complete concrete flow definition.
Activate the alias only when this is a brand-new concrete slug.
Commit before worker registration starts.
Activation rules:
Compiling a brand-new concrete slug activates it.
Rechecking the active concrete slug preserves activation.
Rechecking an inactive concrete slug keeps it inactive.
Restarting an old worker never reclaims the alias.
Local same-slug destructive recompilation preserves prior activation state.
Alias membership mismatch always fails, including local development.
If worker startup fails after activation commits, keep the alias active. Durable tasks wait for worker recovery.
Two brand-new versions for one alias may compile concurrently. The final alias-lock holder wins. Document that one rollout must deploy only one new concrete version per alias.
Startup results and logs must distinguish compilation from activation. At minimum, expose:
compilation: compiled | verified | recompiled
activation: activated | active | inactive
alias
active concrete slug
An inactive version may still need a worker to drain existing tasks, so inactivity must not fail startup.
Start APIs
Move the current concrete implementation behind start_flow_by_slug() and make alias resolution the default:
start_flow() alias
start_flow_by_alias() alias, explicit
start_flow_by_slug() concrete slug
start_flow_with_states() alias
start_flow_with_states_by_slug() concrete slug
Compatibility rules:
Keep the existing flow_slug RPC argument name on start_flow() and start_flow_with_states().
It must validate immutable membership, take the alias lock, and atomically switch the pointer. It must not require a healthy worker; work may queue until workers recover.
Routing will be immutable for one concrete slug. A route change creates a new concrete version and activates it through the alias. Existing tasks retain their original queue snapshots.
This changes the current ownership statement:
Runs, task state, and history remain bound to concrete slugs.
Tasks retain concrete slugs and queue snapshots.
Queues may serve multiple concrete flows.
Workers may poll one queue and dispatch several concrete flows.
Compatibility ownership
All versions behind one alias must accept compatible flow input. The user owns this invariant.
Do not add input schemas, schema hashes, runtime compatibility checks, or activation history in this issue.
Acceptance criteria
defineDeployedFlow(flow, { alias }) creates a typed deployment declaration without changing Flow generics.
EdgeWorker.start(flow) retains current self-alias and flow-slug queue behavior.
Every stored concrete flow has immutable, non-null alias membership.
The upgrade backfills existing flows as self-aliases without suffix inference.
Exactly one active concrete target exists per alias.
Startup compiles and activates a new concrete slug in one transaction.
Existing active and inactive versions preserve activation on restart.
Alias mismatch fails in every environment.
Alias and concrete locks use one documented order.
Summary
Add stable logical aliases for immutable concrete flow versions, and represent deployment metadata with a
DeployedFlowwrapper.Callers start a stable alias such as
greetUser. Startup deployment resolves that alias to one concrete version such asgreetUserV2. Runs, task state, broadcasts, and history store the resolved concreteflow_slug.aliasis deployment metadata, not DAG behavior. It therefore belongs onDeployedFlow, notFlow.A plain
Flowremains supported and resolves to a default deployment whose alias and queue are bothflow.slug.This issue does not add named queue routing. The wrapper is the ownership boundary that a later routing issue will extend.
Motivation
Changing flow topology correctly requires a new concrete slug. Changing every application call, trigger, cron job, and SQL function to that slug is repetitive and error-prone.
The design separates:
Prerequisite
compilation: false; no supported alternate compilation path remains.The public
compileFlow()helper may remain, but it must accept the sameFlow | DeployedFlowcontract or be deprecated. It must not become a second deployment model.Public API
Backward compatibility:
is equivalent to a default deployment:
defineDeployedFlow()must preserve the exactFlowtype. It must not add another generic parameter toFlowor weaken handler, dependency, condition, input, or output inference.Data model
Add immutable alias membership and a separate active pointer:
Invariants:
Startup deployment and activation
Worker startup deploys or verifies the complete
DeployedFlowbefore worker registration.Use one lock order everywhere:
Within one transaction:
Activation rules:
Two brand-new versions for one alias may compile concurrently. The final alias-lock holder wins. Document that one rollout must deploy only one new concrete version per alias.
Startup results and logs must distinguish compilation from activation. At minimum, expose:
An inactive version may still need a worker to drain existing tasks, so inactivity must not fail startup.
Start APIs
Move the current concrete implementation behind
start_flow_by_slug()and make alias resolution the default:Compatibility rules:
flow_slugRPC argument name onstart_flow()andstart_flow_with_states().Client behavior:
PgflowClient.startFlow()andPgflowSqlClient.startFlow()use alias semantics.startFlowBySlug()for pinned starts.startFlowByAlias()because alias dispatch is already the default.FlowRun.applySnapshot()must replace its initially requested alias with the resolvedrun.flow_slug.Rollback and deletion
Add:
It must validate immutable membership, take the alias lock, and atomically switch the pointer. It must not require a healthy worker; work may queue until workers recover.
Keep
delete_flow_and_data()concrete-slug based:Deletion rules:
drop_alias => truesucceeds only when the supplied concrete slug is the alias's sole version.Future queue-routing contract
Named queue routing will extend
DeployedFlow, notFlow:Routing will be immutable for one concrete slug. A route change creates a new concrete version and activates it through the alias. Existing tasks retain their original queue snapshots.
This changes the current ownership statement:
Compatibility ownership
All versions behind one alias must accept compatible flow input. The user owns this invariant.
Do not add input schemas, schema hashes, runtime compatibility checks, or activation history in this issue.
Acceptance criteria
defineDeployedFlow(flow, { alias })creates a typed deployment declaration without changingFlowgenerics.EdgeWorker.start(flow)retains current self-alias and flow-slug queue behavior.compilation: falseand its documentation are removed.Out of scope