Skip to content

fix: keep narrated progress notes apart and name a delegated conductor - #40

Merged
cgaravitoq merged 4 commits into
stagingfrom
cgaravitoq/bd2-a3-narration
Oct 7, 2026
Merged

cgaravitoq merged 4 commits into
stagingfrom
cgaravitoq/bd2-a3-narration

Conversation

@cgaravitoq

@cgaravitoq cgaravitoq commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

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. between Task: and Status:. A milestone without a token (the progress bearer, even for task: "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/narrated as before, and both milestones of a joined turn still answer 200 {"ok": true, "narrated": true}.

Verify

  • Two milestones narrated in one turn yield text where the notes are separated - apps/slack-agent/test/delivery.test.ts "keeps a blank line between the notes a joined response carries". Red on base 0d453e1^: 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 at c40a74a, and the whole delivery.test.ts (106 tests) unchanged otherwise.
  • A delegated milestone's narration body carries the conductor line - 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 no Delegation: paragraph; green at 24c076c with the paragraph asserted verbatim.
  • A non-delegated one does not - the same test posts the same milestone under the progress bearer and expects no 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 verify passes and CI verify is green - bun run verify green locally on 24c076c (format:check, lint, typecheck, 363 tests, knip, deploy:dry, bun audit clean). CI verify green on this PR: run 37675505815, 49s, pass.

Commits, in dependency order

  1. 0d453e1 test: pin the glued notes a joined narration response carries (red on its own)
  2. c40a74a fix: keep a blank line between the notes a joined reply carries
  3. 24c076c feat: make a delegated milestone's narration name its conductor

Not covered

  • The live re-run in staging (the conductor runs it after the pin), so E10's fix is proven by the delivery-level witness, not by a real Slack message from the joined turn.
  • Wording in right-hand's narration config that still names the owner as the actor. The core now tells the model who works on a delegated task, but a config voice that says "Carlos" or "you, the owner" can still override it; the pin row can adjust that wording.

cgaravitoq and others added 4 commits October 7, 2026 21:29
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.
@cgaravitoq
cgaravitoq merged commit f28de36 into staging Oct 7, 2026
2 checks passed
@cgaravitoq
cgaravitoq deleted the cgaravitoq/bd2-a3-narration branch October 7, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant