Repository navigation
fix: keep narrated progress notes apart and name a delegated conductor - #40
Merged
Merged
Conversation
Two progress milestones dispatched into one busy conversation join into a single reply, so the second note's first delta lands against the first note's last character. The test reproduces that glue and demands a blank line, so the fix that follows has a witness that fails without it.
A dispatch that arrives while the conversation is answering joins the live response, so two narrated progress milestones stream into one Slack message. The delivery appended the second turn's deltas to the first one's with nothing between them, which read live as "...feedback.Now on the fix." The record now marks a join that lands on an already answered thread, and the joined turn's first text carries the blank line, so a turn that answers with only a tool call adds no stray separator.
The narration voice comes from the operator's config and calls the owner the actor, so a milestone posted under a task token reached the model as the owner's own work. The body now carries the token's runner as the conductor doing the task on the owner's behalf and tells the model to name that conductor, never the owner; a milestone without a task token keeps the body it had.
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.
Row A3 of wave bloop-delegation-2, one logical step per commit.
E10 - two notes, one reply. Two progress milestones posted close together are dispatched into the same conversation Durable Object; Flue joins the second dispatch into the live response at a turn boundary and drives a continuation turn, so both narrations stream into one Slack delivery record and stream with nothing between the deltas ("...feedback.Carlos sigue..."). One reply per milestone is not reachable: the runtime has no opt-out from a join, and the only seam the app owns in that path is
packages/core/src/delivery.ts. So the result is one reply whose notes are separated by a blank line: a delivery that joins a destination the record already answers, while that record carries reply text, prepends"\n\n"to the joined turn's first text delta. At the first text and not at the join, because a joining turn that answers with a tool call and no note would otherwise leave a stray separator, and a record resumed after a restart would keep a trailing one.Q2 - a delegated milestone names its conductor. A milestone posted under a task token is the conductor's work, so its narration body now carries
Delegation: the conductor <runner> is doing this task on the owner's behalf; name the conductor, never the owner, as the one working.betweenTask:andStatus:. A milestone without a token (the progress bearer, even fortask: "delegation:<id>") keeps the body it had, byte for byte. The name is the registered runner name from the token's task; the public repo says runner, conductor and task, never a machine or a vendor.Receipts are untouched: one receipt per milestone,
posted/narratedas before, and both milestones of a joined turn still answer200 {"ok": true, "narrated": true}.Verify
apps/slack-agent/test/delivery.test.ts"keeps a blank line between the notes a joined response carries". Red on base0d453e1^:expect(slack.markdown()).toBe("Reviewed the feedback.\n\nNow on the fix.")received"Reviewed the feedback.Now on the fix.", the E10 glue with the space missing. Green atc40a74a, and the wholedelivery.test.ts(106 tests) unchanged otherwise.apps/slack-agent/test/delegation.test.ts"narrates a delegated milestone as the conductor's work, not the owner's". Red on base: the narrated body had noDelegation:paragraph; green at24c076cwith the paragraph asserted verbatim.the conductor;progress.test.ts"narrates a milestone as one turn in its thread and posts no reply itself" still pins the exact body array for a non-delegated milestone, so the line cannot leak into it.bun run verifypasses and CI verify is green -bun run verifygreen locally on24c076c(format:check, lint, typecheck, 363 tests, knip, deploy:dry,bun auditclean). CIverifygreen on this PR: run 37675505815, 49s, pass.Commits, in dependency order
0d453e1test: pin the glued notes a joined narration response carries (red on its own)c40a74afix: keep a blank line between the notes a joined reply carries24c076cfeat: make a delegated milestone's narration name its conductorNot covered