Skip to content

Tracking: Update/Edit/Replace triad wave for the replace-semantic recordables (documents #543, todolists #544/#545, schedule entries #546/#547) #374

Description

@jeremy

SCOPE REVISED 2026-07-27 — see the BC3 contract audit.

The premise below ("sibling recordable endpoints share the same server contract") does not hold uniformly, verified against bc3 c308693171:

  • Messages is merge, not replace (Recordable#changing, plus an explicit key-guard on category_id) — dropped from scope.
  • Cards is hybrid (merge for title/content, key-guarded for assignee_ids, forced-replace for due_on) — moved to Cards.update silently erases due_on on every sparse update (BC3 forced-replace) #467, and must not get a Replace* rename.
  • Documents, Todolists, Schedule entries are genuine replace — these three keep the triad.

Blocked on five contract/modeling questions documented in that comment (see also the follow-up correction: blockers 4 and 5 are read-modeling gaps, not blockers). One is a live SDK-reachable data-loss bug — ScheduleEntry's unconditional replace_participants wipes every participant on any update omitting participant_ids — and one is an active corruption trap: ScheduleEntry's writable :url collides with the recording's API url on read-back.


🤖 Drafted with agent assistance; filed by @jeremy.

Context

#372 fixed the partial-PUT-against-replace-semantics data-loss bug for todos: the spec operation was renamed to ReplaceTodo (content required, x-basecamp-write-semantics: {mode: replace, clearsOmitted: true}), and every SDK gained the three-method surface — merge-safe update (GET → overlay set fields → full PUT), edit (read-modify-write closure), and verbatim replace.

Sibling recordable endpoints share the same server contract (full replace, omission clears) and today have the same defect shape: typed partial-update surfaces transmitting sparse PUTs.

Scope

Audit and convert, using todos as the template (spec rename + @basecampWriteSemantics + generator grouping + per-language extension + conformance fixtures):

  • Messages (UpdateMessage)
  • Documents (UpdateDocument)
  • Cards (UpdateCard)
  • Todolists (UpdateTodolistOrGroup)
  • Schedule entries (UpdateScheduleEntry)

Each one needs the same verification as todos: confirm the server contract from BC3 controllers/docs (which fields are writable, which are required), then encode it in the behavior model and update-merge / edit-clear / replace-omission-clears conformance cases.

Template

The todos implementation to copy from:

  • Spec: ReplaceTodo in spec/basecamp.smithy + basecampWriteSemantics trait
  • Per-language: Go todos.go (shared replace transport + full-state vs sparse builders), TS services/todos-extensions.ts, Python services/todos.py (context-manager edit), Ruby services/todos_extensions.rb (prepend via zeitwerk on_load), Kotlin services/TodosService.kt (open generated class + accessor override), Swift TodosServiceExtensions.swift
  • Conformance: conformance/tests/todos_write.json + requestMethod/requestBody/requestBodyAbsent assertions

Related

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