Repository navigation
Do not silently discard recorded activity input during dependency injection #910
Description
Problem
After an activity signature changes, Laravel dependency resolution can discard
its recorded business argument and execute the activity with a newly constructed
DTO's defaults. The run then records successful completion with the wrong data.
This was reproduced while qualifying website#363, rather than inferred from
its documentation report.
Published reproduction
Normally installed Workflow 2.11.2, source
a730773a3dcdf4c6d4ea0297f6c9010ccc0603f9; PHP 8.5.12, Laravel
13.38.0, SQLite. Real package migrations, workflow/activity jobs and durable
history are used. Queue transport is faked so those public jobs can be driven
explicitly. Execution crosses two fresh PHP processes; no service SDK or native
Server behavior is inferred.
-
The first process starts a workflow with
7. Its workflow task records an
activity with positional arguments[7], then stops before that activity.
The original activity is:final class QuoteActivity extends \Workflow\V2\Activity { public function handle(int $amount): array { return ['amount' => $amount]; } }
-
A fresh process loads the same original database and the same activity class
identity with this changed definition:final class QuoteRequest { public function __construct(public int $amount = 0) {} } final class QuoteActivity extends \Workflow\V2\Activity { public function handle(QuoteRequest $request): array { return ['amount' => $request->amount]; } }
-
Execute its original ready activity task, then the original parent task.
The recorded arguments still decode to[7], with SHA-256
73ab292f3f07ffff5c58d9a88b09cf78a3a2b7490f9011dd10a0d33ed84d4536.
The activity and workflow nevertheless complete withamount: 0.
Actual original run 01m5kr1j590a3vcc3j0hh7meat: one activity attempt,
three first-attempt tasks, zero repairs, and exactly
StartAccepted → WorkflowStarted → ActivityScheduled → ActivityStarted → ActivityCompleted → WorkflowCompleted.
There is no failure or input-mismatch diagnostic. An assertion requiring the
original amount 7 fails against actual 0.
Independent controls on the same published package pass: adding a trailing
optional activity argument uses its default; a new workflow reader can consume
an old result with a field fallback. Adding a required argument records
ArgumentCountError/ActivityFailed, and reading a missing old result field
records an application failure. Those outcomes are separate from this silent
DTO substitution.
Cause and acceptance
RunActivityTask
resolves method dependencies before invoking the activity. The container inserts
a default DTO ahead of the recorded scalar; the scalar becomes an extra PHP
argument and is silently unused. This is the activity counterpart of the entry
safety concern fixed for workflow methods in #866.
- Refuse binding that would silently replace or discard recorded activity input
before application activity code or its side effects run. - Keep original input, run/activity/task identities and committed history intact.
Define a typed, inspectable mismatch outcome and a supported recovery path
after compatible code is restored; do not invent a successful result. - Preserve legitimate dependency-injected services, scalar input, optional
trailing defaults, variadics and supported input adapters. - Add a failing-before regression using the real activity job, with cold
persisted input plus valid DI/default controls. Cover supported SQL backends. - Verify the affected published embedded consumer and document the resulting
compatibility/recovery contract in website#363 and all offered locales.
Check PHP service-mode binding independently before claiming it affected or
fixed. Native/shared parity stays coordinated with its owner.
Current delivery
Published 2.11.3 at 71213ae1 after exact candidate and independent main checks passed. Normally installed cold-process refusal and same-original authored-retry recovery pass, with the old 2.11.2 originals retained as failures. Full receipt. Published Laravel/PHP profile 39933343761 passes all seventeen jobs. Website #363 / PR #379 is delivered in all eight locales (receipt6362864678). Sample #275 is delivered at 40be451c after all candidate/main checks and anonymous both-registry amd64/arm64 image verification. The affected PHP Server package/image pin remains a separate qualified-release follow-through, with no native or Cloud adoption inferred.
Activity
rmcdaniel commentedon Oct 23, 2026
General accepted this as a P1 product defect. #911 now guards both queued and local activities before their entry methods run. Real failing-before controls separately establish silent local input loss and dependency-resolution errors escaping the activity outcome path.
The failure policy preserves the activity's authored attempts, backoff and deadlines. An input-binding mismatch is a typed failed attempt, not an invented successful result. With retries configured, restoring compatible code completes the same original run/activity/input on a later attempt. A one-attempt activity can still fail its workflow normally; this change does not refund attempts or reopen terminal runs. Existing valid injected services, defaults and variadics remain supported.
Eleven focused source tests pass. Full exact-head checks and SQL qualification are in progress. Published 2.11.2 remains affected; publication, installed acceptance and the all-locale website#363 handoff are still required.
Source qualification checkpoint: #911 merged normally at 71213ae / whole b48b19574b13c369274d986158bf4511041566ca after all seventeen candidate checks passed, with six explicit PR skips. The remote head is independently absent. Independent full main qualification is run 39925010321 and remains queued/running. Patch 2.11.3 has not been tagged or published; fixed-package cold-process acceptance and downstream follow-through are still required.
rmcdaniel commentedon Oct 23, 2026
Workflow 2.11.3 is published; cold-process acceptance passes.
The immutable annotated tag
55945e03cc98e5441a6f129d2b389bdd8c00664epoints to source71213ae1e0338cda5967e0840782a981530ed854, whole treeb48b19574b13c369274d986158bf4511041566ca. All five independent main workflows pass, including the complete SQL/framework/coverage build. The release and Packagist source/dist references match.A normal Composer consumer changed only Workflow 2.11.2 to 2.11.3 in its 119-package lock. PHP 8.5.12, Laravel 13.38.0, SQLite and real public workflow/activity jobs are used, with queue transport faked for explicit job execution. There is no source overlay or service-worker/native claim.
Five fresh PHP processes, five tests / 52 assertions pass:
01m5m2669r7ffmm7sgz9knkq92, activity01m5m266jb3p27r757n4kxd74y. The cold incompatible DTO is refused before its body runs. TypedActivityEntryArgumentsMismatchExceptionproducesActivityFailed, then normalWorkflowFailed, with null output, one activity attempt, three tasks and zero repairs.01m5m26qbp25snv1jaqjzfbkcf, activity01m5m26qmy0vfxtzr0w2a9xnd0. The incompatible process recordsActivityRetryScheduledwithout entering the body. A third fresh process restores the compatible scalar method and completes the same original run/activity with output7, two activity attempts, oneActivityCompleted, noActivityFailedand zero repairs. Four tasks remain; the retry task correctly records attempt two.Both preserve recorded input
[7]and SHA-25673ab292f3f07ffff5c58d9a88b09cf78a3a2b7490f9011dd10a0d33ed84d4536. The same fixture/assertions against installed 2.11.2 retain two incompatible-body failures after successful seed phases: original runs01m5m1ydkwx91qpat5dw1m8xe3and01m5m1zcjkhnzdb97rgvaavgmh. Fixture SHA-256 is70477c16aa0355a8d90a6b25e568ae728df36f81e0464333f04c66232ab3c332. Public source regressions in #911 also cover valid DI/default/variadic controls and local activities.The published verification profile now passes all seventeen jobs: package/platform identity and all sixteen supported Laravel/PHP upgrade combinations. Website #363's all-locale fixed-version/recovery guidance and affected consumer pins/rebuilds remain in progress. This library tag does not update existing Server images, reopen terminal runs or grant additional attempts.