Skip to content

cli: group versions & block-config, add spec --to, role-based help, friendlier env selection#7

Open
anaslabgoul wants to merge 17 commits into
retab-dev:mainfrom
anaslabgoul:versions-grouping-env-fixes
Open

cli: group versions & block-config, add spec --to, role-based help, friendlier env selection#7
anaslabgoul wants to merge 17 commits into
retab-dev:mainfrom
anaslabgoul:versions-grouping-env-fixes

Conversation

@anaslabgoul

Copy link
Copy Markdown

Summary

A focused pass over the CLI command surface and its --help presentation,
making the command tree mirror the backend resource model more closely and
simplifying several rough edges. Net change is +494 / −710 (a
simplification). All commits touch only cli/cmd/.

What changed

  • workflows versions {list,get,diff,restore} — replaces the four flat
    verbs (versions/diff/version/version-restore) and their dual
    positional/flag resolvers with one cohesive group. Mirrors the SDK
    (ListVersions/GetVersion/ListDiff/CreateVersionRestore).
  • workflows blocks config {pull,push,diff,validate,doctor} — groups the
    five flat *-config verbs under a config subcommand.
  • workflows spec plan|apply --to <workflow-id> — folds the plan-to /
    apply-to commands into a --to flag (mutually exclusive with
    --project-id); the old commands remain as hidden, deprecated aliases.
  • env selection by id or case-insensitive name with loud ambiguity /
    not-found errors instead of a silent fallback to the active environment;
    env get is now visible and create is a proper alias of add.
  • extractions subcommands reordered to the shared lifecycle order used by
    the other primitives, so every primitive's --help reads the same.
  • Root --help recategorized by backend role (Primitives · Resources ·
    Workflows · Organization · Account · Setup), so backend resources no longer
    sit in an anonymous "Other" bucket next to the local-only setup/sync
    tools. A guard test fails if a future top-level command is left
    uncategorized.

Bug fix included

cli/cmd/main_test.go now isolates both HOME and %USERPROFILE% in a
TestMain. Previously the cmd tests only isolated $HOME, but config resolves
via os.UserHomeDir() (= %USERPROFILE% on Windows), so config-writing tests
could overwrite a developer's real ~/.retab/config.json.

Testing

  • go build ./...go vet ./cmd/gofmt clean ✅
  • go test ./... — green except 12 pre-existing Windows-only failures
    (Unix file-mode/exec-bit//tmp assertions); these fail identically on a
    clean main baseline and are unrelated to this change. The design/parity/help
    contract tests (api_design_*, sdk_parity, help) all pass.

Notes for reviewers

  • Deprecation style is mixed by intent: the versions collapse removed the old
    commands outright, while spec plan-to/apply-to and env create are kept
    as hidden aliases. Happy to align on one convention.
  • Unrelated but noticed: the repo has no .gitattributes enforcing LF, so
    Windows checkouts (core.autocrlf=true) trip gofmt -l on every file; and
    no CI workflow runs go test/go vet. Both are out of scope here but worth a
    follow-up.

anaslabgoul and others added 17 commits June 10, 2026 16:38
Restore the grouped `versions {list,get,diff,restore}` structure for workflow, block, and edge versions, mirroring the backend /v1/workflows[/blocks|/edges]/versions/* route tree. Reverts the flattening from 18bcea4; matches the v0.2.1 release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Accept env id/name/slug (case-insensitive) in env switch/claim with tiered precedence and explicit ambiguity/not-found errors instead of a silent fallback. Add TestMain to isolate cmd tests from the real config dir on all OSes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- workflows blocks: group the five *-config verbs under a `config`
  subcommand (config pull/push/diff/validate/doctor)
- workflows spec: add `--to <workflow-id>` to plan/apply; hide
  plan-to/apply-to as deprecated aliases
- env: expose `env get`; make `create` a real alias of `add` (drop the
  hidden duplicate command)
- extractions: reorder subcommands to the shared lifecycle order used by
  the other primitives
- reconcile api_design and sdk_parity tests with the already-collapsed
  `versions` group surface

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recategorize the root --help command menu so every command sits in a
role-named group (Primitives, Resources, Workflows, Organization,
Account, Setup) instead of leaving backend resources (tables, secrets,
projects) in the anonymous Other fallback next to the non-API local
tools (setup, sync). `files` leads the renamed Resources group as the
first-class resource it is.

Add TestEveryTopLevelCommandIsCategorized so a future uncategorized
top-level command fails loudly instead of silently landing in Other.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
retab env switch/claim resolve a server environment by id or
case-insensitive name; the environment object has no slug field, and
"slug" already means the local --env config profile (credential.go).
Rewording removes that conflation in the help text and resolver comments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…create

Inline --capture run-id[:step-id] avoids hand-writing a captures JSON
file; --run (+ --wait) creates and launches a run in one command;
--from-experiment clones an existing experiment. block-id/name move to
RunE enforcement so the clone path can inherit them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
filepath.IsAbs misses POSIX-rooted paths like /tmp/out on Windows (no
drive letter), letting them escape the function bundle. Also reject a
leading slash: a no-op on POSIX (already IsAbs), closes the Windows gap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
All test-side: guard POSIX mode/exec-bit asserts on Windows; isolate
USERPROFILE (config/registry resolve via os.UserHomeDir there) and
LocalAppData (liteparse cache); assert the api-call request path via the
decoded JSON field instead of a raw substring (Windows escapes \).
Verified green on Windows and on a real Linux run via WSL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Serialize the OAuth bearer-token provider with a mutex: on the pure-OAuth
  path it is the bearer provider directly (no dashboard-context mutex in
  front), so concurrent SDK requests raced on the captured token and
  interleaved config writes. Locking also collapses a refresh thundering herd.
- Send direct storage uploads through the bounded fileDownloadClient instead
  of http.DefaultClient (which has no timeout), so a wedged storage endpoint
  can't hang an upload forever in unattended scripts. Fixed in both build-tag
  mirrors (files.go and the oagen overlay).
- Cap how much request/response body --debug copies into the dump string
  (256 KiB); the wire body is untouched, but dumping a large inline base64
  document no longer doubles its size in memory just to print it.
- Clean up the orphaned temp file when writeParseCache's atomic rename fails.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
InferMIMEData accepts a {content, mime_type} descriptor with no url (a valid
inline document input server-side), but MIMEData.MarshalJSON only emitted
{filename, url} — serializing such documents as {"filename":"","url":""}, i.e.
an empty document silently sent over the wire. Emit content/mime_type too,
with omitempty so url-only inputs are unchanged. Adds a marshalling regression
test (the existing request-shape test only asserted filename survived).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…tic floor

- Request builder used `elif data:` which is falsy for `{}`/`[]`, so cancel/
  complete-style endpoints that legitimately POST an empty object sent no body
  at all. Use `data is not None` (4 sites, sync + async, request + stream).
- setup.py read requirements.txt / README.md relative to cwd, so `pip install .`
  from another directory raised FileNotFoundError; resolve relative to __file__.
- Pin pydantic>=2: the SDK uses v2-only APIs, so a v1 environment installs
  cleanly then crashes at import.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README: `npm install @retab/node` (not `retab`); pass json_schema as a dict;
  drop the dead `client.projects.extract` snippet (no such resource) while
  keeping the platform/evals framing.
- Cookbook Python: unwrap the ExtractionRequest wrapper (create() takes kwargs),
  load json_schema as a dict, use a real model, and fix the off-by-one asset
  path (../../ -> ../../../).
- Cookbook TS: the object-form create({...}) call throws "unsupported input
  type" at runtime; rewrite to the real positional signatures for extractions
  and parses, and fix the asset paths.
- Node README local-dev path (open-source/sdk/... -> clients/node).
- SKILL install command missing `| sh`; goreleaser comment go 1.26.1 -> 1.25.0
  (matches cli/go.mod).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…c message

parseAPIError's detail switch only handled string and map shapes, so FastAPI's
native validation envelope {"detail": [ {loc, msg, type}, ... ]} fell through
and the error degraded to the generic "Request failed (422)". Add a []any case
that stringifies the array so the real validation errors are surfaced. Adds a
regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… validation

- Wire the existing Tables/Secrets resources onto Retab and AsyncRetab; they
  were implemented but never attached, so client.tables / client.secrets
  raised AttributeError.
- prepare_mime_document: a bare file-like object without a .name (e.g.
  io.BytesIO) produced filename "uploaded_file" with no extension and then
  failed validation with "Invalid file type: uploaded_file"; sniff the bytes
  to recover an extension, matching the `bytes` branch.
- Replace the bare asserts in the document type/extension validation with
  explicit raises so the checks are not stripped under `python -O`.
- display.py: image `detail` is optional in the OpenAI format; use .get(...)
  to avoid a KeyError. Also drop a computed-then-discarded sha256.
- Adds offline regression tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The quickstart notebooks didn't run:
- extractions.create() was called with an ExtractionRequest(...) wrapper, but
  create() takes keyword args directly -> unwrapped all call sites.
- 00: schemas.generate() has no `modality` param (removed); it returns a
  SchemaGeneration object, not a dict, so use schema.json_schema; client.models
  .list() doesn't exist -> replaced with a comment (no enumeration method).

Every transformed code cell was validated with ast.parse and the notebooks
re-validated with nbformat. Not executed against the live API (no key locally).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- parseSheetRows decoded a cell's column letters with no upper bound, then
  allocated a per-row slice of that size. A crafted/garbage .xlsx with a ref
  like "ZZZZZZZ1" (colIndex in the hundreds of millions) drove an effective
  OOM on `files parse|grep|inspect`. Clamp to Excel's hard limit (XFD = 16384)
  and drop out-of-range cells.
- cleanWorkflowTableCell truncated by bytes, cutting multibyte (accented / CJK)
  cells mid-rune and emitting invalid UTF-8 in `tables query` output. Truncate
  by runes, matching the sibling truncateReviewCell.
- Adds regression tests for both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove four frictions hit while running model experiments:

- workflows runs create: add --wait (+ --poll-interval-ms,
  --timeout-seconds) to block until the run reaches a terminal
  status, matching extractions create / experiments runs create.
- workflows runs get: add --steps to fetch and embed per-block
  step records (the run GET endpoint does not return them
  server-side, so this saves a second steps-list call).
- workflows blocks get: emit derived input/output handle ids so
  edge wiring no longer requires inspecting another workflow's
  edges. Inputs from config.inputs; outputs for deterministic
  block types only (routing handles are route-keyed).
- projects create: add the command (POST /v1/projects) so
  'workflows create --project-id' is satisfiable end-to-end via
  CLI; api_design command-surface test updated accordingly.

Co-Authored-By: Claude Opus 4.8 <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