Skip to main content

review

Strict end-of-task review. Use before marking any non-trivial task done. Returns axis-by-axis pass/fail; any FAIL blocks completion. Trigger on user /review or end-of-task self-check.

Informations de source

Dépôt
metabase/metabase-cli
Dernière activité de la source
7 octobre 2026 à 17:29
Langue détectée de SKILL.md
anglais
Étoiles
16
Forks
6

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
review
description
Strict end-of-task review. Use before marking any non-trivial task done. Returns axis-by-axis pass/fail; any FAIL blocks completion. Trigger on user /review or end-of-task self-check.
# review Non-deterministic architectural check that complements the deterministic gate (`bun run check` — typecheck, lint, format, unit tests, skill lint). Determinism catches typos; this catches drift. ## When to invoke - After finishing any non-trivial change, before claiming "done." - On user `/review`. ## Inputs 1. Diff scope: - `git diff` (uncommitted) AND - `git diff main...HEAD` if on a feature branch. - If outside a git repo, review uncommitted changes only. 2. `CLAUDE.md`. Read both before forming any judgment. All other rules required for this review are embedded in this skill below — do not depend on any external doc. ## Project layering (memorize) A bun workspace with two packages. `packages/client` (`@metabase/client`) is the private Metabase API client, built by `tsdown` to a `dist/` its `exports` map names; `packages/cli` (`@metabase/cli`) is the publishable CLI built on it, and inlines the client at build time. The e2e tier lives at the repo root under `tests/e2e/` and drives the built binary; unit tests sit beside their source under `packages/<p>/src/**/*.test.ts`. ``` packages/client/src/ index.ts public barrel: client factory, error taxonomy, Credential, parseJson client.ts createClient — composes the resource namespaces onto one transport resources/ one file per Metabase resource; every /api/ path and transport call http/ the HTTP boundary — transport.ts, errors.ts, network-error.ts, response-shape.ts, sanitize.ts, retry.ts, oauth.ts testing/ the shipped test doubles — fake-client.ts, fetch-capture.ts auth/ OAuth login flow: credential.ts, pkce.ts, callback-server.ts, oauth-*.ts version/ tag.ts, probe.ts, known-range.ts (KNOWN_RANGE), profile.ts (ServerProfile), features.ts (FEATURE_RULES — the only file comparing a Metabase major), requirements.ts (METHOD_REQUIREMENTS), requirement-check.ts (checkFeatures), capability-error.ts, refusal.ts (explainer → explain / explainWalk / refuse / refuseAfterReading, explainRefusal) domain/ Metabase API resource Zod schemas + inferred types errors.ts MetabaseError taxonomy, toMetabaseError, isFileNotFoundError, errorMessage, isNonInterruptFailure list.ts ListResult<T> = { data, total } — what a one-response list method returns url.ts normalizeUrl, displayUrl, assertEndpointOrigin — the only home for new URL() json.ts parseJson — the only home for JSON.parse poll.ts pollUntil — the wait-loop home (http/retry.ts drives its own backoff wait) paginate.ts, signal.ts, predicates.ts, json-pointer.ts packages/cli/src/ cli.ts entrypoint; only file allowed to call process.exit main.ts root citty command, lazy subCommands commands/ CLI shell — defineMetabaseCommand, flags, prompts, orchestration core/ pure logic, no CLI deps core/auth/ credential storage + verification core/config.ts flag → env → stored resolver core/env.ts readEnv — every MB_* env name, no raw process.env reads elsewhere core/user-agent.ts USER_AGENT, the CLI's wire identity core/npm-registry.ts the upgrade check's dist-tags lookup — the CLI's only fetch() core/system-ca.ts trustSystemCa — the only node:tls in the repo, a CLI prerogative output/ presentation; takes typed values; renders text or JSON runtime/ platform glue: stdin, csv, yaml, citty helpers ``` Allowed import direction — the package boundary first, then within each package: - **Nothing in the client may import from the CLI** — not by an `@metabase/cli` specifier, not by a relative path that escapes the client's own src tree. The reverse is expected: the CLI consumes the client as `@metabase/client/<path>`. - The client's dependency budget is `zod` + `node:` builtins. Root devDependencies hoist, so the resolver will not reject a third dependency — a production client file importing anything else is FAIL even though it type-checks. - `domain/*` imports only `zod`, sibling `domain/*` files and — for a reader that selects a wire schema by generation (`<resource><Endpoint>Schema(features)`) — `import type { Features } from "../version/features"`. A value import from `version/`, or anything else from elsewhere in either package, is FAIL. - `resources/*` is imported by `client.ts` and by sibling `resources/*` files, and by nothing else. A `domain/`, `http/`, `auth/` or `version/` file importing a resource is FAIL — the direction runs one way, and the permitted importers are an allowlist, so a directory added later is not exempt for having arrived after the rule. - `commands/*` may import `core/*`, `output/*`, `runtime/*`, and any `@metabase/client/*` path. - `core/*` may import `runtime/*` and `@metabase/client/*`. Never `commands/`, never `output/`. - `output/*` may import `runtime/*`, `core/*` except `core/auth/`, and `@metabase/client/*` **except `@metabase/client/http/`** — presentation consumes the error taxonomy from `@metabase/client/errors`, not the HTTP boundary. Never `commands/`. - `runtime/*` may import `@metabase/client/*` (typed errors are the project's pattern: `runtime/body.ts` uses `parseJson` and `ConfigError`, `runtime/yaml.ts` uses `ValidationError`). It must not import from `commands/`, `core/`, or `output/`. ## Domain-file contract Every Metabase API resource in `packages/client/src/domain/<resource-singular>.ts` exports exactly two things per resource — the client's agent-facing contract: 1. **`<Resource>`** — `z.object({ ... }).loose()` for the full API shape, with the type inferred via `z.infer`. `.loose()` is the default so Metabase API additions don't break the parse — tighten over time, don't preempt. (Zod 4 — `.passthrough()` is deprecated; use `.loose()`.) 2. **`<Resource>Compact`** — `<Resource>.pick({ ... }).strip()` projection with its inferred type. The trailing `.strip()` is **mandatory**: `.pick()` on a `.loose()` parent inherits the loose catchall and silently passes every API field through at parse time, which means the "compact" projection isn't actually compact. A diff that lands a Compact missing `.strip()` is FAIL — quote `<Resource>.pick({...})` without trailing `.strip()`. Presentation is CLI-owned, never the client's: the `<resource>View` (`ResourceView<T>` with `compactPick: <Resource>Compact` and `tableColumns: ColumnDef<T>[]`) lives in `packages/cli/src/output/views/<r>.ts`, with any `format:` cell helper colocated there. A `ResourceView`, a `ColumnDef`, a `tableColumns` list, or a terminal-presentation `format:` helper appearing in `packages/client/src/domain/` is FAIL. Inlining a column list inside a command is FAIL. Naming: `<Resource>` is PascalCase (`Card`); the CLI-side `<resource>View` is camelCase (`cardView`). `<Resource>` is the _resource name_, not the file name — a file may host multiple resources (e.g. `domain/user.ts` → `CurrentUser` + `CurrentUserCompact`); the pair contract holds per-resource. Adding a resource is additive: a new `packages/client/src/domain/<r>.ts` for the pair, `packages/client/src/resources/<r>.ts` for the methods, and `packages/cli/src/output/views/<r>.ts` for the view — consumed by commands as `client.<r>.<method>(…)`, `@metabase/client/domain/<r>` and `../../output/views/<r>`. A schema declared in `packages/cli` instead — or a command importing one by a relative path into the client's src tree — is FAIL. Editing existing commands to wire the schema is FAIL — commands import what they need. Do **not** require a fixture + parse-test pair — that is a tautology against Zod itself. The schema is contract-tested by the e2e tier when a command consuming it runs against the live API. A new or modified API resource without both exports, or that omits `.loose()` on the full schema, or that omits `.strip()` after `.pick()` on the Compact, or that hand-rolls `Array<Record<string, unknown>>` / inline `as { ... }` casts in commands instead of consuming `z.infer<typeof Schema>`, is a structural failure. `Record<string, unknown>` is forbidden in command files. The single exemption is `packages/cli/src/output/projection.ts` — the canonical structurally-untyped envelope for dot-path `--fields` projection over already-validated payloads. ## Resource-method contract `packages/client/src/resources/<r>.ts` exports `<r>Resource(transport)` and holds every `/api/` path the client sends. `packages/client/src/client.ts` composes each namespace onto the client, so a resource file with no line in `client.ts` is unreachable and FAIL. Eight conventions bind every method: 1. `client.<resource>.<method>(...)`, namespace named after the API resource (`mb.card`, singular). The resource is what Metabase calls the thing, not what it calls the route: the file and the namespace are `snippet` even though the endpoint is `/api/native-query-snippet`, because the domain schema, the CLI noun and the Metabase concept all read `snippet`. 2. Path parameters positional, then params, then options: `update(id, params, options?)`. 3. Params use Metabase's own field names verbatim — `f`, `model_id`, `include_inactive`. **No mapping layer, no renaming.** 4. Transport concerns (`signal`, `timeoutMs`, `retries`) live in the trailing `options`, never in params. 5. Wire envelope schemas are module-private to `resources/`; they never appear in `domain/`. 6. Methods return domain values, never wire envelopes. Non-paginated lists return `ListResult<T>` = `{ data, total }`. 7. A string path parameter always goes through `encodeURIComponent`. 8. Every method carries the endpoint's description as a doc comment. A method violating any of the eight is FAIL — quote the signature or the path template. Convention 7 is the one with a silent failure mode: an un-escaped string interpolated into a path template lets a `/` or `?` in a setting key or collection ref rewrite the request. A new or changed method comes with its wire test in `packages/client/src/resources/<r>.test.ts` — one `it` per method, asserting URL, verb, headers and body in a single `toEqual` over `capture.calls` from `@metabase/client/testing/fetch-capture`. That test is what pins the path and query vocabulary; a method landed without one is FAIL. ## Version contract `packages/client/src/version/features.ts` is the only production file that compares a Metabase major (`major-comparison-guard.test.ts` scans both packages); `profile.ts` places a version and `tag.ts` parses one. A `.major` comparison or a literal major used as a decision anywhere else is FAIL. Feature names describe a behaviour (`transformTargetTableId`), never a version (`transformV61Shape` is FAIL), and every version-gated rule must flip somewhere inside `KNOWN_RANGE` — `features.test.ts` enforces it, so a diff that raises `KNOWN_RANGE.min` without deleting the rules that went constant is FAIL. Every method in `resources/*.ts` has a matching `METHOD_REQUIREMENTS` entry under its own namespace, strictest feature first (`requirements.test.ts`). A gated method not exported through exactly one of the resource's `explain`, `explainWalk`, `refuse` or `refuseAfterReading` (`explainer(transport, "<ns>")`) is FAIL (`explain-guard.test.ts`). A method refusing before the wire is FAIL unless the refusal guards a parameter an unsupporting server would drop or rewrite without a word, asked through `transport.requireFeatures` or a value check on `transport.preflightServer()` whose refusal is thrown through `transport.refuseBeforeSending` (a refusal thrown directly, or a value check reading `verifiedServer`, is FAIL: the caller could not skip it), or the method is one whose every call such a server would answer wrongly without a word, exported through `refuse` and ending in optional `RequestOptions`, or through `refuseAfterReading` with a reader when it answers from a setting the session properties carry (a `refuse`-wrapped method that reads the session properties itself pays a second request); either way with a comment saying what the server would do; a parameter the server rejects belongs in `explain`'s parameter callback, with the request fields it travels in. A method that chooses what to send from `transport.server()`'s `features` is FAIL — that profile may be a stale cache; send what every server accepts, or decide against `verifiedServer`. A method without an entry, or a command under `packages/cli/src/commands/` declaring `requires` that differs from the `client.<ns>.<method>(` calls in its body (`requires-guard.test.ts`), is FAIL. A `defineMetabaseCommand` declaring a version floor or a token feature is FAIL — the features are derived from `requires`, never declared. Version-driven drift is a `<Resource>WireV<N>` schema plus a converter, selected by a feature and handed to `requestParsed` through an exported `<resource><Endpoint>Schema(features): z.ZodType<Resource>` reader. The canonical schema is the newest server's shape; a field an older server cannot report is `.nullable()`. FAIL: `.optional()` meaning "an older server omits it"; a converter mapping two distinguishable wire states onto one canonical state; a `z.union` of generation shapes parsed by trial (runtime shape detection); a converter that leaves a wire-only field on the canonical value; a discriminated union of generations as a method's public return type; version prose in `packages/cli/skill-data/` where a `requires:` frontmatter key or a `<!-- requires: … -->` … `<!-- /requires -->` fence belongs (markers on their own lines, never inside a table). ## Forbidden patterns and their permitted homes CLAUDE.md declares these as a **separate policy per package**, applied to production source only (`*.test.ts` files are exempt). A pattern's permitted home in one package says nothing about the other: `JSON.parse` has a home in the client and none in the CLI, `fetch` has a home in each but at different paths. The e2e-side restrictions below are part of this review too. ### The client — every file under `packages/client/src/` | Pattern | Permitted only in | | ---------------------------------------------------------------------------------------------- | --------------------------------------- | | bare `fetch(`, `globalThis.fetch`, third-party HTTP libs (`got`/`axios`/`node-fetch`/`undici`) | `http/**` | | `new URL(` | `http/**`, `url.ts` | | `JSON.parse(` | `json.ts` | | `setTimeout` inside an `await` wait loop | `poll.ts`, `http/retry.ts` | | `process.stdout.write`, `process.stderr.write` | nowhere — the client owns no output | | `process.exit(` | nowhere — the client never ends the run | | `@clack/prompts`, `@clack/core` | nowhere — prompting is the CLI's job | | `fs`, `fs/promises`, `node:fs`, `node:fs/promises` | nowhere — the client touches no files | | `child_process`, `node:child_process` | nowhere — the client spawns nothing | | a `.ts` extension in an import specifier | nowhere | | any dependency beyond `zod` and `node:` builtins | nowhere | | an import resolving into `packages/cli` | nowhere | ### The CLI — every file under `packages/cli/src/` | Pattern | Permitted only in |
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub