Accept a service name anywhere a service ID is accepted - #241
Merged
Merged
Conversation
aprimakina
force-pushed
the
alexandra/service-name-refs
branch
from
September 22, 2026 15:07
6f2b9d6 to
380538a
Compare
aprimakina
marked this pull request as ready for review
September 23, 2026 09:11
nathanjcochran
self-requested a review
September 25, 2026 20:37
nathanjcochran
approved these changes
Sep 25, 2026
nathanjcochran
left a comment
Member
There was a problem hiding this comment.
Left some comments, but overall this looks great! Thank you for doing this. I think it's going to be a HUGE improvement for UX! 💯
aprimakina
force-pushed
the
alexandra/service-name-refs
branch
from
September 29, 2026 19:43
4136790 to
a243961
Compare
aprimakina
force-pushed
the
alexandra/service-name-refs
branch
2 times, most recently
from
September 30, 2026 08:15
20a21b9 to
f5fb479
Compare
Every command that identifies a service now takes a ref: its ID, a read replica set ID, or its name. The new `resolveServiceRef` API operation matches ID and name together and refuses a ref matching more than one service, so the CLI classifies nothing and never has to tell them apart. - `getServiceRef` picks the ref out of `args[0]` or `cfg.ServiceID`, so the `--service-id` flag, `TIGER_SERVICE_ID` and the config file all arrive through one path. The positional is `[name-or-id]` in every usage line. - `resolveService`, `resolveServiceID` and `resolveServiceForWrite` (`service_ref_helper.go`) hand it to the API. Resolving returns the whole service, so the commands that already fetched one pay nothing for it. - `resolveServiceForWrite` carries the read-only gate for the commands that change the service they resolve: it refuses the blanket case before the network call, then gates on the tag the resolution returns, so neither half can be skipped or ordered wrong at a call site. The refusal no longer prefixes the service, which only ever echoed the argument back; `CheckReadOnlyByServiceID` still names it when the tag lookup fails. - A configured default must be an ID. A name there breaks silently on the next rename, so it is refused after the lookup with the ID to store instead. `config set service_id` resolves at write time and stores the ID, echoing the resolution when a name was given. - Destructive commands accept a name, but their confirmation prompt shows both forms and still takes only the ID. - Status output names a service as 'name' (id) through one `serviceLabel` helper, so someone who typed a name sees which service it hit. - "failed to <verb> Service" is lowercased, and "service is required" / "service cannot be empty" both become "service name or ID is required". - MCP tools stay IDs-only, an intentional divergence documented at `setServiceIDSchemaProperties`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Drop name resolution from `config set service_id`: it stores the value as given again, like every other key. A name there is still refused when a command reads the default, and the error no longer suggests `config set` as the way to turn a name into an ID. - Drop `resolveServiceID`; callers use `resolveService` and read `.ServiceID`. - Drop the `serviceID := service.ServiceID` aliases. - Split the variadic `expectResolveRef` into `expectResolveRef(m, ref, svc)` and `expectResolveRefID(m, ref)`. - Test the ambiguous-name refusal in every command that resolves a ref. - Correct the MCP comment: only the resolve operation takes a name, and the tools never call it. - Trim the service-ref sections of CLAUDE.md and README.md to the rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The gateway replaced the resolve operation (POST .../services/resolve) with an internal ref query parameter on getServices. Sync openapi.yaml from it and regenerate. - resolveService calls GetServicesWithResponse with the ref. A match is a one-item list; no match is an empty list rather than a 404, so the CLI now writes "service '<ref>' not found" itself, still exiting with ExitServiceNotFound. More than one item means the filter was ignored, and is refused rather than acted on. - The existing list callers pass nil params. - The resolve test helpers expect the list call with the ref, and expectResolveRefNotFound covers the empty list; the cases that used a 404 for not-found now use it. - CLAUDE.md and the MCP comment describe the ref filter. - The integration test for a deleted service expects the CLI's own not-found message. - The name-as-default error states the rule and the fix without the rationale, which stays in the serviceRef comment; two comments that repeated it are trimmed. - service get's cases follow the command's execution order. The sync also brings the gateway's deprecation of the 'ai' add-on and the removal of the legacy database metrics series, both description-only here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`service backup region add`, `list` and `remove` arrived on main taking a service ID only. They now take a ref like every other command: - `add` and `list` accept a name as an argument, fall back to the default service, and resolve through resolveServiceForWrite and resolveService respectively. - `remove` follows `service delete`: the service must be given explicitly, a name is accepted, and the confirmation prompt shows both forms but takes only the ID. Read-only mode refuses before the prompt. - Status output names the service by both forms. Their tests follow the other command tables, including the ambiguous-name case and delete's prompt cases for `remove`. The MCP region tools stay IDs-only. Also: `service delete` shows `<name-or-id>`, as a required argument should, and the `service backup list` ambiguous case runs the renamed command. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aprimakina
force-pushed
the
alexandra/service-name-refs
branch
from
September 30, 2026 08:35
f5fb479 to
626fdbb
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every command that identifies a service now takes a ref — its ID, a read replica set ID, or its name:
The CLI classifies nothing. The
resolveServiceRefAPI operation matches ID and name together and refuses a ref matching more than one service, so nothing here ever tries to tell an ID from a name by its shape.How it works
getServiceRefpicks the ref out ofargs[0]orcfg.ServiceID, recording which it was. The--service-idflag,TIGER_SERVICE_IDand the config file share that second path.resolveService,resolveServiceIDandresolveServiceForWrite(newinternal/cmd/service_ref_helper.go) hand it to the API. Resolving returns the whole service, so the nine commands that already fetched one pay nothing for resolution.resolveServiceForWritealso carries the read-only gate for the seven commands that change the service they resolve: it refuses the blanket case before the network call, then gates on the tag the resolution returns, so neither half can be skipped or ordered wrong at a call site.Deliberate choices
A name is accepted as an argument, but not as a stored default. An argument's result is visible immediately;
--service-id,TIGER_SERVICE_IDand the config file are set once and forgotten, so a name there works until someone renames the service and then breaks every command at once. An ID never changes.Status output names the service, not just its ID. Typing a name and getting an opaque string back is no confirmation you hit the right service, so every status line carries both, through one
serviceLabelhelper:Error wording was made consistent while the output was being touched: five
failed to … Servicecapitals lowercased,service is requiredandservice cannot be emptyunified asservice name or ID is required, and service identifiers single-quoted in errors (with%qkept for opaque values).Destructive commands accept a name; their confirmation prompt does not.
service deleteshows both forms —'my-api-db' (kd9w2xp4mz)— and requires the ID to be typed back.Completion keeps offering IDs, with the name as the description.
MCP tools stay IDs-only, an intentional CLI/MCP divergence documented at
setServiceIDSchemaProperties.The positional is
[name-or-id]rather than[service], so the usage line itself shows both accepted forms.