Skip to content

Add a tiger feedback command - #226

Draft
aprimakina wants to merge 3 commits into
mainfrom
feedback-command
Draft

Add a tiger feedback command#226
aprimakina wants to merge 3 commits into
mainfrom
feedback-command

Conversation

@aprimakina

@aprimakina aprimakina commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Adds tiger feedback, which relays feedback, a bug report, or a support
request to the Tiger Data team through the gateway's new /feedback endpoint.
Modeled on ghost's feedback command.

tiger feedback "I can't connect to my service after resuming it"
echo "Great tool!" | tiger feedback
tiger feedback   # reads from stdin, prompting on a terminal

The submitter's email is resolved from the caller's credentials and the client
version and OS from the User-Agent, so neither is sent in the body. The
command opens no support case and returns no ticket.

Blocked on two upstream changes

timescale/savannah-gateway#1982 adds the endpoint and the Slack relay, and
must merge and deploy before this is usable.

/feedback must also lose x-tigerdata-preview: true upstream. The command
is registered unconditionally, and CLAUDE.md only permits that for a
non-preview endpoint — a surface built on a preview operation has to stay
gated. Gating it was the alternative, but a feedback command nobody can
discover collects no feedback, and the endpoint's contract is small enough
(message, optional source, 204) that preview status looks like a default
rather than a judgment. That call belongs to the endpoint's owner
(x-tigerdata-owner: prodexp).

Warning

The openapi.yaml in this branch already has the marker removed, by hand.
That file is a verbatim copy of the upstream spec and is never hand-edited
here, so the edit is a stand-in: the marker has to actually come off in the
gateway spec and openapi.yaml be re-synced from it before merge, or the next
sync silently reinstates it and leaves an ungated command on a preview
endpoint. Removing the marker changes no generated code (go generate ./...
produces no diff), so the real sync will be a spec-only change.

Three commits, reviewable separately

Sync openapi.yaml — re-syncs the spec and regenerates the client, types,
and mocks. Almost all of it is generated bulk that a reviewer can skip, which
is why it's split out. It picks up everything that landed upstream since the
last sync on 2026-08-27: the /feedback endpoint, plus enableDataTiering,
the backup-regions and backup-retention operations, getServiceMetricDetails,
the exporters operations and their schemas, and x-tigerdata-owner
annotations on nearly every existing operation (which is why the diff touches
so many paths without changing them).

The one part that needs reading: the service metrics series aggregation enum
moved out of the request body into a shared MetricsAggFn schema and gained
MIN_TOTAL/MAX_TOTAL. That renames the generated constants and updates three
hand-maintained mirrors of the enum — the CLI's --fn help and completion, and
the MCP tool's JSON-Schema enum. The MCP one matters: schema enums are enforced
before the handler runs, so without it an assistant asking for MAX_TOTAL
would be rejected outright.

Add a `tiger feedback` command — the command, its tests, the analytics
redaction, and the README/CLAUDE.md updates.

Add a `feedback` MCP tool — pairs the command with an MCP tool, so an
assistant can relay a user's feedback the same way. Ported from ghost's
ghost_feedback, named feedback here since Tiger MCP doesn't prefix its tool
names. Registered through mcp.AddTool rather than the gating addTool, and
left out of readOnlyGatedTools: submitting feedback mutates no service, so
the tool stays available in every read-only mode.

Review notes

  • The message never reaches analytics. Positional arguments are otherwise
    tracked verbatim, and a feedback message is free text that may quote a
    connection string or a query. A new annotationRedactArgs annotation tells
    wrapCommands to replace a command's argument values with [REDACTED]
    before tracking. Values are replaced rather than the property dropped, so the
    argument count survives and a message passed as an argument stays
    distinguishable from one piped in. "message" also joins the analytics
    ignore list, so a future feedback MCP tool's parameter is filtered the same
    way. Both mirror ghost, which does the same via a command-path switch in its
    analytics package; the annotation keeps the decision on the command itself
    instead of in a central list that drifts.
  • The MCP tool logs nothing, unlike every other handler in the package,
    which all log a "MCP: …" line. The only argument feedback has is the user's
    own free text, so there is nothing safe worth logging; a comment in the
    handler says so, since the absence would otherwise read as an omission.
  • The MCP input schema carries minLength/maxLength from the endpoint's
    own bounds, so an over-long message is rejected before the handler instead of
    round-tripping to a 400. The CLI has no equivalent guard yet — worth adding
    if you think it's warranted there too.
  • No MCP-side test. Only db_query of the now-15 MCP tools has one, so
    this follows the existing pattern; the mcp list expectations in
    internal/cmd/mcp_list_test.go do cover registration in each read-only
    mode.
  • The stdin hint is gated on util.IsTerminal(cmd.InOrStdin()) only, not both
    streams, following the existing readQuery precedent in db_query.go — the
    read's purpose is piped input, so only the hint is gated.

Testing

go build, go vet, go test ./..., go tool staticcheck ./... clean, and
go generate ./... produces no diff. Both commits pass independently, so the
branch bisects.

11 command test cases cover the auth error, argument and blank-message
validation, network/API/server errors, both input paths, the terminal prompt,
and the analytics redaction. The redaction cases were mutation-checked:
removing the annotation fails them with the connection string that would have
leaked.

Not run: govulncheck (no dependency changes).

🤖 Generated with Claude Code

aprimakina and others added 2 commits September 10, 2026 13:56
Re-sync openapi.yaml from the Tiger Cloud API spec and regenerate the
client, types, and mocks. Nothing here is hand-edited except the three
MetricsAggFn call sites described below.

The sync picks up everything that landed upstream since the last one:

- New endpoints: submitFeedback, enableDataTiering, the backup-regions
  and backup-retention operations, getServiceMetricDetails, and the
  exporters operations.
- New component schemas for the exporter families (CloudWatch, Datadog,
  Prometheus, Azure Monitor) and their AWS credential types, along with
  BackupRegion, BackupRetentionByTime, MetricDetails, and DataTiering.
- x-tigerdata-owner annotations on nearly every existing operation,
  which is why the diff touches so many paths without changing them.

The aggregation enum on `service metrics series` moved out of the
request body into a shared MetricsAggFn schema, renaming the generated
constants from MetricsSeriesRequestFn* to MetricsAggFn*. Update the
three call sites accordingly; --fn now also accepts MAXTOTAL and
MINTOTAL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Relay feedback, a bug report, or a support request to the Tiger Data
team through the gateway's /feedback endpoint, which posts it to Slack.
The message comes from an argument or, when none is given, from stdin —
piped or typed interactively, with the hint TTY-gated so piped input
stays clean. The submitter's email is resolved from the caller's
credentials and the version and OS from the User-Agent, so neither is
sent in the body.

The command is registered unconditionally and listed in the README:
/feedback is not a preview operation, so nothing gates it.

A feedback message is free text that may quote connection strings,
queries, or anything else, and positional arguments are otherwise
tracked verbatim. Add an annotation that tells wrapCommands to replace a
command's argument values with [REDACTED] before tracking, and set it
here. Redacting the values rather than dropping the property keeps the
argument count, so a message passed as an argument stays
distinguishable from one piped in. Flags are still tracked, subject to
the analytics ignore list, which gains "message" so a future feedback
MCP tool's parameter is filtered the same way.

This mirrors ghost, which redacts the message from `ghost feedback` and
carries "message" in its own ignore list.

Gateway side: timescale/savannah-gateway#1982

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pair `tiger feedback` with an MCP tool, so an assistant can relay a
user's feedback the same way. Ported from ghost's `ghost_feedback`, named
`feedback` here since Tiger MCP doesn't prefix its tool names.

The input schema carries the endpoint's own bounds (minLength 1,
maxLength 3000), so an over-long message is rejected before the handler
instead of round-tripping to a 400. The handler trims like the CLI, so
both surfaces send the same message for the same text, and logs nothing —
the only argument worth logging is the user's own free text.

Registered through mcp.AddTool rather than addTool, and left out of
readOnlyGatedTools: submitting feedback mutates no service, so the tool
stays available in every read-only mode. Its `message` parameter is
already filtered from analytics by the ignore list.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@aprimakina aprimakina self-assigned this Sep 10, 2026
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