Skip to content

feat: label and narrate the delegation state notices - #44

Merged
cgaravitoq merged 5 commits into
stagingfrom
cgaravitoq/bd3-a5-state-labels
Oct 8, 2026
Merged

cgaravitoq merged 5 commits into
stagingfrom
cgaravitoq/bd3-a5-state-labels

Conversation

@cgaravitoq

Copy link
Copy Markdown
Owner

A deployment that sets progress.narration reads Spanish in every thread, but the delegation state notices stayed English: packages/core/src/delegation.ts built them from a hardcoded map and posted them itself, so no config could change them and no narration turn ever saw them.

delegation.labels now names the word each notice opens with (running, failed, unknown), missing or blank values keep today's English strings, and a configured narration hands the labelled notice to the same narration path a milestone takes, keyed delegation:<task id>:<state>, with the labelled line as that turn's fallback. Without narration the notice is posted directly as before.

Verify:

  • A config with Spanish labels posts "Iniciado." on running in the no-narration deployment: a Spanish deployment posts its labelled notice directly, once per state in apps/slack-agent/test/delegation.test.ts posts ["Iniciado.", "Desconocido. Budget ended ", "Fallido. Stop confirmed"] for running, unknown and failed.
  • With narration configured the running notice reaches the narration path with the label and the note, and nothing is posted directly: a narrated deployment hands the labelled notice to the narration turn and posts nothing itself records one notice (conductor: "runner-1", label: "Iniciado", note: "Budget ended ", state: "running"), builds its turn with delegationNoticeTurn, asserts idempotencyKey === "delegation:task-1:running", binding.fallbackText === "Iniciado. Budget ended ", the shared instruction and Status: Iniciado in the body, and that h.posts grew by zero.
  • A repeated running still posts nothing: the same test reports running twice and expects one line; a repeated runner state answers 200 once and another runner is still refused still expects ["Started.", "Failed."] for two running and two failed reports.
  • Labels absent means today's strings: delegation without labels keeps today's English notice posts ["Started.", "Outcome unknown. Budget ended", "Failed. Stop confirmed"], and delegation labels resolve from config and fall back when blank or absent asserts a blank failed resolves to Failed through defineAgentConfig.
  • On base f28de36 these tests cannot pass: a detached worktree at f28de36 with only the new test file copied in fails to load it with SyntaxError: Export named 'delegationNoticeTurn' not found in module 'packages/core/src/index.ts', and bun -e against base's defineAgentConfig with delegation.labels = { running: "Iniciado" } prints labels on base: undefined.
  • bun run verify is green: 376 pass, 0 fail, plus knip, deploy:dry and bun audit clean.

Commits, in dependency order:

  1. test: demand Spanish delegation notices and their narrated turn
  2. feat: label and narrate the delegation state notices
  3. docs: document the delegation notice labels and their narration

Notes:

  • SlackProgressTurn.milestoneId became idempotencyKey, so the host dispatches under the key the turn carries: progress:<milestone id> for milestones, delegation:<task id>:<state> for notices. apps/slack-agent/test/progress.test.ts and apps/slack-agent/test/worker-stream.test.ts build or compare a turn literal and moved with it.
  • apps/slack-agent/src/index.ts wires the notice narrator because that is where narrateProgressTurn and trusted.teamId live; apps/slack-agent/src/delegation.ts only forwards the optional callback.

Not covered: right-hand's Spanish values (row R1) and the live thread (wave-end pass).

cgaravitoq and others added 5 commits October 8, 2026 16:04
Bloop's threads read Spanish for every narrated progress note while "Started.",
"Failed." and "Outcome unknown." stayed English, because the delegation route
builds them from a hardcoded map and never reaches the narration path. The tests
pin the labels config with its blank and absent fallbacks, the labelled line a
deployment without narration posts, the turn that carries the label and the
runner's note when narration is configured, and the labelled line that still
goes out when that turn cannot start, so the change that follows has a witness
that fails without it.
A deployment that sets progress narration reads Spanish in every thread, while
the delegation state notices stayed English: the route built them from a
hardcoded map and posted them itself. Delegation config now carries running,
failed and unknown labels with today's English strings as defaults, a missing or
blank value falling back to them, and a configured narration hands the labelled
notice to the narration path a milestone already takes, keyed
delegation:<task id>:<state> so a retried state never reaches the thread twice,
with the labelled line as that turn's fallback. Without narration the notice is
posted directly as before.

The narration turn now carries the idempotency key its host must dispatch under,
because a turn keyed only by a milestone id cannot tell a delegation notice from
a milestone of the same id.
The delegation section listed the config keys and the state notices, so it now
says which words the labels name, that a blank value keeps the default, and that
a configured narration turns the notice into a narrated reply with the labelled
line as its fallback.
Review P1: the contract says a deployment with progress narration hands each
delegation state notice to the narration path and posts nothing directly, yet
the only wiring that does so lives in the worker entry, which no test reached.
Dropping narrateNotice from configuredDelegation in index.ts left every test
green while a narrated deployment went back to posting English lines itself.

The new worker test loads the entry with delegation labels and narration
configured, reports a running state over the real route, and demands one turn
dispatched under delegation:<task id>:running with the labelled fallback and no
Slack call. The migrated SQLite D1 stand-in moves to a shared test module so the
worker test can seed the task the route transitions.
@cgaravitoq
cgaravitoq merged commit 30f8c61 into staging Oct 8, 2026
2 checks passed
@cgaravitoq
cgaravitoq deleted the cgaravitoq/bd3-a5-state-labels branch October 8, 2026 14:23
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