Skip to content

Card step updates wholesale-replace fields: assign --step wipes due_on; step update --due wipes assignees #604

Description

@jeremy

Observed on released v0.8.0 against production; still present in v0.8.1.

#496 fixed the 400 on step assignment by carrying the step title over, but
the round-trip stops there — the other mutable fields are still dropped, so any
single-field step mutation silently destroys the rest:

$ basecamp cards step create "Step with title and due date" --card <card> --in <project> --due 2026-08-14
# step: title set, due_on 2026-08-14

$ basecamp assign <step> --step --to me --in <project>
# ok: true — but due_on is now null (title preserved, assignee set)

$ basecamp cards step update <step> --in <project> --due 2026-08-14
# ok: true — due_on restored, but assignees are now [] (title preserved)

Correction: this is Rails-owned, not CLI-owned

This issue originally said "steps#update in BC5 replaces the recordable
wholesale, so the CLI owns preservation." The first half is right and the
second half is wrong. app/controllers/steps_controller.rb:

def update
  @recording.update! recordable: new_step
  @recording.replace_assignees find_assignees, notify: true

new_step is Step.new step_params — a fresh Step built from whatever the
request carried, so an omitted due_on produces one without a due date.
replace_assignees then runs unconditionally; with no assignee_ids key,
assignee_ids falls through to "".split(",")[], so every assignee is
removed.

Both clears are performed by the server, from the absence of a key.
Omitting a field and explicitly clearing it are the same request on the wire,
so no client change reaches this — including pointerizing
UpdateStepRequest.DueOn in the SDK, since a nil *string is omitted from the
JSON and the controller clears on that omission regardless.

The only client-side workaround is read-modify-write on every mutation, which
is racy against concurrent edits, doubles the request count, and has to be
reimplemented by every API consumer.

Lane

  1. BC3 Rails (blocking): make steps#update presence-aware, the way
    cards#update already is for assignees (update_assignees? guards on
    has_key?) and for unmentioned attributes (recordable.changing(...)).
    Filed as a communiqué in Ask BC3 for presence-aware card step updates #608.
  2. SDK (dependent follow-up): pointer semantics on UpdateStepRequest,
    mirroring UpdateCardRequest (pointerized in v0.10.0) — what lets a client
    express omit-vs-clear once the server honours the difference. Inert until
    then.
  3. CLI: nothing to change. internal/commands/cards.go and
    internal/commands/assign.go stay as they are pending the Rails fix.

A deployed BC3 fix stops the data loss for every already-installed v0.8.x
client, with no CLI or SDK release.

Predicted when #496 was verified (Jul 13); confirmed it survived into v0.8.0
and v0.8.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions