Repository navigation
Add resumable batch cancel and signal with a private manifest (cli#60) - #137
Merged
Merged
Conversation
New Rust-only commands, purely additive (no 2.x command changes): - batch:cancel / batch:signal select workflows with a visibility query (reusing workflow:cancel --all-matching's selection: pinned runs, repeated-token and empty-page guards), write the whole selection to a new private manifest (create_new, 0600, never the token) before any mutation, then send one request per target, recording a dispatching event before and an outcome after each, each fsync'd. - States: acknowledged, skipped (404/409/410, or no run ID), refused (permanent 4xx, request-ID conflict), pressure (429, 503+Retry-After; retried in place with the same key, Retry-After honoured up to 60 s, at most 3 attempts per target), unknown (lost response, 5xx, or dispatching without an outcome), pending (not sent, or 401/403). A run stops at the first unknown outcome, auth refusal or exhausted pressure. - batch:resume refuses another Server or namespace, never relists, sends only pending/pressure targets with their original keys and recorded signal input, and never resends unknown targets. An flock prevents two processes running one manifest; a half-written final line is dropped. - batch:show reads the manifest offline. - Manifest schema durable-workflow.cli.batch-manifest/v1 (JSONL header plus events). Runbook in docs/batch-operations.md. Catalog: parity/catalog-additions.json adds the commands (rust_only) via update-catalog.sh. The harness announces them, skips their --help, and strips only them from candidate listings, so listings still compare. Shared pieces moved out of workflow_control/workflow_message (selection, filters, confirmation, message paths) instead of being copied; the client gains workflow_post_observed to read Retry-After. Part of #60 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
rmcdaniel
marked this pull request as ready for review
November 12, 2026 02:17
This was referenced Nov 12, 2026
rsguimaraes-pe
pushed a commit
to rsguimaraes-pe/cli
that referenced
this pull request
Nov 13, 2026
Finishes cli#60 on top of durable-workflow#137. - batch:update <update-name>: one request per target with the target's command key as request_id, --wait checked against the Server's request contract before selection, and the input recorded once and resent unchanged on resume. An accepted or completed update is acknowledged; one the workflow rejected or failed is refused; anything that may have been admitted without a definite answer stays unknown and is never resent. - --deadline on batch:cancel/signal/update/resume (seconds or 90s, 5m, 1h30m; at most 7 days). No request starts after it, or with under 0.5 s left; unsent targets stay pending; a Retry-After past it leaves the target in pressure; an in-flight request's timeout is capped to the time left, and a capped timeout after send is unknown. - Every early stop (deadline, unknown, pressure, authentication) is recorded as a target-less {"event":"stopped"} manifest line, shown by batch:show and listed under "stops" in JSON. - parity/live_batch.py runs the batch commands against the published PHP 2.11.8, native 3.5.0 and native 3.0.5 Servers in the rust-live-parity job: cancel 5/5 with show and resume agreeing, signal 3/3, and a deadline stop partway followed by a resume that completes 6/6. Tests: 6 new batch integration tests and an update classification unit test; 299 Rust tests pass. Parity 919/947, 28 intentional, 0 unexpected; live 84/84. Closes durable-workflow#60 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.



Part of #60. Implements the plan as new Rust-only commands. It is purely additive: no 2.x command changes behaviour. Targets 2.6.0; no version or release files are touched.
Commands
dw batch:cancel --query=… --manifest=FILE [--type] [--status=running] [--limit=100 (≤1000)] [--reason] [--rate=10] [--yes] [--json]dw batch:signal <signal-name> --query=… --manifest=FILE [-i/--input-file/--input-encoding] [filters] [--rate] [--yes] [--json]dw batch:show FILE [--json]: reads the manifest only and makes no network requests.dw batch:resume FILE [--rate] [--yes] [--json]Selection reuses
workflow:cancel --all-matching: each target is pinned to the listed run (#59), with the repeated-token and empty-page guards. Confirmation works the same way (stdin line,-n,--yes;--yesis required with JSON).Manifest (
durable-workflow.cli.batch-manifest/v1)A private JSONL file: created with
create_new, mode 0600, and it never stores the token.manifest_id(128-bit hex),created_at,cli_version,server,namespace,operation,parameters(the reason, or the signal name and the input exactly as sent),selection(query, filters, limit, snapshot note), andtargets[](index,workflow_id, pinnedrun_id,command_key = batch-<manifest_id>-<index>).dispatchingbefore each request, thenoutcomewithstate,http_status,reason,messageandretry_after. Every line is fsync'd.flockkeeps a second process off the same manifest.States and rules
Retry-AfterRetry-Afterhonoured up to 60 sdispatchingwithout an outcomeA run stops at the first unknown outcome, auth refusal or exhausted pressure, so the remaining targets stay pending. Resume refuses a different Server or namespace, never relists, and reuses the recorded keys and signal input. Exit 0 only when every target is acknowledged or skipped.
Catalog and parity
parity/catalog-additions.jsonadds the four commands, markedrust_only.update-catalog.shmerges them into their namespace, and the regeneratedcatalog.jsonshows only insertions, so the 2.x part is byte-identical.--help(there's no 2.x help to compare), and removes only those lines or entries from candidate listings and completions before comparing. The rest oflist,list raw,list short json, the ANSI and no-ANSI listings, and the baredwlisting are still compared in full. New harness unit tests cover the strip and the skip.test-release-rust-workflow.py: OK.Tests (Rust 1.86; fmt and clippy clean; 292 pass)
New:
rust/tests/batch.rs, 11 tests:interruptedand never resends itbatch:showmakes zero requestsPlus unit tests for the classification and the manifest's UTC and state handling.
Refactors (no behaviour change, parity unchanged):
workflow_control's selection, filters and confirmation are shared;message_pathis shared; the client gainsworkflow_post_observed, which returnsRetry-After.Runbook:
docs/batch-operations.md(start, inspect, resume, the states, how to resolveunknown, and the manifest format), linked fromrust/README.md.Gaps against #60's acceptance (why this is "Part of")
batch:update. The plan covered cancel and signal; update needs the update-diagnostics contract and is follow-up work.--deadline.unknownstays unresolved by design until the Server advertises retained exact-key admission.🤖 Generated with Claude Code