Durable slice 4 reference workload
Source of truth
This is the first-success and conformance workload for the source-only PostgreSQL Flow engine (slice 4). It proves atomic Flow instance creation, transition step evaluation, external event delivery, timer expiry, child Work activity integration, recovery invariants across 11 protocol boundaries, options reuse, RLS scope isolation, and operator release/disable safety. The timer-winner boundary is certified by force-terminating a separate process after the database commit; the other Flow boundaries use transaction rollback and fresh processor instances. The existing slice-3 workload separately certifies child Work process loss after an effect permit.
Slice 4 starts no background worker, polling loop, scheduler, hosted service, or automatic migration. The test harness manually drives each step and operation. Production activation remains slice 6.
Success target and prerequisites
- At most 5 minutes warm with PostgreSQL ready; at most 10 minutes cold with Docker.
- .NET 10 SDK and either Docker or a dedicated PostgreSQL 17.5 connection in
APPSURFACE_POSTGRES_TEST_CONNECTION. - Filtered test run executing
DurableSlice4ReferenceWorkloadTests. A skipped or zero-test run is not success.
The Docker path uses the immutable multi-platform image
postgres:17.5@sha256:aadf2c0696f5ef357aa7a68da995137f0cf17bad0bf6e1f17de06ae5c769b302.
Use a disposable database. The workload requires forward-only Flow schema through 0003_flow_protocol; the current
provider may also apply later compatible migrations such as the Work-first Schedule 0004_schedule_protocol. This
workload does not exercise those later protocol facts and supplies no destructive down migration.
Run the proof
From the repository root using .NET:
dotnet restore ForgeTrust.AppSurface.slnx --locked-mode
dotnet test \
Durable/ForgeTrust.AppSurface.Durable.PostgreSql.Tests/ForgeTrust.AppSurface.Durable.PostgreSql.Tests.csproj \
--configuration Release \
--no-restore \
--filter FullyQualifiedName~DurableSlice4ReferenceWorkloadTests \
--logger "console;verbosity=normal"
From the repository root using the verification shell script with the --flow flag:
./Durable/verify-postgresql.sh --quick --flow
For strict CI verification across all PostgreSQL integration tests including Flow:
./Durable/verify-postgresql.sh --ci --flow
APPSURFACE_POSTGRES_TEST_ALLOW_SKIP=true is a local-only escape hatch and is rejected when CI=true.
What the workload proves
- Schema Deployment: Migration owner checks status, applies the current reviewed forward migrations (including the
Flow prerequisites
0001_work_shared.sql,0002_forced_rls.sql, and0003_flow_protocol.sql), readsStoreId, and explicitly initializes the runtime epoch. - Atomic Flow Start:
PostgreSqlDurableFlowClientcommits each Flow start atomically in its own short transaction. Slice 4 exposes no caller-owned Flow transaction API. Re-usingstart_idempotency_keywith identical payload returnsDuplicate; divergent definition or a new start identity targeting an existing Flow instance returnsASDUR206. - Step Evaluation & Determinism: Step evaluation advances Flow state machine (
ready->evaluating) and verifies definition fingerprint SHA-256 against registered code. - Child Work Activity Lifecycle: Flow enqueues child activity Work item in
appsurface_durable.workand enterswaiting_activity. Work engine claims item, acquires effect permit, executes provider I/O, and commits terminal fact. Completion handler resolvesflow_wait(activity_completed) and returns Flow toready. - External Event Delivery: Incoming event delivers atomically via
flow_command, resolves active wait (event_won), supersedes scheduled timer (timer_wonlost), and updates state toready. Exact command/event retries returnDuplicate; divergent reuse returnsASDUR207. - Timer Expiry: Scheduled timer fires when
due_at <= clock_timestamp(), resolves wait (timer_won), supersedes event wait, and updates state toready. - 11 Recovery Boundaries (boundary 9 is a forced child-process termination; the others are transactional/fresh-processor proofs):
- Boundary 1 (Start pre-commit): Transaction rollback leaves no state; safe caller retry.
- Boundary 2 (Start post-commit):
flow_instancedurable inreadystate at revision 1. - Boundary 3 (Evaluation in-flight): Crash before child accept rolls back step transaction; state remains
ready. - Boundary 4 (Child activity accepted): Work item durable in
dispatch; Flow durable inwaiting_activity. - Boundary 5 (Child activity permit acquired): Work effect permit committed; Flow remains in
waiting_activity. - Boundary 6 (Child activity completed, Flow evaluation pending): Work completion committed (
activity_completed); Flow statereadywith result payload ready for next step evaluation. - Boundary 7 (Event delivery pre-commit): Rollback leaves
event_idunconsumed and wait active. - Boundary 8 (Event delivered, Flow evaluation pending): Event command accepted (
event_won), Flow statereadyawaiting step evaluation. - Boundary 9 (Timer fired, Flow evaluation pending): Timer updated to
fired(timer_won), Flow statereadyawaiting step evaluation. - Boundary 10 (Flow suspended): Instance durable in
suspendedstate withsuspended_from_statepreserved. - Boundary 11 (Flow terminal): Instance durable in
completed,faulted, orcanceledstate; subsequent commands failalready_terminal/ASDUR110.
- Safety Suspension & Operator Release: Non-restorable child activity failure or code mismatch causes safety suspension (
suspended). Operator release command (release) validates authorized epoch, clears suspension, and restores state. - Scope Disabling: Disabling scope tombstones scope (
state = 'disabled') and suspends all non-terminal Flow instances in scope.
Application sequence
The compiled reference workload requires:
- Construct
PostgreSqlDurableRuntimeSchemaManagerwith a migration-owner data source. - Call
GetStatusAsync,ApplyAsync(applying current reviewed forward migrations; Slice 4 requires Flow facts through0003_flow_protocol.sql), andInitializeRuntimeEpochAsync; capture StoreId and active epoch. - Construct
PostgreSqlDurableWorkOptionswithRuntimeEpochandExpectedStoreId. - Construct
PostgreSqlDurableFlowClientwith the scoped data source, Flow registry, payload codec registry, and shared PostgreSQL options. - Invoke
IDurableFlowClient.StartAsyncorIDurableFlowClient.RaiseEventAsync. The manually driven Slice 4 processor discovers and evaluates steps throughPostgreSqlDurableFlowProcessor.DiscoverAsyncandTryProcessAsync; no publicExecuteStepAsyncAPI exists.
Failure interpretation
- Preflight/domain outcomes preserve caller transaction usability as specified by the Flow protocol.
ASDUR200means definition unavailable;ASDUR201means history/definition mismatch;ASDUR203means aggregate revision race lost;ASDUR204means duplicate event ID;ASDUR206means start conflict;ASDUR207means command or event identity conflict.ASDUR400-ASDUR403require deployment correction via schema manager, not runtime DDL.
See the diagnostics catalog,
Flow protocol, and slice 4 reconstruction ledger.
Slice 6 comparison gate
Slice 6 must run this workload through hosted activation with identical safety evidence and fewer manual steps. If that path is not materially simpler or safer than established workflow alternatives, pause for an explicit go/no-go decision before publication or further scope expansion.