Skip to content

fix: review follow-ups for the 2026-09-01 API migration - #379

Open
leggetter wants to merge 2 commits into
feat/api-2026-09-01from
review/api-2026-09-01-fixes
Open

fix: review follow-ups for the 2026-09-01 API migration#379
leggetter wants to merge 2 commits into
feat/api-2026-09-01from
review/api-2026-09-01-fixes

Conversation

@leggetter

Copy link
Copy Markdown
Collaborator

Follow-ups from the review on #378. Targets feat/api-2026-09-01 so you can merge, cherry-pick, or close it - whichever is least disruptive.

Claude-assisted, same as the review comment. Every claim below was checked against core origin/staging or a real run, not just read off the diff.

The /projects item is deliberately not here: origin/staging registers the route on both /projects and /teams, so that one is a deploy-ordering call rather than a code change.

Behaviour

--delivery-group only where the API accepts it. It was added to every metrics subcommand, but resolving the filter schemas in core origin/staging gives delivery_group on event_filters_schema, attempt_filters_schema and queue_depth_filters_schema only. request_filters_schema, transformation_filters_schema, events_pending_timeseries_filters_schema and events_by_issue_filters_schema do not carry it, and those filters are additionalProperties: false - so it was a guaranteed 422 rather than an ignored field.

  • metrics requests and metrics transformations no longer register the flag.
  • metrics events keeps it, but errors client-side on the two routes that cannot use it (--measures pending, and per-issue).
  • metricsFlagOpts replaces the positional skipIssueID bool, so a second exclusion does not turn call sites into (cmd, f, true, false).

team_mode fallback. The cutover left none, and an empty product blanks ProjectProduct, ProjectMode and ProjectType together - after which IsGatewayProject("") is false and every gateway command fails with an empty type in the message. To be clear about the risk: the live API does return team_product today. I checked by stripping type, mode and product from a config and running whoami against prod - still Project type: Gateway. So this is insurance rather than a repair.

ListProjects no longer swallows a shape mismatch. The unmarshal error was discarded, so a renamed field or a wrapped envelope returns an empty list and a nil error - "you have no projects" instead of a failure. That is the specific risk an endpoint rename carries.

,omitempty on Event.DeliveryGroup, so event list --output json stops emitting "delivery_group": null on every event.

Consistency

One Profile.ResolveProjectType() replaces the five open-coded Type -> Product -> Mode ladders (gateway.go, whoami.go, SaveProfile, setProfileFieldsInViper, constructConfig). Same precedence, one place to get it wrong.

Case handling. ProjectTypeToProduct and IsGatewayProject are now case-insensitive like the three mappers beside them.

gofmt on the files touched here; profile.go and profile_credentials_test.go were unformatted on the branch. I have not reformatted the ~40 files that were already unformatted on main - that is not this PR's job.

Tests

The upgrade path had no coverage. config_test.go was untouched, yet constructConfig gained the back-fill every existing user hits on first run: a config written before this release has no project_product and it has to come from the legacy mode. Now asserted, along with project_product actually being persisted by UseProject.

The mapping tests covered about a third of the matrix. Now table-driven per function, including the empty-product case, case-insensitivity, and a round trip that pins the deliberate outbound-to-inbound flattening that was previously only described in a comment.

TestProfile_LegacyModeFallback covers the new fallback in all three Apply* paths, product-wins-over-mode, and both-absent.

The acceptance test now checks project_product reaches config.toml. Nothing did before - every other test on this path reads a mock this repo also writes, so the persisted field was never verified against a real CLI run.

Verification

go build ./..., go test ./pkg/..., go test -tags basic ./test/acceptance/ -run TestLoginAfterValidate401StartsBrowserFlowAcceptance and generate-reference --check all pass.

One thing I noticed but did not touch: go vet reports pkg/login/interactive_login.go:162: misuse of unbuffered os.Signal channel as argument to signal.Notify. Pre-existing on your branch and unrelated to the migration, but worth someone picking up - an unbuffered channel there can drop the signal.

Findings from reviewing #378. The `/projects` question is not addressed here -
that is a deploy-ordering decision, not a code change.

Only offer --delivery-group where the API accepts it. The flag was added to
every metrics subcommand, but delivery_group exists only on the events,
attempts and queue-depth filter schemas, and those filters are
additionalProperties:false. So `metrics requests --delivery-group` and
`metrics transformations --delivery-group` were a guaranteed 422, as were the
two `metrics events` routes that land on events-pending-timeseries or
events-by-issue. The flag is now omitted where it cannot work and rejected
client-side on the two routes that share a command with routes where it can.
metricsFlagOpts replaces the positional skipIssueID bool so a second exclusion
does not turn every call site into unreadable booleans.

Fall back to team_mode when team_product is absent. The cutover left no
fallback: an empty product blanks ProjectProduct, ProjectMode and ProjectType
at once, IsGatewayProject("") is false, and every gateway command then fails
with an empty project type in the message. The live API does return
team_product today - verified against prod with the type, mode and product
stripped from a config - so this is insurance, not a repair.

Stop ListProjects swallowing a shape mismatch. The unmarshal error was
discarded, so a renamed field or a wrapped envelope would return an empty list
and a nil error: "you have no projects" rather than a failure. Exactly the risk
an endpoint rename introduces.

Collapse five copies of the Type -> Product -> Mode ladder into
Profile.ResolveProjectType. Same precedence, one place to get it wrong.

Make the two case-sensitive mappers case-insensitive like their three
siblings in the same file. Nothing documented which was which.

Add ,omitempty to Event.DeliveryGroup so `event list --output json` does not
start emitting "delivery_group": null on every event.

Document --delivery-group in REFERENCE.md, including where it does not apply.

Run gofmt on the files this change touches; profile.go and
profile_credentials_test.go were unformatted on the branch.

Tests. The upgrade path had none: a config written before this release has no
project_product, and nothing asserted it gets derived from the legacy mode -
which is what every existing user hits on first run. The mapping tests covered
roughly a third of the matrix; they are now table-driven per function and
include the empty-product case, case-insensitivity, and a round trip that pins
the deliberate outbound-to-inbound flattening. The acceptance test now asserts
project_product actually reaches config.toml, which nothing did before: every
other test on this path reads a mock this repo also writes.

Refs #378

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnrKWZQASV7bFJ4oGWwmo9
@leggetter
leggetter added this pull request to stack #380 September 10, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant