Skip to content

chore(cli): define error actionability taxonomy#5829

Open
jgoux wants to merge 6 commits into
developfrom
julien/cli-1560-error-actionability-colocated
Open

chore(cli): define error actionability taxonomy#5829
jgoux wants to merge 6 commits into
developfrom
julien/cli-1560-error-actionability-colocated

Conversation

@jgoux

@jgoux jgoux commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Defines the CLI error actionability taxonomy for KPI reporting (CLI-1560). Replaces #5775 with an architecture where classification lives on the error types themselves instead of a centralized tag registry.

What

  • Taxonomy (apps/cli/src/shared/telemetry/error-actionability.ts): error_kind, error_category, suggestion_type enums, the sanitized CliErrorActionability contract, and the Q2 metric definitions (strict recovery, repeat error, internal/unknown bug rate).
  • Co-located declarations: every error class in apps/cli (391 classes) declares its own classification via a getter under the ErrorActionabilityId symbol, next to the message/suggestion it already carries. Instance-dependent cases branch on typed fields (statusCodeActionability(this.status, { upgradeSuggested })), never on message text.
  • Exhaustiveness tests: a coverage test enumerates every TaggedError in apps/cli/src and fails if one lacks a declaration (or isn't exported for verification), and checks that every error tag in packages/{stack,config,process-compose} has an external adapter. A production unknown classification now means "genuinely unforeseen failure", not registry drift.
  • Structured discriminants instead of string sniffing: StackBuildError.reason (invalid_config / asset_preparation), DockerPullError.daemonDown and LegacyDockerRunError.reason/daemonDown (detected at the docker boundary where the output is produced), and a typed upgradeSuggested prop threaded from legacySuggestUpgrade into the plan-gated sso/branches/vanity-subdomains errors — no Object.defineProperty side-channel, no sniffing user-facing English for "upgrade"/"billing"/"quota".
  • External adapter: a small structural adapter classifies the 27 error tags from workspace packages plus effect cli/http errors, kept exhaustive by test.

Why the different architecture vs #5775

The centralized actionabilityByTag map re-stated, far from each definition, facts each error already knows about itself. It covered 229 of 407 tags after 169 review-comment rounds, and any new error silently fell through to unknown — inflating the exact KPI numerator this project is meant to measure. With co-location the classification is reviewed in the same diff that introduces an error, and the coverage test turns "we forgot to classify" into a CI failure instead of a data-quality bug.

Scope notes

  • No telemetry capture changes: cli_command_executed still emits only existing fields. Wiring classification into failure events (and command-name attribution for pre-handler failures) is CLI-1561.
  • No Go changes: the Go CLI is being removed; the KPI targets the TypeScript CLI.
  • Follow-up opportunity for CLI-1561: derive has_suggestion/suggestion_type from the code path that renders suggestions, so telemetry can never disagree with what the user saw.

🤖 Generated with Claude Code

…rations

Defines the CLI error actionability taxonomy for KPI reporting (CLI-1560):
error kinds, categories, suggestion types, and the Q2 strict-recovery /
repeat-error / internal-unknown-rate metric definitions.

Classification is declared where each error is defined: every error class in
apps/cli exposes a CliErrorActionabilityDeclaration under the
ErrorActionabilityId symbol, enforced by an exhaustiveness test that fails
when any error class lacks a declaration. Errors from workspace packages
(stack, config, process-compose) and effect cli/http are classified by a
small structural adapter, exhaustiveness-checked against those packages'
sources.

Broad wrapper errors gain structured discriminants instead of message
sniffing: StackBuildError.reason, DockerPullError.daemonDown,
LegacyDockerRunError.reason/daemonDown, and a typed upgradeSuggested prop
threaded from legacySuggestUpgrade through the plan-gated sso, branches, and
vanity-subdomains errors.

No telemetry capture changes: cli_command_executed still emits only existing
fields until CLI-1561 wires classification into failure events.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jgoux jgoux requested a review from a team as a code owner July 8, 2026 12:22
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase/cli/supabase@c9af260751871ee82f3854e0e53894931ddad892

Preview package for commit c9af260.

@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: afd73d0c8f

ℹ️ 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 apps/cli/src/shared/telemetry/error-actionability-coverage.unit.test.ts Outdated
Comment thread apps/cli/src/shared/telemetry/error-actionability.ts Outdated
Comment thread packages/stack/src/StackPreparation.ts Outdated
Comment thread apps/cli/src/shared/telemetry/error-actionability.ts Outdated
Coverage: the drift guard now also enforces declarations on plain
`extends Error` classes, and the external adapters cover the full effect
cli parser error set (MissingArgument, DuplicateOption, InvalidValue),
UserError unwrapping, and PlatformError/BadArgument with reason-based
classification.

Classification fixes from a construction-site audit of all ~420 classes:
local filesystem failures mislabeled as config/input errors now classify
as permission; CLI-internal declarative script failures move to
internal_bug; transport failures conflated with user errors in
LegacyDbQueryExecError, LegacyDeclarativeApplyError, and
LegacyDomainsCnameError are split via typed discriminants; paused
projects get the new open_dashboard suggestion type; and
LegacyDbConfigIpv6Error's suggestion now matches the link command the
CLI actually prints.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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: 68442e6671

ℹ️ 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 apps/cli/src/shared/functions/delete.errors.ts Outdated
Comment thread apps/cli/src/legacy/commands/db/query/query.errors.ts Outdated
Comment thread apps/cli/src/legacy/commands/link/link.errors.ts Outdated
Comment thread apps/cli/src/legacy/commands/link/link.errors.ts Outdated
- Coverage scan now recognizes any local `*Error("Tag")` factory (e.g. the
  next login shell's LoginError), closing the drift-guard gap for NoTtyError
  and LoginFailedError, which now declare auth classifications.
- classifyCliErrorActionability recurses into a StackError's preserved
  tagged cause before falling back, and uses own-property adapter lookups so
  sanitized tags like "constructor" cannot hit Object.prototype.
- DockerPullError.daemonDown now also covers docker spawn failures (binary
  missing), not just daemon-down output.
- DeleteFunctionUnexpectedStatusError, LegacyDbQueryUnexpectedStatusError,
  and LegacyLinkAuthTokenError thread the response status and classify via
  statusCodeActionability, so 401s land in auth and 5xx in api_status.
- Paused projects get a dedicated project_paused category instead of
  polluting plan_limit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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: 11232cb803

ℹ️ 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 apps/cli/src/legacy/shared/legacy-storage-credentials.errors.ts
Comment thread packages/stack/src/StackLifecycleCoordinator.ts
Comment thread apps/cli/src/shared/telemetry/error-actionability.ts
Comment thread apps/cli/src/shared/telemetry/error-actionability.ts
jgoux and others added 3 commits July 8, 2026 17:40
…ures

- classifyCliErrorActionability recurses into the classifiable preserved
  cause of stack wrapper errors (asset-preparation StackBuildError,
  DownloadError, StackError), so a daemon-down DockerPullError or a local
  filesystem PlatformError is no longer reported as a registry/network
  failure. Recursion is guarded: an unclassifiable cause keeps the
  wrapper's own bucket.
- statusCodeActionability and the HttpClientError adapter classify 403 as
  user-actionable account permission (after the entitlement-gate branch, so
  confirmed plan gates stay plan_limit).
- packages/api joins the adapter coverage scan; SupabaseApiConfigError
  classifies as auth/set_env_var.
- LegacyStorageAuthTokenError branches on its stored status like its link
  twin, so api-keys 5xx responses stop counting as user login problems.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The coverage guard correctly failed on the develop merge: the stop/status
port and config-validation refactor introduced new error classes. Each now
declares its actionability from its construction sites (workdir flags →
provide_flags, config.toml content → invalid_config, docker CLI wrapper
failures → docker_not_running, missing/unhealthy containers → start-stack
remediation), LegacyContainerRuntimeNotFoundError is exported for guard
verification, and packages/config's InvalidRemoteProjectIdError joins the
external adapters as invalid_config.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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