Skip to content

let the server decide which features a call may use - #71

Open
alxnddr wants to merge 6 commits into
mainfrom
fix/server-authoritative-gates
Open

alxnddr wants to merge 6 commits into
mainfrom
fix/server-authoritative-gates

Conversation

@alxnddr

@alxnddr alxnddr commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

A proper fix for https://linear.app/metabase/issue/GHY-4739/rde-the-first-git-sync-command-after-activating-the-license-fails

Description

The client refused a gated call when the profile's cached probe lacked the feature. After an upgrade or a license activation that cache is stale, so the CLI refused calls the server allows. Now every call goes to the server, and explainRefusal turns a 402, an unrouted 404, or a 400 into a CapabilityError that names the missing feature. The client still refuses first when an older server would drop a parameter without an error, such as merge on a git-sync import before v63.

- a 400 is a feature refusal only when its field errors name nothing
  outside what the missing feature gates; parameter features declare the
  request fields they cover, and the CapabilityError carries the server's
  answer as its cause
- explainer offers explain and explainWalk, so a paged walk's refusal is
  explained without threading features through paginatePages
- explanation runs under the failed request's own budget; a timeout leaves
  the refusal standing rather than reading as an interrupt
- simplify the transport's probe state to the profile in force and the
  newest probe; onServerProbed fires only for the newest answer
- requireFeatures always confirms against a verified profile
- CLI: probe on the way out only for shape errors and unrouted 404s; decide
  the refreshed-profile note by the tag a shape was chosen for; save every
  probe, once per distinct answer per run
- git-sync: refuse syncedCollections/branch without remoteSync; restore
  pre-v63 import/export without a readable branch; read named features
- table update reports a dropped collection_id as PartialWriteError (exit 1)
- git-sync: name the tracked branch only to a server with the branch guard;
  an older server takes an export's branch as a switch
- probe cache: save only when the server changed; serialize profiles-file
  writes within a process so a probe save never restores a rotated token
- shape error after a write no longer tells the user to retry
- explain a refusal under the probe's own bound; drop the request-budget
  side table
- move refusal classification into version/refusal.ts; rename
  preflight-error to capability-error
- keep --skip-preflight / MB_CLI_SKIP_PREFLIGHT as a warning no-op
- fix README, architecture, CLAUDE.md and skill drift
- git-sync: the tracked branch is sent only to a guarding server
- runtime: a probe that agrees with the record leaves it untouched
- transport: drop the cases for the removed per-call explanation budget
- oauth: an unadvertised full-access scope is a v64 server's, not v63's
@alxnddr alxnddr changed the title rely on server state to guard features based on versions or feature flags let the server decide which features a call may use Oct 7, 2026
@alxnddr
alxnddr requested a review from ranquild October 7, 2026 00:04
…p-preflight

- explainer gains refuse / refuseAfterReading: a whole-method refusal before
  the wire is declared where the method is exported, from METHOD_REQUIREMENTS;
  explain-guard requires exactly one wrapper per gated method
- explainRefusal moves into version/refusal.ts; the fake client explains for real
- the shared probe parses SessionProperties only and each caller applies its own
  reader, naming the version that response reported; a good answer is no longer
  discarded when a later probe fails; probe() runs on the probe budget
- pivoted exports read their setting through transport.probe
- git-sync: branch reads cost one request and judge their own read; status
  reads the session properties once with a fixed error precedence; an import
  sends its branch assertion to every server with remote sync
- CLI waits for in-flight probes before exit and says when a refreshed profile
  could not be saved
- --skip-preflight / MB_CLI_SKIP_PREFLIGHT skip only the rule-based checks made
  before sending, through one decision point in the transport; each skipped
  check warns once, an unreachable check lets the call go on, and a refusal
  made before sending names the flag
- search declares verified so a server's 402 is explained
- HTTP status constants live in http/errors.ts; HttpError.serverMessage;
  PartialWriteDetail distinguishes an unreported collection
- docs and skills describe the end state; the e2e version lane expects the
  write remedy for a POST
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