feat: WorkManager parity — task tags, deadlines, InputMerger, UPDATE policy (v3.5.0) - #90
Merged
vietnguyentuan2019 merged 3 commits intoSep 2, 2026
Conversation
…policy Implements four Jetpack WorkManager features that had no equivalent here, on both platforms rather than Android-only. - TaskRequest.tags + cancelByTag()/cancelByWorkerClass(): group cancellation across standalone tasks AND chain steps. enqueue() gained a tags parameter so standalone tasks are reachable. Tags validated at construction (non-blank, no comma, <=100 chars) because an unstorable tag would make cancellation silently no-op. - TaskRequest.deadlineMs: skip rather than execute a task whose window has closed. Enforced in BaseKmpWorker (Android), DynamicTaskDispatcher and ChainExecutor (iOS). Gives TaskTrigger.Windowed.latest real enforcement on iOS for the first time. - TaskRequest.mergeOutputFromPreviousStep: chain InputMerger with overwriting-merge semantics matching WorkManager's default OverwritingInputMerger. - ExistingPolicy.UPDATE: update a periodic task without resetting its interval timer. Fixes found while reviewing the in-progress implementation: 1. Android InputMerger was entirely non-functional. KEY_MERGE_PREVIOUS_OUTPUT was stamped but never read, and BaseKmpWorker returned Result.success() with no output Data, so a worker's Success.data never reached WorkManager and the default OverwritingInputMerger had nothing to merge. The feature worked on iOS and silently no-opped on Android. Now round-trips through a namespaced KEY_STEP_OUTPUT key, with oversized payloads dropped rather than failing a step that actually succeeded. 2. Merge logic was iOS-only; extracted to ChainInputMerger in commonMain so the two engines cannot drift apart, and pinned by commonTest. 3. iOS cancelByTag/cancelByWorkerClass deleted a chain's definition without marking it deleted, leaving its id in the append-only queue. ChainExecutor would later dequeue it, find no definition, and log an error for a cancellation the caller had asked for. Now writes the deleted marker first so the existing cancelled-chain path handles it at INFO level. 4. Deadline semantics diverged: Android skipped and continued, iOS returned Failure and aborted the whole chain. Unified on skip-and-continue — an optional stale step must not destroy the steps after it. 5. Log string had an operator-precedence bug that dropped a closing parenthesis. 6. tags/deadlineMs were reachable only from chains; enqueue() had no way to pass them, so cancelByTag could never match a standalone task. Kept an exhaustive when over ExistingPolicy in iOS enqueueChain so adding APPEND later forces a decision instead of silently routing it down the KEEP branch. cancelByTag/cancelByWorkerClass ship with no-op default implementations so existing third-party schedulers and test doubles keep compiling. 29 new tests (ChainInputMergerTest, TaskTagValidationTest, V350DeadlineAndInputMergerTest), verified to fail when the Android InputMerger fix is reverted.
- README: usage sections for InputMerger, tags/group cancellation and deadlines, a 'What's new in v3.5.0' entry noting the source-compatibility story, and the install snippet bumped off the stale 3.3.0 it still advertised. - ROADMAP: mark the four parity features shipped, and record what is still open from the same analysis (observeTaskState, WorkQuery, ExistingPolicy.APPEND) plus why setNextScheduleTimeOverride is not feasible on iOS. Also adds a regression test proving a worker that returns no data never re-emits the previous step's output — on Android the payload physically travels inside the next step's inputData, so echoing input to output would silently corrupt a pipeline.
… iOS An earlier analysis claimed ChainExecutor re-reads the chain definition from disk before each step, making 'append to the end' race-free. Verified false: executeChain loads the definition once (~line 748) and iterates the in-memory list (~line 925), both in the same function. Records the three consequences that any APPEND design has to answer — appends to a running chain being invisible to the in-flight executor, the definition being deleted on completion, and ChainProgress step indices not tolerating renumbering — so the next attempt starts from the real constraints instead of re-deriving them.
vietnguyentuan2019
deleted the
feat/wm-parity-tags-deadline-inputmerger-update
branch
September 2, 2026 06:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements four Jetpack WorkManager features that had no equivalent here, on both platforms rather than Android-only.
Features
TaskRequest.tags,enqueue(tags = …),cancelByTag(),cancelByWorkerClass()TaskRequest.deadlineMs,enqueue(deadlineMs = …)TaskRequest.mergeOutputFromPreviousStepExistingPolicy.UPDATEDeadlines also give
TaskTrigger.Windowed.latestreal enforcement on iOS for the first time — BGTaskScheduler offers onlyearliestBeginDate, solatestwas previously just a logged warning.Bugs found while reviewing the in-progress implementation
KEY_MERGE_PREVIOUS_OUTPUTwas stamped but never read, andBaseKmpWorkerreturnedResult.success()with no outputData— so a worker'sSuccess.datanever reached WorkManager and the defaultOverwritingInputMergerhad nothing to merge. The feature worked on iOS and silently no-opped on Android. Now round-trips through a namespacedKEY_STEP_OUTPUT, with oversized payloads dropped rather than failing a step that actually succeeded.ChainInputMergerin commonMain so the two engines cannot drift, pinned by commonTest.cancelByTagleft the queue inconsistent — deleted a chain's definition without writing the deleted marker, soChainExecutorwould later dequeue the id, find nothing, and log an ERROR for a cancellation the caller had requested. Now writes the tombstone first so the existing cancelled-chain path handles it at INFO.Failure, aborting the whole chain. Unified on skip-and-continue: an optional stale step must not destroy the steps after it.tags/deadlineMswere unreachable for standalone tasks —enqueue()had no parameters for them, socancelByTagcould never match a non-chain task.Deliberate design decisions
falseby default). Auto-forwarding would change behaviour for every existing chain and blend data between unrelated steps.cancelByTagsilently fail to match — the worst failure mode for a cancellation API.cancelByTagis not supported forTaskTrigger.Exacton Android (AlarmManager is not tag-indexed). Logs a warning rather than failing silently.ExistingPolicy.UPDATEdegrades to REPLACE for one-time tasks and chains, which have no timer anchor to preserve. ThewhenoverExistingPolicyin iOSenqueueChainis kept exhaustive so addingAPPENDlater forces a decision there instead of silently falling through to KEEP.Source compatibility
cancelByTag/cancelByWorkerClassship with no-op default implementations so existing third-party schedulers and test doubles keep compiling. Classes that overrideenqueue()must add the two new (defaulted) parameters — 11 mocks in this repo needed that change, which is the evidence the default-impl decision was worth making for downstream users.Testing
30 new tests (
ChainInputMergerTest,TaskTagValidationTest,V350DeadlineAndInputMergerTest), including a regression test proving a worker returning no data never re-emits the previous step's output — on Android the payload physically travels inside the next step'sinputData, so echoing input to output would silently corrupt a pipeline.Verified the suite actually catches the bug: reverting the Android InputMerger fix turns
successfulWorker_publishesOutputForTheNextStepred.