Repository navigation
Conversation
A publish appends to a stream co-located with the workflow, so it commits with the workflow task and a task that fails takes the publish with it. Both commands write an offsets-only event, because a command that produces none puts the command-to-event matching every SDK replays against out of step.
This was referenced Sep 25, 2026
…workflow-commands # Conflicts: # chasm/lib/workflow/stream_commands.go
A subscribe no longer reads any NotFound as proof that the name is the workflow's own, since a routing failure would then bind it to different, empty data. Subscriptions per execution are bounded, publishes count against the shared byte budget, and both commands answer the namespace switch.
…workflow-commands
Each call had a deadline of its own and nothing bounded how many a task could carry, so the execution's lock hold grew with the subscription count.
…workflow-commands
The append command only ever names a stream this workflow owns, and the subscribe command takes a name or an id. The appended event now bounds its batch the way every other range does.
…workflow-commands
…workflow-commands
…workflow-commands
…workflow-commands # Conflicts: # chasm/lib/workflow/stream_admission_test.go # chasm/lib/workflow/stream_commands.go
Its streams live in the workflow's map and the workflow is still running, so the rule that ends a stream with its execution never reaches them. A retry is not terminal, so the next attempt keeps writing to the same streams.
Covers retry inheritance, the parked reader released at completion, and the workflow's own stream staying open.
2 of 3 tasks
…workflow-commands
The command is checked where it is handled, so a position that names nothing, or one sent alongside a start offset, fails the task with the subscribe cause. The flush resolves it against the stream and records the absolute offset on the subscribed event.
…workflow-commands
…workflow-commands
Nothing observable marks a parked reader, so the wait is the only way to have it parked before the activity completes.
…workflow-commands # Conflicts: # chasm/lib/stream/config.go
…workflow-commands
…workflow-commands
…workflow-commands
…workflow-commands
…workflow-commands
…workflow-commands
…workflow-commands
…workflow-commands
Owner
Author
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.
This PR adds the commands a workflow uses to publish to a stream and subscribe to one.
What changed?
chasm/lib/workflow/stream_commands.goholds the handlers forAppendStreamRecordsandSubscribeStream, their events and the library that registers them. A publish goes to a co-located stream the workflow owns, so the batch and the frontier land in the task's commit. A publish clears the producer id, which is how a reader tells the owner's records apart.respondworkflowtaskcompleted/stream_appends.goresolves the staged subscriptions before the commit. The pin lands before the cursor, so an interruption leaves a pin nobody reads.stream_absentfrom the stream's shard counts as that case. A routing miss stays an error.StreamCursorskeys owned streams by name and standalone ones by id in one map. A collision is refused, but History doesn't say which origin a range came from.stream.enabled.stream.maxSubscriptionsPerWorkflowbounds subscriptions, and all routed calls of one task sharestream.RoutedSetBudget, so the lock hold doesn't grow with the count. A publish counts against the owner's shared byte budget.start_positionrides the subscribe command. A bad position fails the task withBAD_SUBSCRIBE_STREAM_ATTRIBUTES. The flush records the resolved absolute offset on the event, so replay and reset see a fixed number.deleteTerminalActivitycloses its streams, so a reader tailing it is released while the workflow runs. A stream the activity never wrote to reads as open until the workflow closes. A command naming the reservedactivity/prefix fails the task.service/history/history_engine.goinstalls the routed stream client.Delivery to a subscriber comes in #7.
Part of AI-198 (epic AI-37).
Why?
The publish path is the one that makes the design worth having. It costs one event per batch, and it's atomic with the workflow task. That's easier to judge on its own than next to the delivery machinery.
How did you test it?
go build ./...andmake fmtare clean, and the lint findings are fixed. Unit tests ran for./chasm/lib/...and./service/history/.... With-tags test_dep, the functional tests ran the publish, refusal, failed-task, activity-end and recorded-offset cases. The replay and consume tests arrive with delivery in #7. Limit: a subscription to an owned stream ends at continue-as-new, and the successor gets a new stream at offset zero. One on another execution's stream carries over.