Skip to content
Merged
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
18 changes: 9 additions & 9 deletions docs/rust-server-port.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,25 @@ Updated October 27, 2026.
fixtures accept published PHP's typed `429 long_poll_capacity_exhausted`
poll refusal, and retry a locked Composer install after a transient
download failure (the two #813 merge-group failures).
- Native's v1 projection byte meter in Core's canonical unit (#770) is
prepared on `rust-325-wip/canonical-history-bytes`. It lands with the
first reference that carries #812, because `waterline-import` records
`history_size_bytes` from the reference.
- `bridge-adapters` compares exactly: published 2.11.7 accepts and
deduplicates the bridge update (#747) and refuses a foreign instance as
unavailable (#777), so the published-defect answers of 2.11.1 to 2.11.5
are no longer accepted.
- **Still refused at takeover:** workflow-to-workflow messages
(`workflow_messages`, written only by embedded PHP workflows) and external
payload references. PHP keeps references in about 30 table and column
locations in their URI form, so taking them over is its own project; the
recommendation is that the release candidate keeps refusing them.
- **Shared Server fixtures.** Native, embedded and published PHP 2.11.5 are
- **Shared Server fixtures.** Native, embedded and published PHP 2.11.7 are
compared on SQLite, MySQL 8.0, MariaDB 10.11 and PostgreSQL 17. Where the
published reference itself misbehaves, an explicit boundary separates it
from native, and the PHP owner has an issue for each case. Recent ones:
- #777, a bridge start for an instance another namespace holds: 2.11.1
to 2.11.4 name that namespace's type, 2.11.5 the namespace difference;
#811 (after 2.11.5) answers "unavailable";
- #747, a bridge update to an SDK-worker workflow;
- #794, workflow-bound Nexus handlers.

Answered as intended by 2.11.7, so `bridge-adapters` compares exactly:
#747 (a bridge update to an SDK-worker workflow) and #777 (a bridge start
for an instance another namespace holds).

Closed by 2.11.5: Workflow #931 (`service-calls` repeats a key from
another caller again, now refused 403), and the SQLite maintenance pause
for `update-validation` (#723, #726). `service-calls` still records apart
Expand Down
27 changes: 6 additions & 21 deletions scripts/conformance/server-parity/bridge-adapters-contract.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import assert from 'node:assert/strict';

// Reviewed bridge adapter webhooks (BridgeAdapterController), recorded
// against published Server 2.11.1. The fixture's `expected` holds the whole
// against published Server 2.11.7. The fixture's `expected` holds the whole
// normalized observation, with the probe's workflow prefix as `{id}`, run IDs
// as labels and derived workflow IDs as `{derived-<key>}`. Every step
// compares exactly.
//
// Published defect boundaries (fixture `published_defects`): a bridge update
// skips external update admission, so an update to an SDK-worker workflow is
// rejected workflow_definition_unavailable; and a start for an id another
// namespace holds names that namespace's type (published 2.11.1 to 2.11.4)
// or the namespace difference (2.11.5), not the intended unavailable
// (Server #777). At each
// defect's steps the published answers or the intended ones are accepted, as
// a set per defect; nothing else is relaxed.
// Published 2.11.1 to 2.11.5 answered two sets of steps otherwise: a bridge
// update to an SDK-worker workflow was rejected workflow_definition_unavailable
// (Server #747), and a start for an id another namespace holds named that
// namespace's type or the namespace difference (Server #777). 2.11.7 gives
// the intended answers, so neither is accepted any more.
export function checkBridgeAdapters(fixture, observation, workflowId) {
const state = observation.bridge_adapters;
const projection = {scope: 'bridge_adapters'};
Expand All @@ -25,18 +22,6 @@ export function checkBridgeAdapters(fixture, observation, workflowId) {
}
const definition = fixture.bridge_adapters;
const actual = JSON.parse(JSON.stringify(state).replaceAll(workflowId, '{id}'));
for (const {published: defect} of Object.values(definition.published_defects)) {
const steps = Object.keys(defect);
let published = true;
try {
for (const step of steps) assert.deepStrictEqual(actual[step], defect[step]);
} catch {
published = false;
}
if (published) {
for (const step of steps) actual[step] = definition.expected[step];
}
}
for (const [step, expected] of Object.entries(definition.expected)) {
assert.deepStrictEqual(actual[step], expected, `bridge adapters ${step}`);
}
Expand Down
150 changes: 1 addition & 149 deletions tests/Fixtures/ServerParity/bridge-adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,154 +34,6 @@
"bridge_adapters": {
"workflow_type": "parity.v1.bridge",
"empty_blob": "wwHioz3/VYAiNwwA",
"published_defects": {
"update_skips_external_admission": {
"issue": "https://github.com/durable-workflow/server/issues/747",
"summary": "BridgeAdapterController::updateWorkflow calls WorkflowControlPlane::update directly and skips ExternalWorkflowUpdateAdmission, which WorkflowController::update tries first, so a bridge update to a workflow served by an SDK worker is rejected workflow_definition_unavailable and recorded as UpdateRejected, and its replay is rejected again.",
"published": {
"update": {
"status": 422,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "update_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "workflow_definition_unavailable",
"idempotency_key": "u-2",
"target": {
"workflow_id": "{id}-bra-order",
"update_name": "bump"
},
"workflow_id": "{id}-bra-order",
"run_id": "run-2",
"control_plane_outcome": "rejected_workflow_definition_unavailable"
}
},
"update_again": {
"status": 422,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "update_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "workflow_definition_unavailable",
"idempotency_key": "u-2",
"target": {
"workflow_id": "{id}-bra-order",
"update_name": "bump"
},
"workflow_id": "{id}-bra-order",
"run_id": "run-2",
"control_plane_outcome": "rejected_workflow_definition_unavailable"
}
},
"history_event_types": [
"StartAccepted",
"WorkflowStarted",
"StartAccepted",
"StartRejected",
"SignalWaitOpened",
"SignalReceived",
"SignalReceived",
"UpdateRejected",
"UpdateRejected",
"UpdateRejected"
]
}
},
"foreign_instance_names_type": {
"issue": "https://github.com/durable-workflow/server/issues/777",
"summary": "DefaultWorkflowControlPlane::reserveInstance checks a reserved instance's type before its namespace, so a bridge start for an id another namespace holds is refused with that namespace's durable type named, and with a different-namespace message when the types match. Native checks the namespace first and refuses a foreign instance as unavailable, naming neither.",
"published": {
"start_other_type": {
"status": 422,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "start_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "unknown_target",
"idempotency_key": "k-6",
"message": "Workflow instance [{id}] is reserved for durable type [parity.v1.echo] and cannot be reused for [parity.v1.bridge].",
"target": {
"workflow_id": "{id}",
"workflow_type": "parity.v1.bridge",
"task_queue": "{id}-bra-queue"
}
}
},
"start_other_namespace": {
"status": 422,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "start_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "unknown_target",
"idempotency_key": "k-7",
"message": "Workflow instance [{id}] cannot be reused with a different namespace.",
"target": {
"workflow_id": "{id}",
"workflow_type": "parity.v1.echo",
"task_queue": "{id}-bra-queue"
}
}
}
}
},
"foreign_instance_names_namespace": {
"issue": "https://github.com/durable-workflow/server/issues/777",
"summary": "Published 2.11.5's Core 2.15.1 checks a reserved instance's namespace before its type, but its BridgeAdapterController still echoes the control plane's message, so a bridge start for an id another namespace holds is refused with a different-namespace message whatever its type. Server #811 (after 2.11.5) refuses it as unavailable, naming neither the type nor the namespace.",
"published": {
"start_other_type": {
"status": 422,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "start_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "unknown_target",
"idempotency_key": "k-6",
"message": "Workflow instance [{id}] cannot be reused with a different namespace.",
"target": {
"workflow_id": "{id}",
"workflow_type": "parity.v1.bridge",
"task_queue": "{id}-bra-queue"
}
}
},
"start_other_namespace": {
"status": 422,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "start_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "unknown_target",
"idempotency_key": "k-7",
"message": "Workflow instance [{id}] cannot be reused with a different namespace.",
"target": {
"workflow_id": "{id}",
"workflow_type": "parity.v1.echo",
"task_queue": "{id}-bra-queue"
}
}
}
}
}
},
"expected": {
"applicable": true,
"namespace": "{id}-bra",
Expand Down Expand Up @@ -628,5 +480,5 @@
]
}
},
"contract": "Bridge adapter webhooks (BridgeAdapterController) as published Server 2.11.1 serves them, in their own namespace: the adapter name, payload and action refusals (422, the target redacted to its routing fields); start_workflow accepted (202), returned as a duplicate (200) under the default policy, rejected under reject_duplicate (422), with a workflow ID derived from the adapter and the idempotency key when none is given, and on the default queue for an unconfigured type; signal_workflow and update_workflow refused for an unknown workflow or a malformed target, accepted (202), and deduplicated (200) by adapter, action, idempotency key and target name. Run IDs are labels in order of first appearance and derived workflow IDs are checked against PHP's derivation. Two published defects are bounded: a bridge update to an SDK-worker workflow skips external update admission and is rejected workflow_definition_unavailable, and a start for an id another namespace holds names that namespace's type (published 2.11.1 to 2.11.4) or the namespace difference (2.11.5); for each, the published answers or the intended ones (acceptance and deduplication; a foreign instance refused as unavailable) are accepted. Embedded applications have no HTTP bridge adapters."
"contract": "Bridge adapter webhooks (BridgeAdapterController) as published Server 2.11.7 serves them, in their own namespace: the adapter name, payload and action refusals (422, the target redacted to its routing fields); start_workflow accepted (202), returned as a duplicate (200) under the default policy, rejected under reject_duplicate (422), with a workflow ID derived from the adapter and the idempotency key when none is given, and on the default queue for an unconfigured type; signal_workflow and update_workflow refused for an unknown workflow or a malformed target, accepted (202), and deduplicated (200) by adapter, action, idempotency key and target name. Run IDs are labels in order of first appearance and derived workflow IDs are checked against PHP's derivation. A bridge update to an SDK-worker workflow passes external update admission like the regular update route, so it is accepted and deduplicated, and a start for an id another namespace holds is refused as unavailable, naming neither its type nor its namespace (#747 and #777; published 2.11.1 to 2.11.5 answered both otherwise). Every step compares exactly. Embedded applications have no HTTP bridge adapters."
}
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
"outcome": "rejected",
"reason": "unknown_target",
"idempotency_key": "k-6",
"message": "Workflow instance [pr1-bridge-adapters] is reserved for durable type [parity.v1.echo] and cannot be reused for [parity.v1.bridge].",
"message": "Workflow instance [pr1-bridge-adapters] is unavailable.",
"target": {
"workflow_id": "pr1-bridge-adapters",
"workflow_type": "parity.v1.bridge",
Expand All @@ -132,7 +132,7 @@
"outcome": "rejected",
"reason": "unknown_target",
"idempotency_key": "k-7",
"message": "Workflow instance [pr1-bridge-adapters] cannot be reused with a different namespace.",
"message": "Workflow instance [pr1-bridge-adapters] is unavailable.",
"target": {
"workflow_id": "pr1-bridge-adapters",
"workflow_type": "parity.v1.echo",
Expand Down Expand Up @@ -375,43 +375,40 @@
}
},
"update": {
"status": 422,
"status": 202,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "update_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "workflow_definition_unavailable",
"accepted": true,
"outcome": "accepted",
"idempotency_key": "u-2",
"target": {
"workflow_id": "pr1-bridge-adapters-bra-order",
"update_name": "bump"
},
"workflow_id": "pr1-bridge-adapters-bra-order",
"run_id": "run-2",
"control_plane_outcome": "rejected_workflow_definition_unavailable"
"run_id": "run-2"
}
},
"update_again": {
"status": 422,
"status": 200,
"response": {
"schema": "durable-workflow.v2.bridge-adapter-outcome.contract",
"version": 1,
"adapter": "acme.crm:v1",
"action": "update_workflow",
"accepted": false,
"outcome": "rejected",
"reason": "workflow_definition_unavailable",
"outcome": "duplicate",
"idempotency_key": "u-2",
"target": {
"workflow_id": "pr1-bridge-adapters-bra-order",
"update_name": "bump"
},
"workflow_id": "pr1-bridge-adapters-bra-order",
"run_id": "run-2",
"control_plane_outcome": "rejected_workflow_definition_unavailable"
"control_plane_outcome": "deduped_existing_command"
}
},
"update_undeclared": {
Expand Down Expand Up @@ -442,8 +439,7 @@
"SignalWaitOpened",
"SignalReceived",
"SignalReceived",
"UpdateRejected",
"UpdateRejected",
"UpdateAccepted",
"UpdateRejected"
]
}
Loading
Loading