* feat: add unit tests for S3BufferedReader.read and improve read method implementation
* feat: refactor S3BufferedReader.read method and add unit tests for its functionality
* feat: implement peek() on S3BufferedReader with buffered reads
* fix(python-client): keep the read(size) contract and trim the test surface
Drop the duplicated `TestS3BufferedReaderRead` class from
`python-client/tests/wmill_client_test.py`: CI runs `pytest tests/` from
`python-client/wmill`, so that legacy manual harness never executes, and the
same assertions already live in `python-client/wmill/tests/test_s3_reader.py`.
Narrow that file to the four behaviours a future change could break, and make
the `bytes_generator` guard actually call `bytes_generator`.
Align `peek()` with `io.BufferedReader.peek`, which does at most one read on
the underlying stream, rather than looping until `size` bytes are buffered.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(python-client): hold read1 to one underlying read
read1 forwarded to read, so read1(-1) drained the whole object — the same
unbounded buffering this branch removes from read. Now that a buffer exists,
read1 can honour its own contract: fill only when the buffer is empty, then
serve from it.
Also treat read(None) as read(-1), per the BufferedReader contract, and pin
that read(0) does not pull from the stream: that holds only because the
drain sentinel is a negative size, and widening it to any falsy size would
reintroduce whole-file buffering.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(python-client): return from read1(0) without touching the stream
A zero-length read has nothing to serve, so pulling a chunk to satisfy it
both wastes a round trip and advances the stream. Guard it ahead of the
fill, and pin it with a chunk source that counts pulls.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Tushar <tusharanshu18@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(wac): pin the failure record with one corpus both SDKs read
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(wac): add the behaviour matrix that verified the failure record
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(wac): record how to exercise an unreleased SDK change
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): guard the whole extra pair, not just its value
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): never rehash an untrusted extra key
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): walk only a real __dict__ when collecting extra
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(wac): name the divergence the corpus cannot pin
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(wac): state the extra-encoding constraints in four lines
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci: run the python and typescript SDK suites
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* ci: run the SDK suites on release tags only
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(ci): state the constraint without the incident
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(sdk): claim only what functools.wraps actually restores
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(ci): note the interpreter the suite runs on is not the worker's
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): hand a caught task and step failure the same shape in every round
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* refactor(wac): decide the failure record once, server-side
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): leave a legacy SDK's failure marker untouched, and ship wacError to jsr
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): carry a step's custom error fields, and bound the stack in bytes
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): keep a step's extra fields serializable and bounded
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): record a non-Error throw the way a task records it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): guard the last unguarded throw site in the step marker
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): make failure reporting non-throwing on both clients
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): take the step traceback the way the executor takes it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): contain the reads that happen before a failure is checkpointed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): fall back to the checkpointed marker, not the live one
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): keep non-finite fields and hostile proxies out of the checkpoint path
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): keep the snapshot that passed the serialization probe
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(wac): keep the failure-record module's surface to what is used
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): return the checkpointed value from step(), not the live object
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(wac): regenerate system prompts and narrow the round-trip claim
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* style(wac): condense the round-trip comments and fix the fallback note
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(sdk): type step() as the JSON round trip of its body's result
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(sdk): apply the JSON round trip to task() and the standalone paths
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sdk): encode bigint, keep unknown as unknown, align dropped-key results
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): null out results whose key JSON.stringify would drop
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): normalize only the top-level result, keeping nested keys as they were
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): normalize a child task's result so a deployed job cannot fail to parse
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* docs(sdk): pin non-finite number behavior in Jsonified and its tests
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sdk): admit undefined for keys whose value JSON.stringify may omit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sdk): make a key JSON.stringify may omit optional, not just nullable
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(sdk): treat a class-valued property as dropped, like any other function
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(wac): report a task failure the child round's body catches
* chore(wac): state the child-round failure invariant once
* test(wac): pin the catch-then-continue re-raise in the child round
* fix(wac): checkpoint step errors so a caught exception does not hang replay
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(wac): honour a step suspend the workflow body caught and swallowed
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(wac): park every suspend, not only those from a failing step
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore(wac): keep the generated bun wrapper comment-free
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(wac): park the child task-completion suspend and align error identity
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(wac): pin the TaskError identity of replayed step and task failures
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat: bind WAC approval urls to a named wait_for_approval step
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject duplicate WAC approval step keys instead of renaming them
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject WAC approval links minted for a step that is not awaiting approval
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: bind WAC approval links to the awaiting step and stop step key aliasing
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject empty approval keys and scope minted-key writes to the workspace
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: enforce WAC approval binding at consumption and reject colliding keys
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: make WAC approval binding and collision checks atomic, harden TS step keys
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: decrement WAC suspend atomically instead of from a pre-lock snapshot
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* chore: add sqlx cache entry for the atomic WAC suspend decrement
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: omit empty approver param from python get_approval_urls
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: pin the suspend-snapshot decrement and the colliding-mint race
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test: drop the suspend-snapshot interleave test, it cannot both be stable and discriminate
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix: reject step keys that cannot be minted as a URL path segment
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>