Skip to content

Correct column move, question update, lineup marker, project timesheet and event boost contracts - #991

Merged
jeremy merged 4 commits into
mainfrom
fix-pass-api-semantics
Oct 15, 2026
Merged

jeremy merged 4 commits into
mainfrom
fix-pass-api-semantics

Conversation

@jeremy

@jeremy jeremy commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Five SDK contracts that disagree with what bc3 actually does. A production every-tool pass on the Basecamp MCP server (v0.2.7) found them. Each cause is in this SDK's spec or Go wrapper, so each fix belongs here. The MCP server only documents what the SDK sends and returns. All cited bc3 behavior is inside the revision spec/api-provenance.json currently pins, and the cited code is unchanged on bc3 HEAD, so no repin.

Changes

A. MoveCardColumn: target_id is the card table, position is a 1-based slot

  • bc3: app/models/kanban/moves/column_move.rb valid_destination requires destination.kanban_board?. Kanban::MovesController#create rescues ActiveModel::ValidationError to 422. Recording::Positioner#rescore treats position <= 1 as the top and > count as the bottom. doc/api/sections/card_table_columns.md says "target_id - the id of the card table" and "position - index among the columns (ignoring Triage, Not Now or Done). Default: 1".
  • What was wrong: the spec typed target_id as CardColumnId. The Go doc said "the column ID to move relative to" and "zero-indexed position". A caller who followed the docs got 422 on every call; the MCP tool built on them failed every time.
  • Fix: target_id: CardTableId, plus doc comments on source_id, target_id and position. Go MoveColumnRequest docs are corrected, and CardColumnsService.Move fills an unset (zero) TargetID with the card table it is called with. The TS and Ruby tests now send the card table as the target.

B. UpdateQuestion: drop paused

  • bc3: app/controllers/questions_controller.rb question_params permits only :title and schedule: [...]. doc/api/sections/questions.md "Update a question" says the question object "must include title, schedule, or both".
  • What was wrong: a paused-only update answered 400. paused sent with a title was dropped silently and the call reported success, so the question stayed unpaused. Pausing has its own operations (PauseQuestion/ResumeQuestion).
  • Fix: paused is removed from UpdateQuestionInput, and the doc points to pause/resume. Go UpdateQuestion now refuses an update with neither a title nor a schedule before it reaches the wire (ErrUsage).

C. Create/UpdateLineupMarker return the marker

  • bc3: app/controllers/lineup/markers_controller.rb create renders @marker.as_json(except: :account_id), status: :created, and update renders the same with :ok. This has been true since bc3 f85369fbad4 (#9946, 2026-03-18).
  • What was wrong: the outputs were empty, so the SDKs threw the marker away and callers had to list markers to find the new id.
  • Fix: both outputs carry LineupMarker. Go CreateMarker and UpdateMarker return (*LineupMarker, error).
  • bc3 doc follow-up: doc/api/sections/lineup_markers.md still says "201 Created with an empty response body" and "200 OK with an empty response body". Those lines are stale.

D. Project gains timesheet_enabled

  • bc3: doc/api/sections/projects.md response examples include timesheet_enabled. TimesheetsController#set_recording raises RecordNotFound unless project.timesheet_recording exists, and Project::Timesheetable#record_timesheet creates it only once the project has timesheet_enabled.
  • What was wrong: the SDK dropped the field, so a caller couldn't tell why GET /projects/:id/timesheet.json answered 404.
  • Fix: Project.timesheet_enabled is added, with a doc comment about the 404. Go: Project.TimesheetEnabled bool.
  • Possible follow-up, not done here: bc3 ProjectOperations#create_project_params permits timesheet_enabled on project create and update, but only for people with can_access_timesheet?. doc/api doesn't document it as a write parameter, so this PR doesn't add it to the inputs.

E. Event boosts: which events are boostable

  • bc3: app/models/event.rb:23 BOOSTABLE_ACTIONS = %w[ completed adopted column_changed moved_in ]. BoostsController#ensure_boostable answers 403 on index and create for any other event. #ensure_comments_are_open answers 403 on create when the recording's comments are closed. #set_boostable answers 404 (render_not_found) for a recording the person can't see.
  • Fix: doc comments on ListEventBoosts, CreateEventBoost and Event.boosts_count/boosts_url. The fields were already in the spec. This change is docs only.
  • bc3 doc follow-up: doc/api/sections/boosts.md lists completed, adopted and column_changed but leaves out moved_in.

Breaking changes (in MIGRATING.md under v0.25.0)

  • Go: UpdateQuestionRequest.Paused is removed. Every SDK's update-question request type loses paused.
  • Go: LineupService.CreateMarker and UpdateMarker now return (*LineupMarker, error) instead of error. The other SDKs return the marker where they returned nothing.

Verification

Overlap

#990 and #988 have merged; this PR is rebased on top of both, and the combined ListMarkers keeps #990's empty-not-nil initialization.


Summary by cubic

Corrects five SDK contracts that disagreed with what Basecamp actually does, found in a production pass on the MCP server. Each fix brings the spec, generated SDKs, and Go wrapper in line with bc3 behavior.

Contract fixes

  • MoveCardColumn.target_id is now the card table, not the column; Basecamp rejects any other destination with 422, and position is the 1-based slot, with negative values refused. Go's Move fills an unset target with the card table it is called with.
  • UpdateQuestion drops paused, which Basecamp never reads on update; Go refuses an update with neither title nor schedule before the wire, and a schedule replaces the whole schedule.
  • Lineup marker create and update now return the saved marker instead of nothing.
  • Project gains timesheet_enabled so callers can tell why the timesheet report answers 404.
  • Event boost operations document which events are boostable and their 403/404 cases.
  • Because the TS, Ruby and Kotlin generators drop member docs, contract details — the card-table target, the 1-based position, the whole-schedule replacement — now live in operation descriptions, kept single-line so those generators don't truncate them.

Breaking changes

  • paused is removed from every SDK's update-question request type; Go's UpdateQuestionRequest.Paused is gone.
  • Go LineupService.CreateMarker/UpdateMarker return (*LineupMarker, error); other SDKs return the marker where they returned nothing.
  • Migration steps are recorded in MIGRATING.md under v0.25.0.

Written for commit 425d18c. Summary will update on new commits.

View guided diff Turn on auto-fix

Copilot AI balanced review requested due to automatic review settings October 15, 2026 09:19
@jeremy jeremy added breaking Breaking change to public API bug Something isn't working labels Oct 15, 2026
@github-actions github-actions Bot added typescript Pull requests that update TypeScript code ruby Pull requests that update the Ruby SDK go kotlin swift spec Changes to the Smithy spec or OpenAPI python Pull requests that update the Python SDK rust Rust SDK labels Oct 15, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T16:26:39.738622Z 425d18c Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI 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.

🔵 Needs a closer look

Breaking API changes across seven SDKs warrant human approval once the outstanding cross-language verification is complete.

1 open finding
What changed in this PR

Aligns five API contracts across the Smithy specification, generated SDKs, and Go wrappers.

Changes:

  • Corrects column-move targets and question updates, and returns saved lineup markers.
  • Exposes project timesheet availability and documents event-boost restrictions.
  • Updates regression tests, fixtures, and migration guidance.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
typescript/​tests/​services/​lineup.test.ts Asserts returned markers.
typescript/​tests/​services/​checkins.test.ts Updates questions by title.
typescript/​tests/​services/​card-columns.test.ts Checks table target and position.
typescript/​src/​generated/​services/​lineup.ts Returns markers from writes.
typescript/​src/​generated/​services/​checkins.ts Removes paused updates.
typescript/​src/​generated/​services/​card-columns.ts Corrects move documentation.
typescript/​src/​generated/​services/​boosts.ts Updates boost documentation.
typescript/​src/​generated/​schema.d.ts Reflects corrected contracts.
typescript/​src/​generated/​openapi-stripped.json Synchronizes OpenAPI contracts.
typescript/​src/​generated/​metadata.ts Refreshes generation timestamp.
swift/​Sources/​Basecamp/​Generated/​Services/​LineupService.swift Returns saved markers.
swift/​Sources/​Basecamp/​Generated/​Models/​UpdateQuestionRequest.swift Removes paused input.
swift/​Sources/​Basecamp/​Generated/​Models/​Project.swift Adds timesheet availability.
spec/​fixtures/​projects/​get.json Includes enabled timesheet.
spec/​basecamp.smithy Defines the five contract corrections.
rust/​basecamp-sdk/​src/​generated/​types.rs Updates request and response models.
rust/​basecamp-sdk/​src/​generated/​services/​lineup.rs Decodes saved markers.
rust/​basecamp-sdk/​src/​generated/​services/​checkins.rs Documents pause/resume alternatives.
rust/​basecamp-sdk/​src/​generated/​services/​card_columns.rs Updates move documentation.
rust/​basecamp-sdk/​src/​generated/​services/​boosts.rs Documents boost restrictions.
rust/​basecamp-sdk/​src/​generated/​routes.rs Marks marker responses as JSON.
ruby/​test/​basecamp/​services/​lineup_service_test.rb Asserts returned markers.
ruby/​test/​basecamp/​services/​checkins_service_test.rb Removes paused update input.
ruby/​test/​basecamp/​services/​card_columns_service_test.rb Uses table destination.
ruby/​lib/​basecamp/​generated/​types.rb Adds timesheet availability.
ruby/​lib/​basecamp/​generated/​services/​lineup_service.rb Returns marker response data.
ruby/​lib/​basecamp/​generated/​services/​checkins_service.rb Removes paused updates.
ruby/​lib/​basecamp/​generated/​services/​card_columns_service.rb Corrects move parameter documentation.
ruby/​lib/​basecamp/​generated/​services/​boosts_service.rb Updates boost documentation.
ruby/​lib/​basecamp/​generated/​metadata.json Refreshes generation timestamp.
python/​src/​basecamp/​generated/​types.py Updates project and question types.
python/​src/​basecamp/​generated/​services/​lineup.py Returns markers synchronously and asynchronously.
python/​src/​basecamp/​generated/​services/​checkins.py Removes paused updates.
python/​src/​basecamp/​generated/​services/​card_columns.py Corrects move documentation.
python/​src/​basecamp/​generated/​services/​boosts.py Documents boost restrictions.
openapi.json Publishes corrected wire contracts.
MIGRATING.md Explains compatibility changes.
kotlin/​sdk/​src/​commonMain/​kotlin/​com/​basecamp/​sdk/​generated/​services/​Types.kt Removes paused input.
kotlin/​sdk/​src/​commonMain/​kotlin/​com/​basecamp/​sdk/​generated/​services/​lineup.kt Decodes saved markers.
kotlin/​sdk/​src/​commonMain/​kotlin/​com/​basecamp/​sdk/​generated/​services/​checkins.kt Removes paused serialization.
kotlin/​sdk/​src/​commonMain/​kotlin/​com/​basecamp/​sdk/​generated/​services/​card-columns.kt Updates move documentation.
kotlin/​sdk/​src/​commonMain/​kotlin/​com/​basecamp/​sdk/​generated/​services/​boosts.kt Updates boost documentation.
kotlin/​sdk/​src/​commonMain/​kotlin/​com/​basecamp/​sdk/​generated/​models/​Project.kt Adds timesheet availability.
go/​pkg/​generated/​client.gen.go Updates generated models and parsers.
go/​pkg/​basecamp/​projects.go Exposes timesheet availability.
go/​pkg/​basecamp/​projects_test.go Tests timesheet field propagation.
go/​pkg/​basecamp/​lineup.go Returns markers through shared conversion.
go/​pkg/​basecamp/​lineup_test.go Tests marker write responses.
go/​pkg/​basecamp/​checkins.go Rejects empty question updates.
go/​pkg/​basecamp/​checkins_test.go Tests update validation and serialization.
go/​pkg/​basecamp/​catalog/​catalog.json Synchronizes operation contracts and documentation.
go/​pkg/​basecamp/​cards.go Defaults move target to card table.
go/​pkg/​basecamp/​cards_test.go Tests default and explicit targets.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread spec/basecamp.smithy Outdated
@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Local verification at 07dd413: make -k check-targets exits 2, and the only failing target is rs-deny (RUSTSEC-2026-0349, rustls 0.23.45). That failure is already on origin/main and #989 fixes it. Every other check target passes. The earlier full make check run also confirmed the checks leave all 11 lockfiles byte-identical.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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: 07dd413386

ℹ️ 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 thread spec/basecamp.smithy Outdated
Comment thread spec/basecamp.smithy
Comment thread spec/basecamp.smithy

Copilot AI 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.

🔵 Needs a closer look

Breaking changes across seven SDKs warrant human sign-off while complete cross-language verification remains unconfirmed.

0 open findings

1 resolved since last review

🧠 Review effort: Balanced

@jeremy
jeremy requested a balanced review from Copilot October 15, 2026 10:00
@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Review threads: 4 resolved (4 fixed; one also declines a part, with the reasoning in its thread).
Declined: default/validate target_id outside Go. Those services are generated, so the corrected contract is carried in the operation docs instead.
Open for a decision: CI is red on 1a8e78d only from the rustls advisories (RUSTSEC-2026-0346/0347/0349) and the Go 1.26.8 net/http advisories (GO-2026-6613/6617). Both are already on main, and #989 fixes them. This PR needs a rebase after #989 merges, then a green CI run.
Not reported on the head: Copilot and Codex were re-requested on 1a8e78d.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 1a8e78d6e7

ℹ️ 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".

Copilot AI 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.

🔵 Needs a closer look

Breaking contracts across seven SDKs need human confirmation of upstream behavior and outstanding verification results.

0 open findings

🧠 Review effort: Balanced

@jeremy
jeremy force-pushed the fix-pass-api-semantics branch from 1a8e78d to 126cbb8 Compare October 15, 2026 15:16
@jeremy
jeremy requested a balanced review from Copilot October 15, 2026 15:16
@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: 126cbb8367

ℹ️ 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".

Copilot AI 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.

🔵 Needs a closer look

Breaking changes across seven SDKs need human sign-off because upstream contracts and complete verification results remain independently unverified.

0 open findings

🧠 Review effort: Balanced

@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Nothing open on 126cbb8.

… timesheet and event boost semantics

- MoveCardColumn: target_id is the card table (bc3 Kanban::Moves::ColumnMove
  refuses any other destination with 422); position is the 1-based slot among
  the columns. Go Move defaults an unset TargetID to the card table.
- UpdateQuestion: drop paused, which bc3 never permits on update; Go refuses an
  update with neither title nor schedule before the wire.
- Create/UpdateLineupMarker return the saved marker bc3 renders.
- Project gains timesheet_enabled.
- Event boost operations document which events are boostable and the 403/404s.
The TypeScript, Ruby and Kotlin service generators keep only the first line
of an operation's description, which cut the event boost and question update
docs mid-sentence.
…docs

Generators other than Go drop member docs, so the card-table target_id and
the whole-schedule replacement on question updates now live in the operation
descriptions every generator keeps. Position docs no longer promise that a
negative slot is accepted.
@jeremy
jeremy requested a balanced review from Copilot October 15, 2026 16:24
@jeremy
jeremy force-pushed the fix-pass-api-semantics branch from 126cbb8 to 425d18c Compare October 15, 2026 16:24
@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 425d18c535

ℹ️ 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".

Copilot AI 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.

🔵 Needs a closer look

Breaking contracts across seven SDKs need human confirmation against the cited upstream implementation, which was not independently verified.

0 open findings

🧠 Review effort: Balanced

@jeremy
jeremy merged commit 0b3283d into main Oct 15, 2026
56 checks passed
@jeremy
jeremy deleted the fix-pass-api-semantics branch October 15, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking change to public API bug Something isn't working go kotlin python Pull requests that update the Python SDK ruby Pull requests that update the Ruby SDK rust Rust SDK spec Changes to the Smithy spec or OpenAPI swift typescript Pull requests that update TypeScript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants