Skip to content

Commit 03dc476

Browse files
authored
Merge pull request #5 from flowglad/sqlite3-invocation-shape-tests-and-dot-commands
sqlite3: pin agent invocation shapes; add dot-commands + -init/-batch
2 parents c79cd0b + 654a758 commit 03dc476

7 files changed

Lines changed: 1502 additions & 5 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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

Comments
 (0)