|
| 1 | +# sqlite3 invocation shapes — agent parity catalogue |
| 2 | + |
| 3 | +This index tracks how reasoning agents actually invoke `sqlite3` in production |
| 4 | +(via the `bash` tool) and our parity status. Source: a Braintrust review of |
| 5 | +~924 spans across the four `flowglad-pay-agent*` projects in 2026-04. |
| 6 | + |
| 7 | +Each shape maps to one or more pinning tests under |
| 8 | +`src/commands/sqlite3/sqlite3.{invocation-shapes,sql-features,dot-commands,flags}.test.ts`. |
| 9 | +If a test in this directory regresses, an agent in production has hit (or will |
| 10 | +hit) the same failure. |
| 11 | + |
| 12 | +This file is the **triage list** — the GitHub issue tracker is disabled on |
| 13 | +this fork, so deferred work lives here. Update the **Status** column when |
| 14 | +patches land. |
| 15 | + |
| 16 | +## Shell invocation shapes |
| 17 | + |
| 18 | +| ID | Shape | Status | Test | |
| 19 | +| --- | ----------------------------------------------------------- | ------------- | ---- | |
| 20 | +| S1 | `sqlite3 :memory: "SELECT sqlite_version();"` | Supported | invocation-shapes / S1 | |
| 21 | +| S2 | `which sqlite3 && file $(which sqlite3)` (capability probe) | **Partial**: `file` builtin missing | — | |
| 22 | +| S3 | `sqlite3 <db> "<single SQL>"` | Supported | invocation-shapes / S3 | |
| 23 | +| S4 | `sqlite3 <db> "stmt1; stmt2; stmt3"` | Supported | invocation-shapes / S4 | |
| 24 | +| S5 | `sqlite3 <db> < /workspace/file.sql` (script redirect) | Supported | invocation-shapes / S5 | |
| 25 | +| S6 | `echo "<SQL>" \| sqlite3 <db>` (stdin pipe) | Supported | invocation-shapes / S6 | |
| 26 | +| S7 | `sqlite3 <db> "$(cat file.sql)"` (command substitution) | Supported | invocation-shapes / S7 | |
| 27 | +| S8 | `sqlite3 -header -separator $'\t' <db> "..."` (TSV+header) | Supported | invocation-shapes / S8 | |
| 28 | +| S9 | `sqlite3 -separator $'\t' <db> "..."` (TSV no header) | Supported | invocation-shapes / S9 | |
| 29 | +| S10 | `sqlite3 <db> "..." > /workspace/out.tsv` | Supported | invocation-shapes / S10 | |
| 30 | +| S11 | Per-statement loop (sequential calls) | Supported | invocation-shapes / S11 | |
| 31 | + |
| 32 | +## SQL features |
| 33 | + |
| 34 | +| ID | Feature | Status | Test | |
| 35 | +| --- | -------------------------------------------------------- | --------- | ---- | |
| 36 | +| F1 | `CREATE TABLE … AS SELECT …` | Supported | sql-features / F1 | |
| 37 | +| F2 | Bulk `INSERT` blocks from script-redirected `.sql` | Supported | sql-features / F2 | |
| 38 | +| F3 | Window functions (`SUM(...) OVER (PARTITION BY ...)`) | Supported | sql-features / F3 | |
| 39 | +| F4 | CTEs (`WITH x AS …`) including `WITH RECURSIVE` | Supported | sql-features / F4 | |
| 40 | +| F5 | `strftime('%Y-%m', …)`, `strftime('%Y-W%W', …)` | Supported | sql-features / F5 | |
| 41 | +| F6 | `sqlite_master` introspection | Supported | sql-features / F6 | |
| 42 | +| F7 | `CASE WHEN`, scalar subqueries | Supported | sql-features / F7 | |
| 43 | +| X4 | `PRAGMA table_info(t)` and friends | Supported | sql-features / X4 | |
| 44 | + |
| 45 | +## Dot-commands |
| 46 | + |
| 47 | +| ID | Command | Status | Test | |
| 48 | +| --- | ------------------------------------ | ---------------------- | ---- | |
| 49 | +| D1 | `.tables [pat]` | Supported (one-name-per-line, not multi-column) | dot-commands / D1 | |
| 50 | +| D2 | `.schema [pat]` | Supported | dot-commands / D2 | |
| 51 | +| D3 | `.headers on/off` (alias `.header`) | Supported | dot-commands / D3 | |
| 52 | +| D4 | `.mode <list\|csv\|json\|line\|column\|table\|markdown\|tabs\|box\|quote\|html\|ascii>` | Supported (last-mode-wins limitation) | dot-commands / D4 | |
| 53 | +| D5 | `.separator <col> [<row>]` | Supported | dot-commands / D5 | |
| 54 | +| D6 | `.nullvalue <text>` | Supported | dot-commands / D6 | |
| 55 | +| D7 | `.read <file>` (recursive) | Supported | dot-commands / D7 | |
| 56 | +| D8 | `.import <file> <table>` | **Deferred — see Open work / D8** | dot-commands / D8 (todo) | |
| 57 | +| D9 | `.dump` | **Deferred — see Open work / D9** | dot-commands / D9 (todo) | |
| 58 | +| — | `.help .show .timer .changes .bail .echo .eqp .width .prompt .print .explain` | Accepted as no-op | — | |
| 59 | +| — | `.import .dump .clone .save .restore .backup .open .shell .system .iotrace .log .cd .load .excel` | Rejected with explicit error | dot-commands / "explicitly unsupported" | |
| 60 | + |
| 61 | +## Flags |
| 62 | + |
| 63 | +| ID | Flag | Status | Test | |
| 64 | +| --- | ------------------------------------- | --------- | ---- | |
| 65 | +| — | `-list -csv -json -line -column -table -markdown -tabs -box -quote -html -ascii` | Supported | options / output-modes | |
| 66 | +| — | `-header / -noheader / -separator / -newline / -nullvalue / -readonly / -bail / -echo / -cmd / -version / --` | Supported | options | |
| 67 | +| X1 | `-init <file>` | Supported | flags / X1 | |
| 68 | +| X2 | `-batch` | Supported (no-op) | flags / X2 | |
| 69 | + |
| 70 | +## Limitations |
| 71 | + |
| 72 | +- **Float precision**: `ROUND(x, 2)` does not emit clean two-decimal output. |
| 73 | + just-bash mirrors real sqlite3's full IEEE-754 precision (`999.99` → |
| 74 | + `999.99000000000001`). Agents who need clean cents should compute in |
| 75 | + integer cents. Pinned in invocation-shapes / S8. |
| 76 | +- **Last-mode-wins**: dot-commands that mutate formatter state (`.mode`, |
| 77 | + `.headers`, `.separator`, `.nullvalue`) apply globally to the entire |
| 78 | + invocation, not incrementally. Real sqlite3 applies them statement by |
| 79 | + statement. Pinned in dot-commands / "interleaved mode + query". |
| 80 | +- **`.tables` format**: one name per line, not real sqlite3's 3-column |
| 81 | + space-padded format. Easier to parse, but a divergence. |
| 82 | + |
| 83 | +## Open work (deferred) |
| 84 | + |
| 85 | +### D8: `.import <file> <table>` — load CSV/TSV into a table |
| 86 | + |
| 87 | +Real sqlite3 uses `.import` to ingest CSV/TSV. Agents currently work around |
| 88 | +this by translating CSV → `INSERT` statements via `awk`, which is slow and |
| 89 | +brittle. Trace evidence: ~22 invocations of the awk-then-script pattern in |
| 90 | +the sample of 200 spans. |
| 91 | + |
| 92 | +**Suggested approach**: implement in `dot-commands.ts`. Parse |
| 93 | +`.import [--csv|--ascii] [--skip N] FILE TABLE`. Read FILE via `ctx.fs`, |
| 94 | +parse with the existing papaparse dep, translate to `INSERT INTO TABLE |
| 95 | +VALUES (...)` statements appended to the SQL stream. |
| 96 | + |
| 97 | +**Test pin**: `sqlite3.dot-commands.test.ts` has `it.todo` under |
| 98 | +`describe("D8: .import")`. Flip to `it(...)` once implemented. |
| 99 | + |
| 100 | +### D9: `.dump` — emit schema + INSERTs reproducing the database |
| 101 | + |
| 102 | +Walk `sqlite_master`, emit every `CREATE TABLE/INDEX/VIEW/TRIGGER` with a |
| 103 | +trailing `;`, then `SELECT *` from each table to generate `INSERT INTO ... |
| 104 | +VALUES (...)` lines. Wrap in `BEGIN; ... COMMIT;`. |
| 105 | + |
| 106 | +**Test pin**: `sqlite3.dot-commands.test.ts` has `it.todo` under |
| 107 | +`describe("D9: .dump")`. |
| 108 | + |
| 109 | +### X3: `ATTACH DATABASE 'other.db' AS o` to real-FS file paths |
| 110 | + |
| 111 | +sql.js is a sandboxed WASM build with no real-FS access. `ATTACH` to a path |
| 112 | +opens an empty in-memory database, not the file the agent expects. |
| 113 | + |
| 114 | +**Suggested approach**: pre-process `ATTACH DATABASE 'path' AS alias` — |
| 115 | +read `path` via `ctx.fs`, load it into sql.js as an in-memory DB, register |
| 116 | +under `alias`. Write back on exit if modifications detected. Non-trivial: |
| 117 | +need a multi-buffer worker protocol. |
| 118 | + |
| 119 | +**No test pin yet** — file under |
| 120 | +`sqlite3.invocation-shapes.test.ts` if/when added. |
| 121 | + |
| 122 | +### S2: `file` builtin missing |
| 123 | + |
| 124 | +`which sqlite3 && file $(which sqlite3)` — `file` is not a just-bash builtin. |
| 125 | +This isn't strictly a sqlite3 issue; it's a gap in the command set. Agents |
| 126 | +use it to verify the binary type during capability probes. |
| 127 | + |
| 128 | +**Suggested approach**: add `file` as a stub in `src/commands/file/` that |
| 129 | +inspects the magic bytes of the target file and reports an extension-based |
| 130 | +type. Doesn't need full libmagic — just enough to identify ELF, Mach-O, |
| 131 | +PE, ASCII text, JSON, gzip, zip, sqlite, etc. |
| 132 | + |
| 133 | +## When this catalogue should be re-run |
| 134 | + |
| 135 | +Pull a fresh sample any time: |
| 136 | + |
| 137 | +- A `flowglad-pay-agent*` project's prompt set materially changes. |
| 138 | +- We bump `sql.js` (regression risk for SQL features). |
| 139 | +- An agent reports a new failure mode involving `sqlite3`. |
| 140 | + |
| 141 | +Re-run via the Braintrust SQL-query MCP: |
| 142 | + |
| 143 | +```sql |
| 144 | +-- across the four flowglad-pay-agent* projects |
| 145 | +WHERE output::text ILIKE '%sqlite3%' |
| 146 | +ORDER BY created DESC |
| 147 | +LIMIT 200 |
| 148 | +``` |
| 149 | + |
| 150 | +Then update the tables above and add new pinning tests. |
0 commit comments