Post a hill chart progress update
Adopts POST /{accountId}/todosets/{todosetId}/hills/versions.json from basecamp/bc3#14214, which posts a progress update on a to-do set's hill chart.
basecamp/bc3#14214 merged at 4303f1dafd4. This PR repins bc3 to that commit (25b74b2a): the route table now carries the route (430 to 431 routes), the spec/bc3-route-allowlist.yml waiver that carried it ahead of the pin is deleted, and spec/api-gaps/README.md records the range's triage. The range's other two API changes, bc3#14191 (a 422 for creating under a trashed recording, and moves restoring trashed recordings) and bc3#14176 (a reply_token on a chatbot command's callback_url), add no route or field the SDK models.
Card: https://app.basecamp.com/2914079/buckets/48521764/card_tables/cards/10457799215
The operation
CreateHillChartVersion. Every field is optional:
description: rich-text HTML.dots:[{id, position}], whereidis a tracked todolist andpositionruns 0 to 100.subscriptions: the person ids to notify.
It answers 201 with the same HillChartVersion shape GetHillChartVersion returns. It answers 422 {error} when a dot doesn't validate, 403 for clients and 404 for an unknown to-do set. It's a non-idempotent create with the usual inert retry block, so every runtime sends it once (SPEC §7 Gate 2).
Added per language
| Language | API |
|---|---|
| Go | HillChartsService.CreateVersion(ctx, todosetID int64, req *CreateHillChartVersionRequest) (*HillChartVersion, error), with new HillChartDotPosition{ID int64; Position int} and CreateHillChartVersionRequest{Description string; Dots []HillChartDotPosition; Subscriptions *[]int64}. req may be nil. A nil Dots is omitted and a non-nil one is sent even when empty. A position outside 0 to 100 is an ErrUsage before any request. |
| TypeScript | hillCharts.createVersion(todosetId, req: CreateVersionHillChartRequest). The request type is exported from the package root. |
| Ruby | hill_charts.create_version(todoset_id:, description: nil, dots: nil, subscriptions: nil) |
| Python | hill_charts.create_version(*, todoset_id, description=None, dots=None, subscriptions=None), sync and async |
| Kotlin | hillCharts.createVersion(todosetId, CreateHillChartVersionBody(description, dots, subscriptions)) |
| Swift | hillCharts.createVersion(todosetId:req: CreateHillChartVersionRequest(...)), with the new HillChartDotPosition model |
| Rust | hill_charts().create_version(todoset_id, &CreateHillChartVersionRequestContent { .. }), with the new HillChartDotPosition type |
Also in this PR:
- Shared fixtures
spec/fixtures/hill_charts/create-version{,-request}.json. The request is bc3's documented one. - Four conformance cases in
conformance/tests/hill_chart_versions.json, dispatched in all seven runners:- the documented request asserted as the exact whole body;
- an empty request sends
{}; - a
422maps to a validation error with bc3's message; - a
503is not retried.
- Two corrections:
- The Go conformance runner's
toInt64Slicenow readsjson.Number. Fixtures decode withUseNumber, so it was silently sending[]for every id array. - SPEC.md's non-idempotent count is now 192. It was already stale before this change.
- The Go conformance runner's
Testing
Unit tests per language:
| Language | Tests | Result |
|---|---|---|
| Go | 4 new: exact body, nil and empty request, off-hill positions with no request sent, 422 | 16/16 hill chart tests pass |
| TypeScript | 3 new | 14/14 pass, plus type tests and typecheck |
| Ruby | 3 new | 18 runs, 0 failures |
| Python | 4 new | 13 pass |
| Rust | 2 wire tests and a 422 error_case! | 10/10 hill chart tests pass; the full basecamp-sdk suite passes |
| Kotlin | 3 new | 8/8 pass |
| Swift | 3 new | Not run locally: no macOS here, and the Swift SDK doesn't build on Linux. CI covers it. |
Conformance passed in every runner here, the new cases included:
| Runner | Passed | Skipped |
|---|---|---|
| Go | 340 | 2 |
| Python | 338 | 4 |
| Ruby | 319 | 23 |
| Rust | 338 | 4 |
| Kotlin | 335 | 7 |
| TypeScript | 395 | 6 |
| Swift | not run (macOS only) |
I ran make -k check-targets locally. Everything passed except the Swift targets, which skip on Linux, and four failures:
- golangci-lint, actionlint and cargo-deny failed only because mise had no version pinned in the worktree. Each passed when rerun on a pinned version: golangci-lint 2.15.0 reported 0 issues, actionlint 1.7.12 was clean, and cargo-deny reported ok.
- Rust
guarantees.rspinned the operation count at 294. It's now 295.
Semver and release notes
- Minor, breaking for one narrow case. It's additive in every SDK. The exception is Go code that implements the generated
ClientInterface/ClientWithResponsesInterfaceitself: those interfaces gainCreateHillChartVersionand itsWithBodyandWithResponseforms. MIGRATING.md has the# Unreleasedsection, so the PR is labelledbreaking. - Release-note section: Features (
enhancement).
Review
A cross-family review of origin/main...HEAD found one issue, now fixed:
- Fixed: the Ruby empty-request test asserted with
body: {}, which WebMock treats as matching any body, so the test couldn't catch anullor field-of-nulls regression. It now parses the body and compares it to{}(8e586a0e).
There were no other findings and nothing was declined. The local check run separately caught the stale Rust operation count, fixed in df6afc7a.
Summary by cubic
Adds CreateHillChartVersion to all seven SDKs (createVersion / create_version), letting callers post a progress update on a to-do set's hill chart. Every field is optional: a rich-text description, dots moving tracked todolists (position 0–100), and people to notify. It returns 201 with the same HillChartVersion shape as GetHillChartVersion, and 422 for invalid dots or 404 for an unknown to-do set.
Adopts basecamp/bc3#14214, which merged at 4303f1dafd4. This PR repins bc3 to that commit: the route table grows from 430 to 431 routes, the waiver that carried the route ahead of the pin is deleted, and the spec's API-gap triage records that the range's other changes add no route or field.
Breaking change
- Go's generated
ClientInterface/ClientWithResponsesInterfacegainCreateHillChartVersionplus itsWithBodyandWithResponseforms; hand-written implementations must add them. - Fixes the Go conformance runner's
toInt64Sliceto readjson.Number, which was silently sending[]for every id array.