Skip to content

Native: land the October 21-22 port PRs together - #629

Merged
rmcdaniel merged 55 commits into
mainfrom
rust-325-train-20261022
Oct 22, 2026
Merged

rmcdaniel merged 55 commits into
mainfrom
rust-325-train-20261022

Conversation

@rmcdaniel

Copy link
Copy Markdown
Member

Part of #325.

This lands twelve open native PRs in one CI cycle. Each one's required checks passed on its own head, but strict branch protection put them back behind main after every merge, so landing them one at a time meant twelve serial CI cycles.

The branch merges each PR's exact current head, so GitHub marks each of them merged when this lands:

Conflicts resolved

Verification on the combined source

  • The full suite on SQLite, PostgreSQL and MySQL passed, apart from one MySQL failure:
    • 346 integration tests and 200 library tests on each database, plus the upgrade tests;
    • the one failure, worker_reported_range_failure_requires_explicit_original_task_repair_across_restarts, read repair_needed right after a repair on the slow MySQL run (868 s).
  • Cause of that failure. Published Workflow dispatches a replay-blocked run's repaired task, as it does an overdue run's. Native marked only the overdue path's task dispatched, so once three seconds had passed since the task was created, the repaired run read as overdue.
  • Fixed here (6c4ef39), with a regression check that the repaired task's dispatch time is at or after its repair. The check fails without the fix and passes with it.
  • After the fix and the final merges of Authenticate database runtime credentials and the none driver like PHP #627's fault test and main:
    • the full SQLite suite passes (347 integration, 200 library, upgrade tests);
    • the task retry, repair, role token and runtime credential tests pass on PostgreSQL and MySQL (24 each).

No checkpoint file: each included PR carries its own.

rmcdaniel and others added 30 commits October 21, 2026 17:43
Published PHP 2.5.14 refuses a malformed input envelope on start, signal,
update and query as Laravel validation of input.codec or input.blob, with
the Avro serializer's diagnosis. Native used its own reasons. In the
harness, only 4 of 60 malformed-input responses matched.

Native now applies PHP's transport, codec and envelope checks on all four
routes. Signal and update refuse an undecodable or scalar blob with PHP's
messages, and take a non-empty map's values as the arguments. Avro decoding
uses PHP's strict base64 rules, so whitespace is skipped and padding may be
omitted. Update refusals carry the control-plane envelope, and command
validation failures include workflow_id.

Start also refuses an undecodable blob with the signal message, where PHP
2.5.14 answers 500 (server#601).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published dw 2.3.0's system:repair-status reached native's 501.

Native now serves admin-only GET /api/system/repair. Its candidates are
TaskRepairCandidates::snapshot across every namespace: overdue, failed
or expired tasks, and open runs without a next task, grouped by
connection, queue and compatibility with PHP's scope-fair round-robin
selection. Its policy is native's own claim-time lease recovery, as
operator metrics already report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published dw 2.3.0's workflow:repair on a run whose ready task was overdue
got native's 422 development_workflow_task_repair_unqualified, where PHP
answered repair_dispatched. Native repaired only a worker-reported
version-range blockage.

Repair now follows WorkflowStub::attemptRepair. A closed run's repair is a
durable rejected command with 409. Otherwise native projects the run's
summary in the same transaction. When it says repair_needed, the
recoverable task is redispatched: a ready task due for redispatch counts
one more repair, and an expired workflow-task lease returns to ready. Any
other active run records repair_not_needed. Both outcomes record PHP's
command payload, RepairRequested event and response. The version-range
repair keeps its existing qualified path.

Creating a missing task and recovering an expired activity lease are still
refused, and are recorded as known gaps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Sample App's unchanged server environment sets
WORKFLOW_SERVER_AUTH_TOKEN. Native required DW_AUTH_TOKEN, and refused to
start when one of the legacy role-token names WORKFLOW_SERVER_WORKER_TOKEN,
WORKFLOW_SERVER_OPERATOR_TOKEN or WORKFLOW_SERVER_ADMIN_TOKEN was set.
Published PHP reads each through EnvAuditor::env: the DW_* name when it is
set, otherwise the legacy name. Native now does the same.

The checkpoint compares all 49 settings PHP's config reads with native's,
and records the ones native ignores but that change behavior as a known
gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DW_DEFAULT_NAMESPACE and DW_WORKER_PROTOCOL_VERSION (and their legacy
names) change published PHP's behavior. Native serves `default` and
implements protocol 1.20, so it now refuses to start when either holds
another value instead of ignoring it. The protocol version is one
constant shared by every response that advertises it.

The other settings recorded as ignored are read by PHP into its
configuration but never applied, so ignoring them matches PHP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published PHP returns the activity execution's deadlines (schedule to
start, start to close, schedule to close and heartbeat) on activity
claims and on the attempt status read. Native returned null on status
and omitted the claim field, so the Sample App's activity recovery
scenario could not see the original total deadline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published PHP checks the reporting attempt before the task's lease or
recorded outcome: a result from an attempt that is no longer current is
answered with 409 stale_attempt, even after another attempt finished the
task. Native compared the task's completion receipt first and answered
conflicting_completion, or task_not_leased for a failure. The Sample App's
activity recovery scenario requires PHP's answer.

A repeated result from the attempt that finished the task keeps its
idempotent receipt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A retried activity closes its failed attempt's task with that failure as
the receipt. A later completion from the same attempt differed from the
receipt and was refused as conflicting_completion. Published PHP answers
it with 409 stale_attempt, because the task is no longer leased. Only an
identical repeat keeps native's idempotent receipt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A timeout closes the attempt and cancels its task. A later completion or
failure for that task got a bare 409 stale_attempt without the task,
attempt and status fields, so the PHP SDK could not recognize the
refusal, tried to fail the task, and stopped its worker. Published PHP
answers with its full stale attempt receipt, which the SDKs discard.
Heartbeats keep their existing answer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published PHP admits a query whose run has an open workflow task and lets
it wait for its claim. A workflow task leased at admission fixes the
query's history cutoff at the run's sequence then. The claim waits while a
workflow task holds a live lease, or while a due resume task (signal,
update, activity, child or timer) is ready that the query cannot see yet:
not one created after a cutoff query was admitted, and not a signal whose
SignalReceived the query already observed. The claim then delivers the
history up to the cutoff, or all of it without one. A poll that finds only
held queries ends with workflow_task_pending at once, or with
workflow_task_leased when its wait times out.

Native refused such queries with 409 query_snapshot_busy, which stopped
the Sample App's update scenario. A held poll re-checks without the
transition lock until the workflow task ends.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rkers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published PHP answers a reported workflow task failure by marking the
task failed and, unless the failure blocks replay, creating a ready retry
task with the same payload, including any update or signal it carries.
Native refused the report with development_workflow_task_retry_messages_
unqualified when the run had pending messages. The Rust SDK stops its
worker on that refusal, so the run's accepted update was never applied.

Native now retries such a task as PHP does. The waiting-for-history
acknowledgement with pending messages keeps its refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published PHP's signalValueFromRecord applies a signal with no arguments
as true, one argument as itself, and several as their list. Native
recorded null for a signal without arguments in SignalApplied. The Rust
SDK checks that value against the received arguments, so it refused a
query over such a history with "conflicting committed signal identity".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Published PHP starts while its database is unavailable
(server:assert-php-database --allow-unavailable). Native exited on the
first refused connection, so an unchanged Compose file failed when MySQL's
health check passed during its temporary init server.

Native now retries a database it cannot reach yet with a capped backoff:
refused or dropped connections, unresolved hosts, pool timeouts and
PostgreSQL's starting-up state. A refused login or a configuration error
still stops the process. SIGTERM or SIGINT while waiting exits cleanly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Sample App's search attribute scenario stopped at its Rust worker's
first upsert, which native refused as an unsupported command.

Native now applies the command as published PHP does:
- the Server's SearchAttributeValueValidator checks each value against
  the namespace's registered, declared or inferred type, with PHP's
  messages under commands.N.attributes;
- the Workflow package's UpsertSearchAttributesCall normalizes values,
  and its key and value errors are reported as PHP reports them;
- typed rows are written and deleted, within 100 attributes and 64 KB;
- SearchAttributesUpserted records attributes, attribute_types and the
  merged view;
- attribute_types needs worker protocol 1.16 and the
  typed_search_attributes capability, which also gates claims of runs
  with typed history and appears in worker discovery;
- continue-as-new inherits the rows instead of being refused.

A differential probe against PHP 2.5.14 matched every refusal and the
history payload. PHP stores an offset datetime as the wrong instant
(workflow#898); native stores the instant written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rmcdaniel and others added 24 commits October 21, 2026 23:03
Published PHP's workflow task claim for a signal task carries
signal_arguments, workflow_signal_id and workflow_command_id. The Rust SDK
resumes a signal wait with signal_arguments. Native sent only the signal
name, so a Rust workflow resumed after a worker restart received no
arguments; the Sample App's search attribute scenario returned signal []
instead of ["released"].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Sample App's search attribute scenario lists workflows with typed
predicates and checks each run's typed attributes. Native refused any
query with a comparison and listed no attributes.

Native now parses the list query like published PHP's
WorkflowVisibilityQuery: OR and AND groups, NOT, comparisons, IN lists and
bare boolean fields over Status, the run's system columns and registered
search attributes, with PHP's validation messages. A plain term keeps the
free-text search. Every listed run carries its typed search_attributes.

Native evaluates the query in memory over candidate runs, 500 at a time,
so its static SQL stays unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Sample App's timers scenario cancels a workflow waiting on three
concurrent timers. Every SDK delivers that cancellation with
call_kind parallel and sequence_span 3, which native refused with
cancellation_delivery_boundary_not_available.

Native now accepts a parallel boundary whose members are all timers. As in
PHP's CooperativeCancellationDelivery, each member must belong to the
group at its index, a group resolved entirely before the request is not
eligible, pending member timers are cancelled, and the delivery records
its sequence_span. Groups with other member kinds keep the refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native refused DW_RUNTIME_CREDENTIALS_ENABLED and DW_AUTH_DRIVER=none at
startup, which the Sample App's children and namespaces scenarios use.

- Serve PHP's admin-only /api/runtime-credentials list, show, upsert, rotate
  and revoke, with the controller's validation and conflict refusals. Only
  the token's SHA-256 and 12-byte prefix are stored.
- With the setting on, authenticate an unknown bearer token by its hash as a
  principal with its subject, roles, tenant and claims, and confine a tenant
  principal to its namespace, as ConfiguredAuthProvider::tenantAllows does,
  including a namespace route's target from the path or the name query.
- Admit every request as the anonymous administrator with the none driver.
- Record the principal's identity in task repair contexts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SonarCloud's Rust analyzer read the inline comparisons as unit comparisons
(S7423) and failed the reliability gate. Clippy reports no unit comparison;
reading the stored values into typed locals first is clearer either way.
… rust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
…to rust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
#	rust/tests/execution.rs
…s' into rust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
…into rust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
#	rust/tests/execution.rs
…ust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
#	rust/tests/execution.rs
…ust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
#	rust/src/main.rs
… into rust-325-train-20261022

# Conflicts:
#	docs/rust-server-port.md
…le and drop gaps this train closes

The union merge of the database wait and legacy setting branches nested the
setting tests inside the startup tests and re-added three known-gap bullets
that their own branches removed or that later branches close: malformed
input envelopes (#606), queries while a workflow task is open (#619) and
search attribute upserts (closed by #622).
…to rust-325-train-20261022

# Conflicts:
#	rust/src/main.rs
#	rust/tests/execution.rs
…e role profile

The role profile asserted that native refuses DW_AUTH_DRIVER=none and
DW_RUNTIME_CREDENTIALS_ENABLED=true at startup. Both are now qualified, so
the refusal cases use the signature driver instead and drop runtime
credentials.
Published Workflow's attemptRepair sends both an overdue run and a
replay-blocked run through TaskRepair::repairRun and then dispatches the
task. Native marked only the overdue path's task dispatched. A replay-blocked
repair left the task's last dispatch at its creation, so once three seconds
had passed the run read as repair_needed right after its repair; the slow
MySQL suite on the combined train showed that.
Security's review of #627 asked for a fault-injected test of the database
lookup. With the credentials table gone, an unknown bearer token gets the
generic 500 server_error without table or token text, configured tokens
still authenticate, and a request without a token is still refused.
@rmcdaniel
rmcdaniel enabled auto-merge October 22, 2026 01:56
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel
rmcdaniel merged commit 25aa94f into main Oct 22, 2026
21 of 27 checks passed
@rmcdaniel
rmcdaniel deleted the rust-325-train-20261022 branch October 22, 2026 02:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant