Skip to content

feat: render a milestone status on the task root - #43

Merged
cgaravitoq merged 5 commits into
stagingfrom
cgaravitoq/bd3-a3-root-status
Oct 8, 2026
Merged

cgaravitoq merged 5 commits into
stagingfrom
cgaravitoq/bd3-a3-root-status

Conversation

@cgaravitoq

Copy link
Copy Markdown
Owner

Row A3 of wave bloop-delegation-3: POST /progress implements contract section 9, the root status. Root-only milestones post no reply, an owned root renders and stores the latest status, and a receipt answers the root's ts on the first call and on every replay.

Commits, in dependency order

  1. c95d493 migration: 0009_progress_root_status.sql adds the nullable status column, with the schema tests.
  2. 5d9b807 test: the route and storage tests, which fail on the base tree.
  3. 497590d feat: the route stores and renders the status, and the README lists the field.

Verify

  • status-only milestone edits the owned root and answers {ok, ts: <root ts>} with no reply posted: progress root status > edits the owned root for a root-only milestone and answers that root without a reply (the fake Slack answers the root 171.1 and a reply 171.2, so the answer is the root's ts, and only the root post and the chat.update are recorded).
  • the same id again answers the same receipt with no Slack call: progress root status > answers the replay of a root-only milestone from its receipt without a Slack call (the retry recorder records no call).
  • a 301-char status answers 400 status_too_long: progress root status > refuses a status longer than 300 characters after redaction without clamping it (301 refused, 300 accepted, and a 400+ char raw status that redacts to [secret] accepted, so the bound is the redacted one).
  • an unowned root stores the status, is not edited, answers 200: progress root status > stores the status of a root-only milestone on a thread it does not own and leaves it alone (no chat.update, the row keeps root_text, the receipt carries 555.000).
  • a failed chat.update answers 500 and the retry runs: progress root status > answers 500 when Slack refuses the root edit a root-only milestone is, and lets the retry through (the receipt is dropped and the same id runs the update again).
  • a later milestone without status still renders the stored status: progress root status > renders the stored status on a later milestone that sends none (the root reads Release 42 · Blocked · 2 of 5 landed).
  • migration 0009 applies and the roots back-fill reads NULL status: D1 migration schema > applies every migration file in wrangler's order, > applies one statement batch per migration file and > leaves the status of the roots stored before it null.
  • bun run verify green: format:check, lint, typecheck, 380 tests across 17 files, knip, deploy:dry and audit (0 vulnerabilities).

The new tests fail on the base: with packages/core/src/progress.ts restored from f28de36, the two suites report 8 fail / 77 pass, the seven progress root status tests plus slack progress root storage > keeps the latest status of a root and leaves a root stored without one without a status.

Not covered

  • bloop-notify's --status flag and its root-only client (row D8).
  • The live wave thread (wave-end pass).

cgaravitoq added 5 commits October 8, 2026 16:04
A root that only shows the label of the last kind cannot say what the task
last reported, so the delegation contract gives every root a status that
later milestones render. Migration 0009 adds the nullable column, and roots
stored before it read NULL.
The route pins the shape section 9 asks for before it exists: a status-only
milestone edits the owned root and answers that root's ts, a replay answers
from its receipt, a status is stored and rendered by later milestones, an
unowned root keeps its text, a 301-character status is refused and a failed
root edit is retryable. The storage test pins the column the same way.
A milestone that carries only a status now edits the owned root and answers
that root's ts, so a wave's root reads as a live board while its replies
stay for what needs the owner. The status is stored with the root and every
later milestone renders it; an owned root is edited only when its rendered
text differs, and an unowned one is never edited.

A failed edit of a root-only milestone answers 500 and deletes the receipt,
because that edit is the milestone and the sender's retry must run; a
milestone with a reply still posts it and logs the failure as before.
P1 of the A3 review: a milestone with a status and text whose chat.update
Slack refused still got its reply, but the status was never stored, so every
later milestone rendered the root without it. Contract section 9 stores the
latest status on the root; storing it with the old root text keeps the
rendered text different, so the next milestone retries the edit with it.
…root-status

# Conflicts:
#	packages/core/src/progress.ts
@cgaravitoq
cgaravitoq merged commit fc1fe21 into staging Oct 8, 2026
2 checks passed
@cgaravitoq
cgaravitoq deleted the cgaravitoq/bd3-a3-root-status branch October 8, 2026 14:17
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