Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e606ee7
Accept published PHP's operator metrics read across its redispatch steps
rmcdaniel Oct 23, 2026
0317e12
Merge branch 'main' into rust-325-opmetrics-race
rmcdaniel Oct 23, 2026
3fa5cd5
Bring the Rust port log's current state up to October 23
rmcdaniel Oct 23, 2026
41bb1e8
Serve message streams like Server 2.11.1
rmcdaniel Oct 23, 2026
6b6757e
Follow PHP's per-query read order in the redispatch model
rmcdaniel Oct 23, 2026
1414996
Accept condition waits without a key or fingerprint like PHP
rmcdaniel Oct 23, 2026
0ec63aa
Snapshot the task on every event PHP records with it
rmcdaniel Oct 23, 2026
05a6110
Describe each command's workflow class and preview flag like PHP
rmcdaniel Oct 23, 2026
1cbe01f
Wait for the query acknowledgement before stopping Python and Rust cl…
rmcdaniel Oct 23, 2026
ca0d23c
Merge remote-tracking branch 'origin/main' into rust-325-opmetrics-race
rmcdaniel Oct 23, 2026
37cc705
Merge remote-tracking branch 'origin/rust-325-history-presentation' i…
rmcdaniel Oct 24, 2026
911b89a
Wait for the expiry evidence before stopping the Python query client
rmcdaniel Oct 24, 2026
65772ab
Rust #325: preview omission reads native stored references; exact pre…
rmcdaniel Oct 24, 2026
dc1b4e0
Server parity: run the SQLite published-SDK patch record without PHP'…
rmcdaniel Oct 24, 2026
17f63ca
Server parity: arm the maintenance restore before the stop and keep i…
rmcdaniel Oct 24, 2026
38717d3
Rust #325: read a signal's stored reference from its signal record
rmcdaniel Oct 24, 2026
1702901
Server parity: name the maintenance container from the job identity (…
rmcdaniel Oct 24, 2026
ce14b38
Merge remote-tracking branch 'origin/rust-325-opmetrics-race' into ru…
rmcdaniel Oct 24, 2026
0ff26be
Merge remote-tracking branch 'origin/rust-325-query-ack-wait' into ru…
rmcdaniel Oct 24, 2026
42cfa99
Check native's unserved published routes against a recorded list
rmcdaniel Oct 24, 2026
22271b6
Server parity: activity cancellation acknowledgement fixture, recorde…
rmcdaniel Oct 24, 2026
ee55d34
Rust #325: activity cancellation acknowledgement, with cooperative de…
rmcdaniel Oct 24, 2026
252ffa4
Native tests and polls follow PHP's worker answers; retried-attempt a…
rmcdaniel Oct 24, 2026
a4f3057
Merge remote-tracking branch 'origin/rust-325-condition-fingerprint' …
rmcdaniel Oct 24, 2026
c4d65f6
Merge remote-tracking branch 'origin/rust-325-task-snapshots' into ru…
rmcdaniel Oct 24, 2026
1e7cbf3
Merge remote-tracking branch 'origin/rust-325-patch-cohort-schedule' …
rmcdaniel Oct 24, 2026
c8b65b3
Merge remote-tracking branch 'origin/rust-325-message-streams' into r…
rmcdaniel Oct 24, 2026
6bde945
Message streams are served: drop them from the unserved route list
rmcdaniel Oct 24, 2026
bf914f9
Compare activity lease renewals as instants, not RFC3339 text
rmcdaniel Oct 24, 2026
d24a001
Merge remote-tracking branch 'origin/main' into rust-325-train-oct24-b
rmcdaniel Oct 24, 2026
4f34e89
Bring the Rust port log's current state up to October 24
rmcdaniel Oct 24, 2026
ab20a21
Merge branch 'rust-325-train-oct24-b' into rust-325-train-oct24-c
rmcdaniel Oct 24, 2026
6f13284
Merge branch 'rust-325-train-oct24-c' into rust-325-activity-cancel-ack
rmcdaniel Oct 24, 2026
efc35b4
Rust #325: activity heartbeats answer times with microseconds, as sta…
rmcdaniel Oct 24, 2026
9c450ca
Rust #325: refuse cancellation deliveries at the wrong or a resolved …
rmcdaniel Oct 24, 2026
9c39ac0
Merge main (#746, #733) into the activity cancellation acknowledgemen…
rmcdaniel Oct 24, 2026
62be4bb
Merge remote-tracking branch 'origin/main' into rust-325-activity-can…
rmcdaniel Oct 25, 2026
6df4e73
Merge remote-tracking branch 'origin/main' into rust-325-activity-can…
rmcdaniel Oct 25, 2026
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
3 changes: 3 additions & 0 deletions .github/workflows/phpunit-feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ jobs:
- name: Require safe PHP image build context
run: node --test tests/Unit/PhpImageContextTest.mjs

- name: Restore the patch cohort's maintenance loop on every path
run: node --test tests/Unit/PatchCohortMaintenanceTest.mjs

- name: Run the required checks for merge queue groups
run: node --test tests/Unit/MergeGroupTriggersTest.mjs

Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/server-parity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ on:
- tests/Unit/ServerParityWorkerReplacementContractTest.mjs
- tests/Unit/ServerParityOperatorMetricsContractTest.mjs
- tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs
- tests/Unit/ServerParityActivityCancellationAckContractTest.mjs
- tests/Unit/ServerParityServiceCatalogContractTest.mjs
- tests/Unit/ServerParitySystemMetricsContractTest.mjs
- tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs
Expand All @@ -48,6 +49,7 @@ on:
- tests/Unit/ServerParityRepairPassContractTest.mjs
- tests/Unit/ServerParityClusterInfoContractTest.mjs
- tests/Unit/ServerParityNamespaceDeleteContractTest.mjs
- tests/Unit/ServerParityMessageStreamsContractTest.mjs
- tests/Unit/ServerParityWaitingHistoryContractTest.mjs
- tests/Unit/ServerParityTaskRetryContractTest.mjs
- tests/Unit/ServerParityPublishedPatchContractTest.mjs
Expand Down Expand Up @@ -1593,7 +1595,7 @@ jobs:
-e COMPOSER_HOME=/tmp/composer -v "$PWD:/source" -w /source "$image" \
install --working-dir=scripts/conformance/server-parity --no-interaction --no-progress
docker run --rm --user=1000:1000 --entrypoint node -v "$PWD:/source:ro" -w /source \
"$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParitySdkDirectionLanesTest.mjs tests/Unit/ServerParityCooperativeContractTest.mjs tests/Unit/ServerParityChildCancellationContractTest.mjs tests/Unit/ServerParityScheduleContractTest.mjs tests/Unit/ServerParityVisibilityContractTest.mjs tests/Unit/ServerParityAdmissionContractTest.mjs tests/Unit/ServerParityRoleTokenContractTest.mjs tests/Unit/ServerParityWorkerDeregistrationContractTest.mjs tests/Unit/ServerParitySearchAttributeContractTest.mjs tests/Unit/ServerParityWorkerListingContractTest.mjs tests/Unit/ServerParityWorkerReplacementContractTest.mjs tests/Unit/ServerParityPatchDeploymentContractTest.mjs tests/Unit/ServerParityWaitingHistoryContractTest.mjs tests/Unit/ServerParityTaskRetryContractTest.mjs tests/Unit/ServerParityPublishedPatchContractTest.mjs tests/Unit/ServerParityPublishedPatchTransportTest.mjs tests/Unit/ServerParityRecordFileTest.mjs tests/Unit/ServerParityLateWorkflowCompletionContractTest.mjs tests/Unit/ServerParityStorageTestContractTest.mjs tests/Unit/ServerParitySchedulePoliciesContractTest.mjs tests/Unit/ServerParitySignalsBeforeWorkerContractTest.mjs tests/Unit/ServerParityScheduleCatalogContractTest.mjs tests/Unit/ServerParityOperatorMetricsContractTest.mjs tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParitySystemMetricsContractTest.mjs tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs tests/Unit/ServerParityPrometheusMetricsContractTest.mjs tests/Unit/ServerParityPayloadCleanupContractTest.mjs tests/Unit/ServerParityRepairPassContractTest.mjs tests/Unit/ServerParityClusterInfoContractTest.mjs tests/Unit/ServerParityNamespaceDeleteContractTest.mjs > parity-evidence/comparator-tests.txt
"$image" --test tests/Unit/ServerParityRunnerTest.mjs tests/Unit/ServerParitySdkDirectionLanesTest.mjs tests/Unit/ServerParityCooperativeContractTest.mjs tests/Unit/ServerParityChildCancellationContractTest.mjs tests/Unit/ServerParityScheduleContractTest.mjs tests/Unit/ServerParityVisibilityContractTest.mjs tests/Unit/ServerParityAdmissionContractTest.mjs tests/Unit/ServerParityRoleTokenContractTest.mjs tests/Unit/ServerParityWorkerDeregistrationContractTest.mjs tests/Unit/ServerParitySearchAttributeContractTest.mjs tests/Unit/ServerParityWorkerListingContractTest.mjs tests/Unit/ServerParityWorkerReplacementContractTest.mjs tests/Unit/ServerParityPatchDeploymentContractTest.mjs tests/Unit/ServerParityWaitingHistoryContractTest.mjs tests/Unit/ServerParityTaskRetryContractTest.mjs tests/Unit/ServerParityPublishedPatchContractTest.mjs tests/Unit/ServerParityPublishedPatchTransportTest.mjs tests/Unit/ServerParityRecordFileTest.mjs tests/Unit/ServerParityLateWorkflowCompletionContractTest.mjs tests/Unit/ServerParityStorageTestContractTest.mjs tests/Unit/ServerParitySchedulePoliciesContractTest.mjs tests/Unit/ServerParitySignalsBeforeWorkerContractTest.mjs tests/Unit/ServerParityScheduleCatalogContractTest.mjs tests/Unit/ServerParityOperatorMetricsContractTest.mjs tests/Unit/ServerParityActivityLeaseRenewalContractTest.mjs tests/Unit/ServerParityActivityCancellationAckContractTest.mjs tests/Unit/ServerParityServiceCatalogContractTest.mjs tests/Unit/ServerParitySystemMetricsContractTest.mjs tests/Unit/ServerParityOperatorDashboardTypesContractTest.mjs tests/Unit/ServerParityPrometheusMetricsContractTest.mjs tests/Unit/ServerParityPayloadCleanupContractTest.mjs tests/Unit/ServerParityRepairPassContractTest.mjs tests/Unit/ServerParityClusterInfoContractTest.mjs tests/Unit/ServerParityNamespaceDeleteContractTest.mjs tests/Unit/ServerParityMessageStreamsContractTest.mjs > parity-evidence/comparator-tests.txt
docker run --rm --user=1000:1000 --cpus=1 --memory=256m --memory-swap=256m \
--network=none --entrypoint node -v "$PWD:/source:ro" -w /source \
"$image" --test tests/Unit/ServerParityCodecDeadlineLoadTest.mjs > parity-evidence/codec-load-tests.txt
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# October 23, optional condition wait fields

- **The gap.** Native refused an `open_condition_wait` command without a
`condition_key`, or without a `condition_definition_fingerprint` of the form
`sha256:` followed by 64 hex digits. Published PHP accepts both.
- **How PHP 2.11.1 does it.** `normalizeOpenConditionWaitCommand` treats the key
and the fingerprint as optional strings of any form. It trims them and leaves
them out when empty. `applyOpenConditionWait` records only the fields that
remain, on `ConditionWaitOpened` and on a condition timeout's
`TimerScheduled`. Run summaries name a keyless wait "Waiting for condition",
without a key. Observed live against the published image: a bare
`open_condition_wait`, a key of `" ready "` with the fingerprint
`"any form"`, and blank values all behave this way.
- **What native does now.** The same. The open wait projection stores no
target name for a keyless wait, and the run summary reads such a wait as
PHP does. A key or fingerprint that is not a string is still refused by the
completion transport rules.
- **How it was checked.** A new integration test covers a bare wait, a padded
key with an arbitrary fingerprint, blank values with a timeout, and a key
that is not a string. The 63 existing condition, timed-condition and
observation tests still pass.
64 changes: 64 additions & 0 deletions docs/rust-server-port-checkpoints/2026-10-23-message-streams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# October 23, message streams

- **The gap.** All three published SDKs declare `message-streams` from
worker protocol 1.15. Native refused a worker that asked for them, did not
serve the append and diagnostics routes, and ignored
`message_stream_cursors` and `message_stream_waits` on a completion.
- **How PHP 2.11.1 does it.** Learned from its source, then observed live
against the published image:
- Each accepted message is delivered to the current run as the
runtime-reserved signal `__durable_workflow_message_stream`, in the
append's transaction. The signal skips the declared-signal and argument
checks but not the pending-signal and payload ceilings.
- SDK receives open a condition wait keyed by the stream cursor. A stream
message wakes that wait.
- When a signal wakes a condition wait, PHP consumes it without
`SignalApplied`, unless the run's durable contract declares that signal.
This holds for any undeclared signal, not only stream messages.
- The stream cursor's `MessageCursorAdvanced` is recorded after the
completion, with the completed task's snapshot.
- On continue-as-new, the next run receives a payload-free cursor
checkpoint for each acknowledged stream, then every unconsumed message
again, by append order. The old run rejects the signals it had not
consumed.
- **What native does now.**
- `POST /api/workflows/{id}/message-streams/{stream}/messages`, with PHP's
validation order, refusals and diagnostics of malformed input.
- `GET /api/workflows/{id}/message-streams` and
`GET /api/workflows/{id}/message-streams/{stream}`.
- Completion cursors and waits: validated before anything changes,
recorded in the completion's transaction, handed over on continue-as-new.
- The worker capability is PHP's contract with `supported: true`, and
cluster info publishes `message_streams_contract`.
- Continue-as-new no longer refuses a run with message streams.
- **Native changes found on the way.**
- A signal consumed by a condition wait no longer records `SignalApplied`
unless it is declared, and keeps the wait's sequence.
- The instance-level `MessageCursorAdvanced` carries the task snapshot, as
PHP's does.
- A completion reports the run's next message task in
`created_task_ids`, as PHP does.
- **How it was checked.**
- The `message-streams` fixture: 15 appends, 8 refused completions, the
deliveries each worker read, both runs' full event sequences, and the
diagnostics at each step.
- Published PHP 2.11.1 and native pass it on SQLite. The probe's output
is the same size on both, and both runs' event sequences are identical.
- The contract test refuses 50 changed observations, among them a
`SignalApplied` for a consumed message, a missing next message task, a
redelivered consumed message and a missing cursor checkpoint.
- **Not done yet** (Server #325 follow-ups):
- **External payload references.** Native refuses a reference as stream
input with 501 `message_stream_external_payload_unavailable`. PHP keeps
the reference and resolves it when history reaches a worker.
- **Cleanup and upgrade.** Retention cleanup of streams
(`MessageStreamRetentionCleanup`) is not ported. Takeover of PHP data
with streams is still refused.
- **Cluster `capabilities` map.** Native has none yet, so
`capabilities.message_streams` is absent; that map is the cluster info
work.
- **History presentation, outside streams.** Task snapshots lack PHP's
`last_dispatch_attempt_at` and `last_dispatched_at`. A consumed signal's
`SignalReceived` lacks `workflow_sequence` in poll history.
- **Condition fingerprint.** Native requires `condition_definition_fingerprint`
on `open_condition_wait`; PHP accepts a wait without one.
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# October 23, operator metrics after PHP redispatches an overdue task

- **Found by.** On PR #673's parity run 39995620421, the Shared Server
fixtures failed on PostgreSQL 17 and MySQL 8.0. The diff came before
any PHP pass line, so published PHP 2.11.1 failed the
`operator-metrics` contract, not native. The failing stage was
`waiting_after_redispatch`.
- **The race in published PHP.** PHP's repair pass redispatches the
overdue ready task in two steps:
- `TaskWatchdog::recoverExistingTask` raises the task's
`repair_count` and projects the run summary, in one transaction. The
run is marked `repair_needed`.
- `TaskDispatcher::markDispatched` then stamps the dispatch, and
`refreshRunSummary` clears `repair_needed` in a separate step.

`OperatorMetrics::snapshot` reads runs, then tasks, then activities,
then the backlog, with no shared snapshot. `HealthCheck` takes its own
snapshot after the metrics request. So one response can mix states.
- **The four PHP states.** In order, the overdue task is:
0. overdue and unrepaired;
1. overdue, repaired, run marked `repair_needed`;
2. dispatched, run still marked `repair_needed`;
3. dispatched, run summary refreshed.

The old contract accepted states 0, 1 and 3 read consistently. It
refused state 2 (the MySQL 8.0 shape) and a backlog read after the
redispatch while the tasks were read before it (the PostgreSQL 17
shape).
- **First model, refused in review.** The first correction (head
`3fa5cd5288`) let each section of the response show one state, with
sections never going backwards. Security's review (#708 comment
6373392835) showed this was not enough. Published Workflow `aca7928484`
reads each value with its own query, even inside one section. So a
forward-only change can split the `tasks` section: `dispatch_overdue`
at state 0, `max_repair_count` at state 1, and `unhealthy` at state 2.
The timestamps are read before the counts, so a timestamp can also
outlive its count.
- **Contract decision.** For published PHP only, the
`waiting_after_redispatch` stage follows PHP's actual query order. The
reads that depend on the repair state are, in order:
- runs: `oldest_repair_needed_at`, then `repair_needed`;
- tasks: `oldest_dispatch_overdue_since` (with `oldest_unhealthy_at`,
which PHP builds from the same timestamps), then `dispatch_overdue`,
then `max_repair_count`, then `unhealthy`, counted again;
- backlog: `unhealthy_tasks`, then `repair_needed_runs`.

The response passes when some sequence of states that never goes
backwards in that order produces exactly its counts and timestamps. The
health request reads its own snapshot afterwards, starting at or after
the response's last read. Each health warning follows the field it
reads:
- `task_transport` follows `tasks.unhealthy`;
- `routing_health` follows `tasks.dispatch_overdue`;
- `durable_resume_paths` follows `backlog.repair_needed_runs`.

Native runs no repair loop, so native must still report the overdue
task consistently, with each timestamp present exactly when its count
is. A native response that shows a redispatch must show it as one
settled state.
- **How it was checked.**
- Security's counterexample passes for PHP and fails for native. So
does the timestamp kept from before a redispatch.
- New tests refuse each backward step: a recount that is overdue after
the dispatch count was not; a backlog that is overdue after the
recount was not; an overdue count after its timestamp showed the
redispatch; a run timestamp from a repair the later reads have not
reached; and health whose recount is overdue after its dispatch count
was not.
- An exhaustive test covers all 256 combinations of the eight values
that depend on the state. The 37 combinations that some forward-only
order produces pass; the other 219 fail.
- Two earlier tests assumed one read per section. They now accept
health whose backlog was read after the run was projected again, and
whose dispatch count was read before the redispatch. A native
response still fails both.
- The original CI shapes (MySQL 8.0 state 2, PostgreSQL 17 backlog
after the redispatch) still pass. The server-parity unit tests pass:
5,502 of 5,504, with one intended skip and one test that needs Docker
inside its container.
- **Not changed.** The fixture, its stages and the other stages' expected
values. Native's behaviour is unchanged.
Loading
Loading