Skip to content

feat: errors that teach — every failure names its own fix (COR-13588) - #9

Open
Bradenream wants to merge 4 commits into
braden/fix-local-build-gosum/COR-0from
braden/errors-that-teach/COR-0
Open

feat: errors that teach — every failure names its own fix (COR-13588)#9
Bradenream wants to merge 4 commits into
braden/fix-local-build-gosum/COR-0from
braden/errors-that-teach/COR-0

Conversation

@Bradenream

@Bradenream Bradenream commented Aug 26, 2026

Copy link
Copy Markdown

Part of COR-13588 — shipping the vf CLI as an agent-era distribution channel.

Why

For both humans and AI coding agents, the error text is the documentation they actually read. An agent that hits a dead-end error picks a competitor; one that reads the fix in the error keeps going.

What

Three request-lifecycle behaviors, all in the sanctioned non-generated hooks extension point (internal/sdk/sdkinternal/hooks/) — zero persisted edits to generated code:

  • No token configured → fail before the network call with the exact fix (Settings → Access tokens, export VF_TOKEN=vfp_...) and a docs_url, in both the agent-mode envelope and pretty output. --dry-run stays usable without credentials.
  • Malformed token → warn once on stderr for legacy VF.DM. Dialog Manager keys and whitespace-wrapped tokens, without blocking the request.
  • 4XX responses → inject actionable hints (401 expiry/renewal path, 403 acts-as-you semantics, 404 with a verified vf <group> list command, 429 Retry-After, 400/422 --dry-run guidance) and a docs_url derived from the operation's verified docs command-group page. Keys already in the body — including future server-provided hints — are never overwritten.

Base branch: #7.

Verification

  • 7/7 hermetic vitest tests (test/errors-teach.test.ts) against a local mock API: preflight fires with zero network (unroutable server), dry-run guard, both warnings, 404/429/401 hint injection
  • go vet clean; manual matrix in agent + pretty modes

For both humans and AI coding agents, the error text is the
documentation they actually read. Three request-lifecycle hooks (all in
the non-generated hooks extension point):

- No token configured: fail before the network call with the exact fix
  (Settings → Access tokens, export VF_TOKEN=vfp_...) and a docs_url.
  --dry-run stays usable without credentials.
- Malformed token: warn once on stderr for legacy VF.DM. Dialog Manager
  keys and whitespace-wrapped tokens, without blocking the request.
- 4XX responses: inject actionable hints (401 token-expiry renewal path,
  403 acts-as-you semantics, 404 verified 'vf <group> list' commands,
  429 Retry-After, 400/422 --dry-run guidance) and a docs_url derived
  from the operation's verified docs command-group page. Keys already in
  the body — including future server-provided hints — are never
  overwritten.

Covered by hermetic tests against a local mock API.
Copilot AI lite review requested due to automatic review settings August 26, 2026 01:17

Copilot AI 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.

Pull request overview

Adds a non-generated SDK hook (“errors that teach”) to improve CLI error ergonomics by failing early when no token is configured and enriching 4XX responses with actionable hints and docs_url, plus a hermetic vitest suite covering the new behaviors.

Changes:

  • Introduces teachHook to preflight missing credentials, warn on recognizable malformed tokens, and non-destructively inject hints/docs_url into JSON error bodies.
  • Registers the new hook in the SDK hook lifecycle (before-request + after-error).
  • Adds hermetic vitest coverage for no-token preflight, malformed-token warnings, and representative 4XX hint injection behaviors.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
test/errors-teach.test.ts Adds hermetic vitest coverage for preflight/warnings and 4XX enrichment behaviors.
internal/sdk/sdkinternal/hooks/teach.go Implements the new before-request and after-error “teaching” behavior and docs/hints mapping.
internal/sdk/sdkinternal/hooks/registration.go Registers teachHook for request lifecycle events.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/sdk/sdkinternal/hooks/teach.go
Comment thread internal/sdk/sdkinternal/hooks/teach.go
Comment thread internal/sdk/sdkinternal/hooks/teach.go
BuildRequest already validates required flags, but its nullable-body
relaxation also fired for params-only commands (bodyFieldPath and
bodyFlagName both empty — get/delete with only path or query params),
stripping Required from every field. Requests left with empty required
params and failed server-side with field paths ('resources[0].id') no
CLI user or coding agent can map back to a flag.

Restrict the relaxation to commands that actually register a body flag.
'vf project get' now fails locally, before any network call, with
'missing required flag: --project-id' — the same message shape cobra
produces for enforced query params. Whole-body input (--body/stdin)
still satisfies required body fields exactly as before.
@Bradenream

Copy link
Copy Markdown
Author

Added: required-param preflight (the P2 gap). Root cause turned out to be in BuildRequest itself — its nullable-body relaxation also fired for params-only commands, stripping Required from path/query params. One-condition fix (b7602c9): relaxation now only applies to commands that register a body flag. 'vf project get' fails locally with 'missing required flag: --project-id' before any network call; --body/stdin behavior unchanged. 9/9 tests. This one is upstreamable to Speakeasy verbatim.

- Agent-mode detection: Claude Code actually sets CLAUDECODE (no
  underscore) — verified in a live session. Without it in the list the
  flagship coding agent never triggered agent mode at all.
- dryRunRequested now accepts every bool spelling cobra does
  (--dry-run=True/T/1/...), instead of string-matching two of them.
- Piped stdin no longer bypasses required-param enforcement on
  params-only commands: 'echo {} | vf project get' used to re-relax
  required path params and send 'GET /project/' with an empty segment.
- 429 hint echoes Retry-After verbatim (it may be an HTTP-date, not
  delta-seconds).
@Bradenream Bradenream changed the title feat: errors that teach — every failure names its own fix feat: errors that teach — every failure names its own fix (COR-13588) Aug 26, 2026
@linear-code

linear-code Bot commented Aug 26, 2026

Copy link
Copy Markdown

COR-13588

- io.ReadAll can return bytes alongside an error; restore whatever
  arrived instead of blanking the body, so a truncated error message
  still reaches the user.
- The 403 hint gated on the operation's own command group but always
  emitted 'vf workspace list'. Always emit it: workspace list is the
  command that reveals reachable scope, and it exists for every account.
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.

2 participants