Skip to content

Stop echoing back step fields the caller never changed - #620

Merged
jeremy merged 3 commits into
mainfrom
fix/card-step-title-workaround
Aug 4, 2026
Merged

Stop echoing back step fields the caller never changed#620
jeremy merged 3 commits into
mainfrom
fix/card-step-title-workaround

Conversation

@jeremy

@jeremyjeremy commented Aug 4, 2026

Copy link
Copy Markdown
Member

bc3#12521 (4e34dc83eb, on
master) made JSON step and card updates presence-aware. steps#update now
does @recording.recordable.changing(step_update_params) and replaces
assignees only if update_assignees?, so omitting a parameter leaves it
unchanged; an explicit null clears the due date and an explicit [] still
removes everyone. The public docs say the same thing now.

That retires the title workaround at all three call sites. Each re-sent the
step's current title because the old controller rebuilt the recordable from
scratch and rejected an update without one:

Tests

TestCardsStepUpdate{Assignees,Due}OnlyCarriesTitle asserted the old
behaviour, so they're inverted rather than supplemented. getCount == 0 is what
proves the extra read is gone — asserting on the request body alone would not.
New coverage pins the no-echo contract for assign/unassign --step, including
that unassigning the last person sends an explicit empty list rather than
omitting the key.

Verified live

Against production (QA sandbox project, card created and trashed afterward):

steprequest sendsresult
assign --stepassignee_ids onlytitle anddue_on survived
cards step update --duedue_on onlytitle and assignees survived
unassign --step (last one)assignee_ids: []assignees cleared; title + due_on survived

Rows 1 and 2 are exactly the two data-loss bugs reported in #604 — both are
fixed server-side, with no client release needed. Row 3 also settles a claim I
made and then withdrew on the record: unassigning the last person does not
silently no-op. The SDK builds body := map[string]any{} and guards on
req.AssigneeIDs != nil, and the CLI's removeID returns a non-nil empty
slice, so assignee_ids: [] does reach the wire.

bin/ci is green.

Not in scope

Explicit due-date clears. The CLI can't express one — cards.go only sets
req.DueOn when --due is non-empty, so --due "" is indistinguishable from
omitting the flag. Separately, SDK v0.12.0 encodes a card's due-date clear by
omittingdue_on, which post-#12521 means "leave unchanged"; that's already
fixed upstream in basecamp-sdk#647 and needs an SDK release to reach us.

Refs #604 — closing that issue is a separate step, once this merges.


Summary by cubic

Send only changed fields when updating card steps to avoid clobbering titles or due dates and remove the extra read in cards step update. Also updates SKILL.md to document presence-aware step updates (omit unchanged fields; use null/[] to clear).

Written for commit 8e5787f. Summary will update on new commits.

Review in cubic

bc3#12521 made JSON step updates presence-aware: omitting a parameter now
leaves it unchanged, an explicit null clears the due date, and an explicit
empty assignee list still removes everyone. The API docs say so, and a live
check against production confirms it.
That retires the title workaround at three call sites. All three re-sent the
step's current title because the old controller rebuilt the recordable from
scratch and rejected an update without one:
- cards step update carried a dedicated CardSteps().Get purely to fetch a
title it would send straight back. That read is gone.
- assign --step and unassign --step already fetch the step for its assignee
list, so no request is saved there — but they stop asserting a value the
caller never asked to change, which is the actual bug from #604: a
concurrent title edit would be silently reverted.
TestCardsStepUpdate{Assignees,Due}OnlyCarriesTitle asserted the old behaviour,
so they are inverted rather than supplemented — getCount == 0 is what proves
the extra read is gone, since the request body alone cannot. New coverage pins
the no-echo contract for assign/unassign --step, including that unassigning the
last person sends an explicit empty list.
Refs #604
CopilotAI balanced review requested due to automatic review settings August 4, 2026 18:22
@github-actionsgithub-actionsBot added commands CLI command implementations tests Tests (unit and e2e) labels Aug 4, 2026
CopilotAI reviewed Aug 4, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:5b395944b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadinternal/commands/assign_test.go
Comment threadinternal/commands/cards_test.go
TestAssignStepCarriesTitle and TestUnassignStepCarriesTitle asserted the
workaround this branch removes. I missed them and added a near-duplicate pair
instead; CI caught it. Invert the originals in place — they already have a
transport that covers both directions and resolves people — give that
transport's step a due_on so the no-echo assertion covers the due date too,
and drop my duplicates.
CopilotAI review requested due to automatic review settings August 4, 2026 18:34
CopilotAI reviewed Aug 4, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

The skill's todo-subtask section still told agents to include the current title
on every raw step update and warned that omitting it "may reset the step title
to Untitled". Under bc3#12521 that is exactly backwards: echoing a title you
did not mean to change is what reverts a concurrent edit — the same failure
this branch removes from the CLI's own code paths.
Todo subtasks are not a separate contract. PUT card_tables/steps/:id routes to
StepsController#update for both spellings (config/routes.rb:1158), so the
presence-aware behaviour is identical. Verified on a live todo-backed subtask:
PUT with only assignee_ids preserved title and due_on; PUT with only due_on
preserved title and assignees.
Document the clears explicitly ("due_on": null, "assignee_ids": []) and keep the
note that assignee_ids replaces the whole list.
CopilotAI review requested due to automatic review settings August 4, 2026 19:29
@github-actionsgithub-actionsBot added the skills Agent skills label Aug 4, 2026
CopilotAI reviewed Aug 4, 2026

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@jeremy
jeremy merged commit 5760ba3 into mainAug 4, 2026
24 of 25 checks passed
@jeremy
jeremy deleted the fix/card-step-title-workaround branch August 4, 2026 20:34
jeremy added a commit that referenced this pull request Aug 22, 2026
…or-roundtrip
* origin/main: (96 commits)
ci: bump the github-actions group with 6 updates (#639)
Reject three more doomed invocations before draining stdin (#645)
Stdin `-` support everywhere sensible; usage error for stray `-` elsewhere (#641)
Add hey-cli Windows signing secrets to the release env manifest (#642)
deps: bump the go-dependencies group with 5 updates (#638)
Update nix flake and plugin version for v0.9.1
ci: bump the github-actions group with 4 updates (#633)
Add basecamp files replace: publish a new version of an uploaded file (#634)
Add basecamp files versions — HELD, blocked on the SDK (#622)
Update nix flake and plugin version for v0.9.0
Make the Codex probe's timeout actually bound doctor (#629)
Make the lockstep check catch stale agreement and .yaml workflows (#628)
Keep refreshing opencode's other spelling (#627)
Lint the release the same way we lint everything else (#625)
Install the skill where opencode actually looks (#624)
Take the communiques out of the source tree (#623)
Correct the API coverage claim: 183/184, not 100% (#621)
Stop echoing back step fields the caller never changed (#620)
Drive the circuit breaker's clock from tests, not sleep() (#619)
Tell agents the truth about card column moves (#618)
...
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commandsCLI command implementationsskillsAgent skillstestsTests (unit and e2e)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@jeremy