Read and follow @AGENTS.md. It holds the architecture, the patterns and the procedures, and it is where the reasoning behind every rule below lives.
- ODBC spec compliance is mandatory. Read the spec page for every function
whose behaviour you change. The generic FFI entry points live in
stackable-odbc-core, but what this driver returns fromget_info,get_info_raw, the catalog functions and the type-conversion paths is directly observable by applications, and each has a spec-defined shape and value range. Never claim a SQLSTATE or an info value is wrong without checking the actual spec table first. Pay attention to (DM) annotations: those SQLSTATEs are returned by the Driver Manager, not the driver. - Route every client error through
map_sqlite_error. It is the single place that decides the SQLSTATE, and a new classified variant must carry the originating error in itscausefield. See Backend error mapping. - One error type. Every
BackendandStatementBackendmethod returnsResult<_, SqliteError>. AnOdbcErrorcore produced travels back throughSqliteError::Odbcvia.into(). Never reclassify it, which would discard the SQLSTATE core chose. - Declare each capability once. A
SQLGetInfovalue with aBackendhook is answered through the hook only, never also inget_info_raw. See Declaring capabilities. - Use
odbc-systypes, re-exported fromstackable_odbc_core::types, or fromstackable_odbc_core::odbc_sysfor anythingtypesdoes not re-export. Never redefine what it provides, never add anodbc-sysdependency to this crate'sCargo.toml, and never hand-roll a#[repr(C)]mirror of one of its structs. See Named constants. - Convert raw integers to typed enums at the boundary with core's
xxx_from_raw()functions, nevertransmute. - Do not make result-set fetching lazy.
exec_directmaterialises every row before returning, and two reported ODBC capabilities (SQL_CURSOR_COMMIT_BEHAVIOR,SQL_CURSOR_ROLLBACK_BEHAVIOR) are only correct because of it. See Result sets are materialised eagerly and Transactions. - Run
pre-commit run --all-filesbefore every commit. It is the single source of truth for what must pass.
- Do not modify files outside the scope of the current task.
- Do not add features, refactoring, or "improvements" beyond what was asked.
- If unsure whether something is in scope, ask.
Never read entire files by default. Survey, locate, then extract.
- Survey first. Check the file size with
stat -c%s filebefore reading it. Anything over 50 KB must be sliced, not read whole; several modules insrc/are. - Navigate definitions with ctags. Run
ctags -R .once to build the index, thengrep "^SymbolName" tagsfor the exact file and line of any function, struct or trait. No file reading needed. - Locate with Grep. Find patterns, keywords or usages before reading. Use
-Cfor context lines. - Extract with Read, using
offsetandlimitonce you know the line range. - Read structured data with a tool that understands it:
jqfor JSON,yqfor YAML. Never read raw markup whole. - Survey the filesystem with
tree -L 2 -I '.git|target|node_modules', not a recursivels. - Verify edits with
git diff -urather than re-reading the file.