From 7279d8a7383a04f8d7b09a4ca5e9f01f9f78bed1 Mon Sep 17 00:00:00 2001 From: Zac Farrell Date: Tue, 8 Sep 2026 14:50:41 -0700 Subject: [PATCH] docs(skills): tell agents what a load does not report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three things an agent cannot infer from the load's own output. A `;`- or tab-separated csv loads with no error into a single column named `id;name` — the load has no delimiter option, so a load that mangles every row still prints "Table loaded" and a row count, and an agent reports success. The skill now says to check the column list with `databases tables show` after loading a csv it did not write, which is where the single column shows up (verified). A `.tsv` extension fails with a 500, so that is named too. "json in any shape" is the CLI's local reshape, not an API feature: the load API and both SDKs take newline-delimited json only. An agent that prototypes with `databases load --file data.json` and then writes SDK code hits a schema inference error with nothing to connect it to. The two local json refusals — `one object per row` and `carries no json rows` — are worth naming as not-retryable. An unrecognised error is exactly what an agent retries unchanged. Skills ship in the release artifact (`skills.tar.gz`), so this reaches agents with the next tag rather than on merge. --- skills/hotdata/SKILL.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/skills/hotdata/SKILL.md b/skills/hotdata/SKILL.md index ed17605..58063b9 100644 --- a/skills/hotdata/SKILL.md +++ b/skills/hotdata/SKILL.md @@ -89,6 +89,12 @@ Returns workspaces with `public_id`, `name`, `active`, `favorite`, `provision_st **File formats:** `databases tables load` accepts **csv**, **json**, and **parquet** (local `--file`, remote `--url`, or a pre-staged `--upload-id`). The format is read from the file's extension; `--format csv|json|parquet` overrides it, and an unrecognised extension is left to the server to resolve from the bytes. **json is read in any shape** — an array of objects, a pretty-printed document, or one object per line: `--file`/`--url` reshape it to newline-delimited json before upload, so no `jq` step is needed. Every row must be an object, and the reshape is byte-faithful per row — key order and number digits are preserved. Two inputs are refused locally, before anything uploads: a row that is not an object, and a source with no rows at all (`[]`, or an empty file). Note the **load itself** reads json numbers as f64, whatever the shape: an integer wider than i64/u64 or a decimal past ~17 significant digits lands rounded — use parquet, or a string column, where the exact digits matter. +**Loading — three checks before you trust a load:** + +- **A csv must be comma-separated.** A `;`- or tab-separated file loads **with no error** into a single column named `id;name` — the load has no delimiter option, so nothing reports this. After loading a csv you did not write yourself, run `databases tables show` and check the column list before you query or report success. (A `.tsv` extension currently fails with a 500.) +- **"json in any shape" is the CLI's doing, not the API's.** `--file`/`--url` reshape an array or a pretty-printed document into newline-delimited json locally. The load API and the SDKs accept **newline-delimited json only** (`format: "json"`), so code that moves off the CLI must convert the file itself. +- **Two json sources are refused before anything uploads — do not retry them unchanged.** `one object per row` means a row is a scalar or a list: reshape the file so every row is an object. `carries no json rows` means the source is `[]` or empty: there is nothing to load. + **Active database:** `hotdata databases use ` saves the active database to config. `databases tables list`/`load`/`remove`, `databases queries`/`results`, and all `databases context` commands default to the active database; pass **`--database `** to override per-command. (`databases tables show` instead takes a fully-qualified `catalog.schema.table`.) **Always select databases by id** (`dbid...`, from `databases list`). Display names and catalog aliases are not unique — several databases can share a name, and a fork answers to the same catalog as its source — so name-based selection is ambiguous.