diff --git a/.beads/.gitignore b/.beads/.gitignore index f336c89..363ebae 100644 --- a/.beads/.gitignore +++ b/.beads/.gitignore @@ -1,52 +1,51 @@ -# SQLite databases -*.db -*.db?* -*.db-journal -*.db-wal -*.db-shm -*.sqlite3 +# Dolt database (managed by Dolt, not git) +dolt/ +dolt-access.lock -# Daemon runtime files -daemon.lock -daemon.log -daemon-*.log.gz -daemon.pid +# Runtime files bd.sock +bd.sock.startlock sync-state.json last-touched # Local version tracking (prevents upgrade notification spam after git ops) .local_version -# Legacy database files -db.sqlite -bd.db - # Worktree redirect file (contains relative path to main repo's .beads/) # Must not be committed as paths would be wrong in other clones redirect -# Merge artifacts (temporary files from 3-way merge) -beads.base.jsonl -beads.base.meta.json -beads.left.jsonl -beads.left.meta.json -beads.right.jsonl -beads.right.meta.json - # Sync state (local-only, per-machine) # These files are machine-specific and should not be shared across clones .sync.lock -.jsonl.lock -sync_base.jsonl export-state/ -# Dolt database (managed by Dolt remotes, not git) -dolt/ -dolt-access.lock +# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned) +ephemeral.sqlite3 +ephemeral.sqlite3-journal +ephemeral.sqlite3-wal +ephemeral.sqlite3-shm + +# Dolt server management (auto-started by bd) +dolt-server.pid +dolt-server.log +dolt-server.lock +dolt-server.port +dolt-server.activity +dolt-monitor.pid -# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here. -# They would override fork protection in .git/info/exclude, allowing -# contributors to accidentally commit upstream issue databases. -# The JSONL files (issues.jsonl, interactions.jsonl) and config files -# are tracked by git by default since no pattern above ignores them. +# Backup data (auto-exported JSONL, local-only) +backup/ + +# Legacy files (from pre-Dolt versions) +*.db +*.db?* +*.db-journal +*.db-wal +*.db-shm +db.sqlite +bd.db +# NOTE: Do NOT add negation patterns here. +# They would override fork protection in .git/info/exclude. +# Config files (metadata.json, config.yaml) are tracked by git by default +# since no pattern above ignores them. diff --git a/.beads/backup/backup_state.json b/.beads/backup/backup_state.json new file mode 100644 index 0000000..b1550ba --- /dev/null +++ b/.beads/backup/backup_state.json @@ -0,0 +1,13 @@ +{ + "last_dolt_commit": "fit9rrh4oo30fkpqacfa5i999p582hrc", + "last_event_id": 0, + "timestamp": "2026-03-06T07:05:13.345572Z", + "counts": { + "issues": 111, + "events": 0, + "comments": 0, + "dependencies": 14, + "labels": 4, + "config": 13 + } +} \ No newline at end of file diff --git a/.beads/backup/comments.jsonl b/.beads/backup/comments.jsonl new file mode 100644 index 0000000..e69de29 diff --git a/.beads/backup/config.jsonl b/.beads/backup/config.jsonl new file mode 100644 index 0000000..0b18a0c --- /dev/null +++ b/.beads/backup/config.jsonl @@ -0,0 +1,13 @@ +{"key":"auto_compact_enabled","value":"false"} +{"key":"compact_batch_size","value":"50"} +{"key":"compact_model","value":"claude-haiku-4-5-20251001"} +{"key":"compact_parallel_workers","value":"5"} +{"key":"compact_tier1_days","value":"30"} +{"key":"compact_tier1_dep_levels","value":"2"} +{"key":"compact_tier2_commits","value":"100"} +{"key":"compact_tier2_days","value":"90"} +{"key":"compact_tier2_dep_levels","value":"5"} +{"key":"compaction_enabled","value":"false"} +{"key":"issue_prefix","value":"el"} +{"key":"schema_version","value":"6"} +{"key":"types.custom","value":"molecule,gate,convoy,merge-request,slot,agent,role,rig,message"} diff --git a/.beads/backup/dependencies.jsonl b/.beads/backup/dependencies.jsonl new file mode 100644 index 0000000..02a5113 --- /dev/null +++ b/.beads/backup/dependencies.jsonl @@ -0,0 +1,14 @@ +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-713","issue_id":"el-713.1","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-713","issue_id":"el-713.2","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-713","issue_id":"el-713.3","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-713","issue_id":"el-713.4","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-713","issue_id":"el-713.5","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-713","issue_id":"el-713.6","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-6yg","issue_id":"el-8wn","type":"blocks"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-oxv","issue_id":"el-8wn","type":"blocks"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-6yg","issue_id":"el-dcb","type":"blocks"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-oxv","issue_id":"el-dcb","type":"blocks"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-5mr","issue_id":"el-e9r","type":"blocks"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-ffc","issue_id":"el-ffc.1","type":"parent-child"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-5mr","issue_id":"el-gwo","type":"blocks"} +{"created_at":"2026-02-16T15:38:03Z","created_by":"import","depends_on_id":"el-5mr","issue_id":"el-pre","type":"blocks"} diff --git a/.beads/backup/events.jsonl b/.beads/backup/events.jsonl new file mode 100644 index 0000000..e69de29 diff --git a/.beads/backup/issues.jsonl b/.beads/backup/issues.jsonl new file mode 100644 index 0000000..4af4ad6 --- /dev/null +++ b/.beads/backup/issues.jsonl @@ -0,0 +1,111 @@ +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"ca13809bea68d6a8003f4faaa0115a577861448d8304b70af0940145ee737b2d","created_at":"2025-12-30T17:44:09Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add support for loading SQLite extensions (FTS5, R-Tree, JSON1, custom extensions).\n\n**Context**: SQLite extensions provide powerful features like full-text search (FTS5), spatial indexing (R-Tree), and enhanced JSON support. Currently not supported in ecto_libsql.\n\n**Missing NIFs** (from FEATURE_CHECKLIST.md):\n- load_extension_enable()\n- load_extension_disable()\n- load_extension(path)\n\n**Use Cases**:\n\n**1. Full-Text Search (FTS5)**:\n```elixir\nEctoLibSql.load_extension(repo, \"fts5\")\nRepo.query(\"CREATE VIRTUAL TABLE docs USING fts5(content)\")\nRepo.query(\"SELECT * FROM docs WHERE docs MATCH 'search terms'\")\n```\n\n**2. Spatial Indexing (R-Tree)**:\n```elixir\nEctoLibSql.load_extension(repo, \"rtree\")\nRepo.query(\"CREATE VIRTUAL TABLE spatial_idx USING rtree(id, minX, maxX, minY, maxY)\")\n```\n\n**3. Custom Extensions**:\n```elixir\nEctoLibSql.load_extension(repo, \"/path/to/custom.so\")\n```\n\n**Security Considerations**:\n- Extension loading is a security risk (arbitrary code execution)\n- Should be disabled by default\n- Require explicit opt-in via config\n- Validate extension paths\n- Consider allowlist of safe extensions\n\n**Implementation Required**:\n\n1. **Add NIFs** (native/ecto_libsql/src/connection.rs):\n ```rust\n #[rustler::nif]\n fn load_extension_enable(conn_id: \u0026str) -\u003e NifResult\u003cAtom\u003e\n \n #[rustler::nif]\n fn load_extension_disable(conn_id: \u0026str) -\u003e NifResult\u003cAtom\u003e\n \n #[rustler::nif]\n fn load_extension(conn_id: \u0026str, path: \u0026str) -\u003e NifResult\u003cAtom\u003e\n ```\n\n2. **Add safety wrappers** (lib/ecto_libsql/native.ex):\n - Validate extension paths\n - Check if loading is enabled\n - Handle errors gracefully\n\n3. **Add config option** (lib/ecto/adapters/libsql.ex):\n ```elixir\n config :my_app, MyApp.Repo,\n adapter: Ecto.Adapters.LibSql,\n database: \"app.db\",\n allow_extension_loading: true, # Default: false\n allowed_extensions: [\"fts5\", \"rtree\"] # Optional allowlist\n ```\n\n4. **Documentation**:\n - Security warnings\n - Extension loading guide\n - FTS5 integration example\n - Custom extension development guide\n\n**Files**:\n- native/ecto_libsql/src/connection.rs (NIFs)\n- lib/ecto_libsql/native.ex (wrappers)\n- lib/ecto/adapters/libsql.ex (config handling)\n- test/extension_test.exs (new tests)\n- AGENTS.md (update API docs)\n\n**Acceptance Criteria**:\n- [ ] load_extension_enable() NIF implemented\n- [ ] load_extension_disable() NIF implemented\n- [ ] load_extension(path) NIF implemented\n- [ ] Config option to control extension loading\n- [ ] Path validation for security\n- [ ] FTS5 example in documentation\n- [ ] Comprehensive tests including security tests\n- [ ] Clear security warnings in docs\n\n**Test Requirements**:\n```elixir\ntest \"load_extension fails when not enabled\" do\n assert {:error, _} = EctoLibSql.load_extension(repo, \"fts5\")\nend\n\ntest \"load_extension works after enable\" do\n :ok = EctoLibSql.load_extension_enable(repo)\n :ok = EctoLibSql.load_extension(repo, \"fts5\")\n # Verify FTS5 works\nend\n\ntest \"load_extension rejects absolute paths when restricted\" do\n assert {:error, _} = EctoLibSql.load_extension(repo, \"/etc/passwd\")\nend\n```\n\n**References**:\n- FEATURE_CHECKLIST.md section \"Medium Priority\" item 4\n- LIBSQL_FEATURE_MATRIX_FINAL.md section 10\n\n**Priority**: P2 - Nice to have, enables advanced features\n**Effort**: 2-3 days\n**Security Review**: Required before implementation","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-07f","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Implement Extension Loading (load_extension)","updated_at":"2026-01-05T14:41:54Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T12:57:34Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f765286bedd7e8c43693002e02b2c014f1f3492bd70ad9abeaa5a0309dc93e94","created_at":"2026-01-08T12:55:32Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"stmt_caching_benchmark_test.exs is ambiguous - unclear if it's a benchmark or a functional test:\n- File is in test/ directory (suggests functional test)\n- File name includes 'benchmark' (suggests it's a performance benchmark)\n- Content needs review to determine intent\n\nAction:\n1. Review the file contents\n2. If it's a benchmark: Move to bench/ directory with proper benchmarking setup\n3. If it's a functional test with assertions: Keep in test/, rename to stmt_caching_performance_test.exs or clarify the name\n\nEffort: 15 minutes\nImpact: Clarify test intent, proper test/benchmark infrastructure","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-092","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Clarify purpose of stmt_caching_benchmark_test.exs","updated_at":"2026-01-08T12:57:34Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:56:11Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"239ca35684a8280c9336f974afc00cc0d9a6ba2863c86d74cf5312a0dc008f98","created_at":"2025-12-30T17:43:58Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"LibSQL-specific extension not in standard SQLite. CREATE TABLE ... RANDOM ROWID generates random rowid values instead of sequential. Useful for distributed systems. Cannot be combined with WITHOUT ROWID or AUTOINCREMENT.\n\nDesired API:\n create table(:users, random_rowid: true) do\n add :name, :string\n end\n\nEffort: 1-2 days (simple DDL addition).","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-0ez","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"RANDOM ROWID Support (libSQL Extension)","updated_at":"2026-01-17T08:56:11Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T22:10:03Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b9caa7080e2c66ae73af0599ff601145c14df7c1c33244dfe4f91bf314f69bfc","created_at":"2026-01-12T22:07:33Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"**Problem**: PR #57 identified critical issues when using ecto_libsql with Oban:\n\n1. **DateTime encoding failure**: NIF cannot serialise DateTime/NaiveDateTime/Date/Time/Decimal structs, causing 'Unsupported argument type' errors\n2. **Oban Lifeline plugin failure**: 'protocol Enumerable not implemented for Atom. Got value: nil' when processing query results\n\n**Root Cause**: \n- Rustler cannot automatically serialise complex Elixir structs like DateTime\n- These need to be converted to ISO8601 strings before passing to the Rust NIF\n\n**Solution Implemented**:\n- Added encode/3 in lib/ecto_libsql/query.ex to convert temporal types and Decimal to strings\n- Added guard clause for non-list params to prevent crashes\n- Native.ex already correctly normalises result columns/rows (nil for write ops without RETURNING, lists otherwise)\n- Added comprehensive test suite for parameter encoding\n\n**Tests Added**:\n- test/ecto_libsql_query_encoding_test.exs - covers DateTime/Date/Time/Decimal encoding, nil/int/float/string/binary pass-through, mixed parameters\n\n**Note**: PR #57's proposed normalisation changes were incorrect - Ecto expects columns: nil, rows: nil for write operations WITHOUT RETURNING, not empty lists.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-0mv","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Fix DateTime/Decimal parameter encoding for Oban compatibility","updated_at":"2026-01-12T22:10:03Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T14:27:33Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"73a1a77876932949fc216121a7030ad1f4ed3ea368e0dc443d6b2f0d2bc73667","created_at":"2026-01-14T14:23:23Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Timestamps are being returned as integers (1) instead of NaiveDateTime when data is queried. The issue is that column type `DATETIME` in manual CREATE TABLE statements needs to be changed to `TEXT` with ISO8601 format to match Ecto's expectations.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-0sm","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Timestamp format in compatibility tests","updated_at":"2026-01-14T14:27:33Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f2b4769b572481df896ee3a38bba4eaa9844e7bb3bc93181b2a749995ce373e8","created_at":"2025-12-30T17:35:53Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Works via fragments. Locale-specific sorting, case-insensitive comparisons, Unicode handling. Desired API: field :name, :string, collation: :nocase in schema, order_by with COLLATE, add :name, :string, collation: \"BINARY\" in migration. Effort: 2 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-0sr","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Better Collation Support","updated_at":"2025-12-30T17:36:48Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:25Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"91b57cbdfa2479ab17eb63b1e5eaa818897115e8faff1e13a9454cd857f9e900","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"test/cursor_streaming_test.exs (new file)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-0wo","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test File","updated_at":"2026-01-12T11:58:25Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2e3a2f048c13dbb95bcc745054ef42af79c4dd7fb1abcbf6274f02518173c750","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"30 minutes","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-1fe","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b3600145688cdaf0a8c1cba48b8697d89746755241fdffbf1b0327e4f3aa6be3","created_at":"2026-01-08T21:35:03Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Create documentation clarifying what should be tested in Rust vs Elixir layers.\n\nFrom TEST_AUDIT_REPORT.md item 7: 'Document Test Layering Strategy' - helps contributors understand testing strategy.\n\n**Documentation to Create**:\n\n**Rust Tests** (native/ecto_libsql/src/tests/) - Low-level correctness\n- Parameter binding (types, NULL, BLOB)\n- Transaction semantics\n- Basic query execution\n- Error handling\n- libsql API integration\n\n**Elixir Tests** (test/) - Integration \u0026 compatibility\n- Ecto adapter callbacks\n- Schema validation\n- Migrations\n- Ecto queries (where, select, joins)\n- Associations, preloading\n- Connection pooling\n- Remote/replica behavior\n- Advanced features (vectors, R*Tree, JSON)\n\n**Decision Tree**: When adding tests, where should they go?\n\n**File**: TESTING.md (create or update)\n\n**Estimated Effort**: 1-2 hours\n\n**Impact**: Better contributor onboarding, clearer test intent","design":"","due_at":null,"ephemeral":0,"estimated_minutes":80,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-1p2","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Document test layering strategy","updated_at":"2026-01-08T21:35:03Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"8a1d99452730487b973f902e2da9f3c217e8c27fd3d9fbe8b71264d46fabb1b9","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Savepoints in replica mode with sync","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-1xs","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test Scenarios","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:51:37Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f6c85861350a9c73c4d6eb70af4c451074ea3a671a912b838af9ac0d1e15f7f5","created_at":"2025-12-30T17:35:51Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Ecto query builder generates CTEs, but ecto_libsql's connection module doesn't emit WITH clauses. Critical for complex queries and recursive data structures. Standard SQL feature widely used in other Ecto adapters. SQLite has supported CTEs since version 3.8.3 (2014). libSQL 3.45.1 fully supports CTEs with recursion.\n\nIMPLEMENTATION: Update lib/ecto/adapters/libsql/connection.ex:441 in the all/1 function to emit WITH clauses.\n\nPRIORITY: Recommended as #1 in implementation order - fills major gap, high user demand.\n\nEffort: 3-4 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-1yl","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"CTE (Common Table Expression) Support","updated_at":"2026-01-17T08:51:37Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:51:12Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c10cfa0ad1d695abebdf308030b6b6294847c328d5cfced3d64eb3295f7defdd","created_at":"2025-12-30T17:43:14Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"CRITICAL: Prepared statements are re-prepared on every execution, defeating their purpose and causing 30-50% performance overhead.\n\n**Problem**: query_prepared and execute_prepared re-prepare statements on every execution instead of reusing cached Statement objects.\n\n**Location**: \n- native/ecto_libsql/src/statement.rs lines 885-888\n- native/ecto_libsql/src/statement.rs lines 951-954\n\n**Current (Inefficient) Code**:\n```rust\n// PERFORMANCE BUG:\nlet stmt = conn_guard.prepare(\u0026sql).await // ← Called EVERY time!\n```\n\n**Should Be**:\n```rust\n// Reuse prepared statement:\nlet stmt = get_from_registry(stmt_id) // Reuse prepared statement\nstmt.reset() // Clear bindings\nstmt.query(params).await\n```\n\n**Impact**:\n- ALL applications using prepared statements affected\n- 30-50% slower than optimal\n- Defeats Ecto's prepared statement caching\n- Production performance issue\n\n**Fix Required**:\n1. Store actual Statement objects in STMT_REGISTRY (not just SQL)\n2. Implement stmt.reset() to clear bindings\n3. Reuse Statement from registry in execute_prepared/query_prepared\n4. Add performance benchmark test\n\n**Files**:\n- native/ecto_libsql/src/statement.rs\n- native/ecto_libsql/src/constants.rs (STMT_REGISTRY structure)\n- test/performance_test.exs (add benchmark)\n\n**Acceptance Criteria**:\n- [ ] Statement objects stored in registry\n- [ ] reset() clears bindings without re-preparing\n- [ ] execute_prepared reuses cached Statement\n- [ ] query_prepared reuses cached Statement\n- [ ] Performance benchmark shows 30-50% improvement\n- [ ] All existing tests pass\n\n**References**:\n- LIBSQL_FEATURE_MATRIX_FINAL.md section 4\n- FEATURE_CHECKLIST.md Prepared Statement Methods\n\n**Priority**: P0 - Critical performance bug\n**Effort**: 3-4 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-2ry","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Fix Prepared Statement Re-Preparation Performance Bug","updated_at":"2026-01-17T08:51:12Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"cc83bb8605bb757e09b7f4da2f3de916efd7187ebc46877ddbc320584df70fc4","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Better contributor onboarding, clearer test intent","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-39j","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Impact","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"902dc2ddcb21c5b66bf22dfcacee48c0cec242b8107580a0860e60d2c1bfd30d","created_at":"2025-12-30T17:35:53Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Basic support only. Data validation at database level, enforces invariants, complements Ecto changesets. Desired API: add :age, :integer, check: \"age \u003e= 0 AND age \u003c= 150\" or named constraints: create constraint(:users, :valid_age, check: \"age \u003e= 0\"). Effort: 2-3 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-3ea","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Better CHECK Constraint Support","updated_at":"2025-12-30T17:36:47Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:26Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"5afc7d67a1597a34b45bca2f3c44c2e4b80cdfebfa2e250e3f919706a72cde97","created_at":"2026-01-08T21:33:39Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-3m3","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"/tmp/test_coverage_issues.md","updated_at":"2026-01-12T11:58:26Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"6f54c84db6b265bf4f17a4fcb85a7eb414c11f4fde8b553b3f69d8f25e58356f","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"- ecto_libsql_test.exs (after cleanup)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-3pz","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Files to Check","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-05T14:53:35Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2d50c329d27b33f3ae8d75842b5f334b8ea4908d34892fd24c5f959c47741728","created_at":"2025-12-30T17:35:52Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Works via fragments, but no dedicated support. libSQL 3.45.1 has JSON1 built into core (no longer optional). Functions: json_extract(), json_type(), json_array(), json_object(), json_each(), json_tree(). Operators: -\u003e and -\u003e\u003e (MySQL/PostgreSQL compatible). NEW: JSONB binary format support for 5-10% smaller storage and faster processing.\n\nDesired API:\n from u in User, where: json_extract(u.settings, \"$.theme\") == \"dark\", select: {u.id, json_object(u.metadata)}\n\nPRIORITY: Recommended as #6 in implementation order.\n\nEffort: 4-5 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-4ha","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"JSON helpers module (EctoLibSql.JSON) created with full API support - 54 comprehensive tests passing","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"JSON Schema Helpers","updated_at":"2026-01-05T14:53:35Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:55:22Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"9eaeab3f83bd07fe5f6114b6ebd830fdae7d857331b88ceb18d861da53e99e25","created_at":"2025-12-30T17:35:52Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Not implemented in ecto_libsql. libSQL 3.45.1 has full R*Tree extension in /ext/rtree/ directory. Complement to vector search for geospatial queries. Multi-dimensional range queries. Better than vector search for pure location data.\n\nUse cases: Geographic bounds queries, collision detection, time-range queries (2D: time + value).\n\nDesired API:\n create table(:locations, rtree: true) do\n add :min_lat, :float\n add :max_lat, :float\n add :min_lng, :float\n add :max_lng, :float\n end\n\n from l in Location, where: rtree_intersects(l, ^bounds)\n\nEffort: 5-6 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-4oc","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"R*Tree Spatial Indexing Support","updated_at":"2026-01-17T08:55:22Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"7f1239cd0a05e5b0e43b23504e104fe1bba106345d5f253766e24e23d2d93bec","created_at":"2026-01-07T11:59:35Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"The current regex-based approach in extract_named_params/1 (lib/ecto_libsql.ex:298-303) cannot distinguish between parameter-like patterns in SQL string literals/comments and actual parameters.\n\nExample edge case:\n SELECT ':not_a_param', name FROM users WHERE id = :actual_param\n\nThis would extract both \"not_a_param\" and \"actual_param\", even though the first is in a string literal.\n\nCurrent mitigations:\n1. SQL string literals with parameter-like patterns are uncommon\n2. Validation catches truly missing parameters\n3. Extra entries are ignored during binding\n\nPotential solutions:\n1. Use prepared statement introspection (like lib/ecto_libsql/native.ex)\n2. Implement a simple SQL parser that tracks quoted strings and comments\n3. Use a proper SQL parsing library (if one exists for Elixir/LibSQL)\n\nBenefits of fixing:\n- More robust parameter extraction\n- Handles edge cases correctly\n- Better alignment with execute path (which uses introspection)\n\nNote: This is only used in the query path (SELECT/EXPLAIN/WITH/RETURNING) where we bypass prepared statement introspection for performance. The execute path already uses the correct introspection approach.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-4tc","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Replace regex-based parameter extraction with SQL-aware parsing","updated_at":"2026-01-07T11:59:35Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"8553491b253f043095dae5b7a2df6f7a827ebeaca26904e6f703e090849c8a13","created_at":"2026-01-14T18:39:24Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Support Ecto's :duration type for storing time durations.\n\n**Options:**\n- Store as integer seconds (simple, efficient)\n- Store as ISO8601 duration string (e.g., PT1H30M, human-readable)\n\n**Implementation:**\n- Add loader to parse duration from storage format\n- Add dumper to convert Duration struct to storage format\n- Add tests for duration round-trips\n- Document in AGENTS.md\n\n**References:**\n- TYPE_LOADER_DUMPER_AUDIT.md suggests this enhancement\n- Ecto 3.x introduced :duration as a native type","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-4zg","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Add :duration type support","updated_at":"2026-01-14T18:39:24Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"14f06bb565661c0b25ff2a20d8c180a71c69caf3b9e82cd07b8a32a33720eec1","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1-2 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-53e","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:57:52Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"64fb18876678b5a0840bc1d38e5235a5d9107ed019f1bc6cfd3e66ed03017fe4","created_at":"2025-12-30T17:46:45Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add comprehensive security tests to verify connections cannot access each other's resources.\n\n**Context**: ecto_libsql implements ownership tracking (TransactionEntry.conn_id, cursor ownership, statement ownership) but needs comprehensive tests to verify security boundaries.\n\n**Security Boundaries to Test**:\n\n**1. Transaction Isolation**:\n```elixir\ntest \"connection A cannot access connection B's transaction\" do\n {:ok, conn_a} = connect(database: \"a.db\")\n {:ok, conn_b} = connect(database: \"b.db\")\n \n {:ok, trx_id} = begin_transaction(conn_a)\n \n # Should fail - transaction belongs to conn_a\n assert {:error, msg} = execute_with_transaction(conn_b, trx_id, \"SELECT 1\")\n assert msg =~ \"does not belong to this connection\"\nend\n```\n\n**2. Statement Isolation**:\n```elixir\ntest \"connection A cannot access connection B's prepared statement\" do\n {:ok, conn_a} = connect(database: \"a.db\")\n {:ok, conn_b} = connect(database: \"b.db\")\n \n {:ok, stmt_id} = prepare_statement(conn_a, \"SELECT 1\")\n \n # Should fail - statement belongs to conn_a\n assert {:error, msg} = execute_prepared(conn_b, stmt_id, [])\n assert msg =~ \"Statement not found\" or msg =~ \"does not belong\"\nend\n```\n\n**3. Cursor Isolation**:\n```elixir\ntest \"connection A cannot access connection B's cursor\" do\n {:ok, conn_a} = connect(database: \"a.db\")\n {:ok, conn_b} = connect(database: \"b.db\")\n \n {:ok, cursor_id} = declare_cursor(conn_a, \"SELECT 1\")\n \n # Should fail - cursor belongs to conn_a\n assert {:error, msg} = fetch_cursor(conn_b, cursor_id, 10)\n assert msg =~ \"Cursor not found\" or msg =~ \"does not belong\"\nend\n```\n\n**4. Savepoint Isolation**:\n```elixir\ntest \"connection A cannot access connection B's savepoint\" do\n {:ok, conn_a} = connect(database: \"a.db\")\n {:ok, conn_b} = connect(database: \"b.db\")\n \n {:ok, trx_id} = begin_transaction(conn_a)\n {:ok, _} = savepoint(conn_a, trx_id, \"sp1\")\n \n # Should fail - savepoint belongs to conn_a's transaction\n assert {:error, msg} = rollback_to_savepoint(conn_b, trx_id, \"sp1\")\n assert msg =~ \"does not belong to this connection\"\nend\n```\n\n**5. Concurrent Access Races**:\n```elixir\ntest \"concurrent cursor fetches are safe\" do\n {:ok, conn} = connect()\n {:ok, cursor_id} = declare_cursor(conn, \"SELECT * FROM large_table\")\n \n # Multiple processes try to fetch concurrently\n tasks = for _ \u003c- 1..10 do\n Task.async(fn -\u003e fetch_cursor(conn, cursor_id, 10) end)\n end\n \n results = Task.await_many(tasks)\n \n # Should not crash, should handle gracefully\n assert Enum.all?(results, fn r -\u003e match?({:ok, _}, r) or match?({:error, _}, r) end)\nend\n```\n\n**6. Process Crash Cleanup**:\n```elixir\ntest \"resources cleaned up when connection process crashes\" do\n # Start connection in separate process\n pid = spawn(fn -\u003e\n {:ok, conn} = connect()\n {:ok, trx_id} = begin_transaction(conn)\n {:ok, cursor_id} = declare_cursor(conn, \"SELECT 1\")\n \n # Store IDs for verification\n send(self(), {:ids, conn.conn_id, trx_id, cursor_id})\n \n # Wait to be killed\n Process.sleep(:infinity)\n end)\n \n receive do\n {:ids, conn_id, trx_id, cursor_id} -\u003e\n # Kill the process\n Process.exit(pid, :kill)\n Process.sleep(100)\n \n # Resources should be cleaned up (or marked orphaned)\n # Verify they can't be accessed\n end\nend\n```\n\n**7. Connection Pool Isolation**:\n```elixir\ntest \"pooled connections are isolated\" do\n # Get two connections from pool\n conn1 = get_pooled_connection()\n conn2 = get_pooled_connection()\n \n # Each should have independent resources\n {:ok, trx1} = begin_transaction(conn1)\n {:ok, trx2} = begin_transaction(conn2)\n \n # Should not interfere\n assert trx1 != trx2\n \n # Commit conn1, should not affect conn2\n :ok = commit_transaction(conn1, trx1)\n assert is_in_transaction?(conn2, trx2)\nend\n```\n\n**Implementation**:\n\n1. **Create test file** (test/security_test.exs):\n - Transaction isolation tests\n - Statement isolation tests\n - Cursor isolation tests\n - Savepoint isolation tests\n - Concurrent access tests\n - Cleanup tests\n - Pool isolation tests\n\n2. **Add stress tests** for concurrent access patterns\n\n3. **Add fuzzing** for edge cases\n\n**Files**:\n- NEW: test/security_test.exs\n- Reference: FEATURE_CHECKLIST.md line 290-310\n- Reference: LIBSQL_FEATURE_COMPARISON.md section 4\n\n**Acceptance Criteria**:\n- [ ] Transaction isolation verified\n- [ ] Statement isolation verified\n- [ ] Cursor isolation verified\n- [ ] Savepoint isolation verified\n- [ ] Concurrent access safe\n- [ ] Resource cleanup verified\n- [ ] Pool isolation verified\n- [ ] All tests pass consistently\n- [ ] No race conditions detected\n\n**Security Guarantees**:\nAfter these tests pass, we can guarantee:\n- Connections cannot access each other's transactions\n- Connections cannot access each other's prepared statements\n- Connections cannot access each other's cursors\n- Savepoints are properly scoped to owning transaction\n- Concurrent access is thread-safe\n- Resources are cleaned up on connection close\n\n**References**:\n- LIBSQL_FEATURE_COMPARISON.md section \"Error Handling for Edge Cases\" line 290-310\n- Current implementation: TransactionEntry.conn_id ownership tracking\n\n**Priority**: P2 - Important for security guarantees\n**Effort**: 2 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-5ef","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add Cross-Connection Security Tests","updated_at":"2026-01-12T11:57:52Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T14:46:48Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"11276b3ecba8059efc587c35ff85f004c2b127c564c804ef69f16957044f6956","created_at":"2026-01-13T11:53:07Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add comprehensive tests in test/ecto_integration_test.exs for all type encodings: 1) UUID structs in query params, 2) Boolean values in raw queries, 3) Atom :null handling, 4) Nested structures (document expected failure), 5) Edge cases like empty strings, large numbers, special characters. Tests should verify both successful encoding and appropriate error messages for unsupported types.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-5mr","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Investigate and add comprehensive type encoding tests","updated_at":"2026-01-13T14:46:48Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T21:30:40Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"02e24d46bac5f3ee5950be26402c951e6e372eab90cca0ef8521547fd8f6d0f0","created_at":"2026-01-08T12:55:42Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Current Rust tests (integration_tests.rs) focus on happy path. Need tests for error scenarios to verify the Rust layer returns errors instead of panicking.\n\nMissing tests:\n- Invalid connection ID → should return error (not panic)\n- Invalid statement ID → should return error (not panic)\n- Invalid transaction ID → should return error (not panic)\n- Invalid cursor ID → should return error (not panic)\n- Parameter count mismatch → should return error\n- Resource exhaustion scenarios\n\nThis is important for verifying that the Rust layer doesn't crash the BEAM VM on invalid inputs.\n\nLocation: native/ecto_libsql/src/tests/error_handling_tests.rs (new file)\n\nEffort: 1-2 hours\nImpact: Robustness, baseline for Elixir error tests, verifies no panic on invalid inputs","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-5nw","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add error handling tests to Rust NIF layer","updated_at":"2026-01-08T21:30:40Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"19992605d55f4611ac37ca576329534bbaa4ea60751f39c473210b6d8e8fb70a","created_at":"2026-01-11T16:55:19Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Several tests are skipped due to inherent SQLite/LibSQL limitations (not missing features):\n\n## Skipped Tests\n\n### 1. ecto_sql_transaction_compat_test.exs:218,228\n**Tag**: `@tag :sqlite_concurrency_limitation`\n**Tests**: \n- 'rollback is per repository connection'\n- 'transactions are not shared across processes'\n\n**Reason**: SQLite uses file-level locking rather than row-level locking like PostgreSQL. This means cross-process transaction isolation works differently than in PostgreSQL's Ecto adapter.\n\n### 2. ecto_sql_compatibility_test.exs:86\n**Tag**: `@tag :skip`\n**Test**: 'fragmented schemaless types'\n\n**Reason**: SQLite does not preserve type information in schemaless queries the way PostgreSQL does. The `type(fragment(...), :integer)` syntax doesn't work the same way.\n\n## Action Items\n\n- [ ] Add `@tag :sqlite_limitation` tag to these tests for clarity\n- [ ] Add documentation in README or LIMITATIONS.md explaining these differences\n- [ ] Ensure test comments explain WHY they are skipped\n\nThese are NOT bugs to fix - they are architectural differences between SQLite and PostgreSQL that users should be aware of.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-6r5","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Document SQLite/LibSQL Known Limitations in Skipped Tests","updated_at":"2026-01-11T16:55:19Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T12:05:25Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f46158c58dad9ab0493ee21e1a0ddf276b5c68481b005a49a49cb7f0f2919997","created_at":"2026-01-13T11:57:42Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"PROBLEM: column_default/1 in lib/ecto/adapters/libsql/connection.ex crashes with FunctionClauseError on unexpected types (e.g., empty maps {} from Oban migrations). SOLUTION: Add catch-all clause 'defp column_default(_), do: \"\"' at end of function definition to gracefully handle unexpected types instead of crashing. IMPACT: Blocks Oban migration creation. REFERENCE: See Fix 2 in feedback document.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-6yg","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Fix column_default/1 crash on unexpected types","updated_at":"2026-01-13T12:05:25Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"3905b117712ada2271114ba1276f0a13d2e957476a7c2901501d6902abea8327","created_at":"2025-12-30T17:43:58Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"LibSQL-specific extension for modifying columns. Syntax: ALTER TABLE table_name ALTER COLUMN column_name TO column_name TYPE constraints. Can modify column types, constraints, DEFAULT values. Can add/remove foreign key constraints.\n\nThis would enable better migration support for column alterations that standard SQLite doesn't support.\n\nDesired API:\n alter table(:users) do\n modify :email, :string, null: false # Actually works in libSQL!\n end\n\nEffort: 3-4 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-6zu","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"ALTER TABLE Column Modifications (libSQL Extension)","updated_at":"2026-01-05T14:41:54Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:56:46Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"8f2330a4eb4007a690e1364eb151bdc6daed08c8b8de261abd368823e6806e4b","created_at":"2026-01-14T13:48:35Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"ecto_libsql's INSERT RETURNING clause doesn't properly populate the returned Elixir struct. This breaks Oban integration and other features that rely on RETURNING. Identified through comparative testing with ecto_sqlite3 which works correctly.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713","is_template":0,"issue_type":"epic","last_activity":null,"metadata":"{}","mol_type":"","notes":"Fixed multiple issues preventing Oban integration:\n\n1. Added JSON/MAP field deserialization (el-713.6) ✅\n - json_decode function to parse JSON strings to maps\n - json_array_decode for array fields\n - loaders for :json, :map, and {:array, _} types\n\n2. Fixed DateTime (UTC) encoding (el-713.3) ✅\n - Added datetime_encode for %DateTime{} structs\n - Previously only supported NaiveDateTime\n\n3. Added Array type support (new finding) ✅\n - array_encode for {:array, _} types\n - Arrays now properly persist to JSON\n\n4. Created comprehensive tests (el-713.4) ✅\n - test/ecto_returning_test.exs: Basic RETURNING test\n - test/oban_like_test.exs: Complex schema with JSON/MAP\n - test/type_compatibility_test.exs: All field type round-trip\n - test/oban_integration_test.exs: Full Oban workflow simulation\n\nAll tests pass. Oban integration now works correctly with ecto_libsql.","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Fix INSERT RETURNING Struct Mapping Issues","updated_at":"2026-01-14T13:56:46Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:53:35Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f858e194eb620d3e4cea415afb1c31b5eb3bb3705bdaf42f3c3ba25df3147463","created_at":"2026-01-14T13:48:41Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"When executing INSERT RETURNING, the result rows are retrieved correctly but the Ecto struct mapping fails. The struct fields (especially id, timestamps) come back as nil even though the database persists the data correctly.\n\nExample:\n- INSERT with RETURNING should return: %Job{id: 123, inserted_at: ~U[...], ...}\n- Currently returns: %Job{id: nil, inserted_at: nil, ...}\n\nTests: Oban integration tests all fail due to this.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713.1","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"Investigation shows RETURNING clause IS working correctly at the struct level. Created test ecto_returning_test.exs - both ID and timestamps are properly populated. Need to investigate Oban-specific issue or check if comparative analysis result is specific to Oban configuration.","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"RETURNING clause doesn't return struct with populated fields","updated_at":"2026-01-14T13:53:35Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:54:47Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"5a787ddeafde1a431a428b90f9ff40abfb28a510e3be8d891de47d1d4534040e","created_at":"2026-01-14T13:48:46Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"The RETURNING clause results aren't being correctly mapped to struct fields. Need to:\n\n1. Verify RETURNING SQL is being executed (should generate 'RETURNING id, col1, col2, ...')\n2. Check that result columns are matched to struct field names\n3. Ensure column order matches struct field order\n4. Verify Rust-to-Elixir struct marshaling\n\nLikely location: native/src/query.rs - inspect handle_returning or similar","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713.2","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"Investigation determined this is not the issue. RETURNING clause properly maps columns to fields and RETURNING execution is correct. The actual issue was JSON/MAP deserialization (el-713.6) which has been fixed.","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Result column to struct field mapping broken in RETURNING","updated_at":"2026-01-14T13:54:47Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:54:56Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"0cbe571f11477707a48c9a705f132aa9db79fd74eac62a00448c5af6c08f5bd3","created_at":"2026-01-14T13:48:52Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"When using RETURNING, timestamp columns (inserted_at, updated_at, etc.) come back as nil in the returned struct. \n\nThe issue may be:\n1. Timestamp conversion from SQLite format (ISO8601 string) to Elixir DateTime failing\n2. Timestamp fields not being included in the RETURNING clause\n3. Type conversion issue during Rust-to-Elixir marshaling\n\nThis prevents proper struct population since timestamps are required fields in Ecto schemas.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713.3","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"Investigation shows timestamp fields are properly populated in RETURNING results. Tests confirm inserted_at and updated_at are correctly converted from ISO8601 strings to NaiveDateTime structs.","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Timestamp fields lost in RETURNING clause results","updated_at":"2026-01-14T13:54:56Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:55:07Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f1e015a9a52e56f71f3fd11059e9917e59612ff8d1b5df1b3fbcb6022e9a9940","created_at":"2026-01-14T13:48:58Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Create tests that verify INSERT RETURNING works correctly with:\n\n1. Basic inserts returning single field\n2. Inserts returning multiple fields\n3. Inserts with timestamps (inserted_at, updated_at)\n4. Inserts with various field types (strings, integers, floats, booleans, etc)\n5. Comparison test against ecto_sqlite3 to verify behavior matches\n6. Oban integration test - verify Oban.insert() returns complete struct\n\nTest output should verify that returned struct has:\n- Correct ID value (not nil)\n- Correct timestamp values (not nil)\n- All fields populated as expected\n\nReference: /COMPARATIVE_TESTING_ANALYSIS.md shows these tests failing","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713.4","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"Comprehensive test cases added: ecto_returning_test.exs (basic RETURNING), oban_like_test.exs (complex schema with JSON/MAP). Both tests pass and verify RETURNING struct mapping works correctly.","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add comprehensive test cases for RETURNING clause","updated_at":"2026-01-14T13:55:07Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:57:48Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"4a62a875a5492d1e2b31acf57e825566a3236d76c1b1805bd7053a05cd7b50e1","created_at":"2026-01-14T13:49:03Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Study how ecto_sqlite3 handles RETURNING clause:\n\n1. Locate ecto_sqlite3's RETURNING handling code\n2. Compare SQL generation (do we generate the same RETURNING clause?)\n3. Compare result column extraction\n4. Compare struct building\n5. Compare timestamp conversion logic\n\nDocument the differences and identify which parts of ecto_libsql need fixing.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713.5","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"Analysis complete. Investigation showed RETURNING clause itself is implemented correctly in ecto_libsql. The issues identified in comparative testing were actually:\n\n1. JSON/MAP field deserialization (fixed in el-713.6)\n2. DateTime encoding missing (fixed in type compatibility work)\n3. Array type encoding missing (fixed in type compatibility work)\n\necto_sqlite3 and ecto_libsql both handle RETURNING the same way - the difference in test results was due to these type handling issues, not RETURNING implementation.","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Compare RETURNING implementation with ecto_sqlite3","updated_at":"2026-01-14T13:57:48Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T13:54:25Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b0933ae39248c84deb5ec89ca40057feb2dc3e25ae85172c701b3146abc9193d","created_at":"2026-01-14T13:53:43Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"When querying JSON/MAP fields from the database, they are returned as JSON strings but not deserialized to Elixir maps.\n\nExample:\n- INSERT with map args works (RETURNING properly shows the map)\n- SELECT/GET returns the JSON string instead of decoded map\n- Error: 'cannot load \"{\\\"key\\\":\\\"value\\\"}\" as type :map'\n\nThis is a loader issue, not a RETURNING issue. Maps are stored as JSON in SQLite but need to be decoded on read.\n\nNeed to add loaders for :map and :json types in lib/ecto/adapters/libsql.ex","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-713.6","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"Fixed by adding json_decode and json_array_decode functions to handle JSON/MAP deserialization in Ecto.Adapters.LibSql.loaders/2","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":0,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"JSON/MAP fields not decoded when reading from database","updated_at":"2026-01-14T13:54:25Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"ee3834a9c8e5b0fe30112e2394a13e5db350185c8d617dea005f792f0962647b","created_at":"2025-12-30T17:35:52Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Partial - Extension loading works, but no schema helpers. libSQL 3.45.1 has comprehensive FTS5 extension with advanced features: phrase queries, term expansion, ranking, tokenisation, custom tokenisers.\n\nDesired API:\n create table(:posts, fts5: true) do\n add :title, :text, fts_weight: 10\n add :body, :text\n add :author, :string, fts_indexed: false\n end\n\n from p in Post, where: fragment(\"posts MATCH ?\", \"search terms\"), order_by: [desc: fragment(\"rank\")]\n\nPRIORITY: Recommended as #7 in implementation order - major feature.\n\nEffort: 5-7 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-7t8","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Full-Text Search (FTS5) Schema Integration","updated_at":"2025-12-30T17:43:19Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"8dd275938a66a0dd0416871fae25378273021934eba31cf3e266ecb84df1222b","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"replication_integration_test.exs (existing), savepoint_test.exs (existing)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-7ux","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Related","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"ae806c08eb1a54a6c6305a7904abe5ff0ff83ce0bfdb0ecbb1a0a86711fa268f","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Extend test/json_helpers_test.exs with JSONB-specific scenarios","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-8fh","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test File","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"761a4dde3c54df9467db57acc865962cc806b248b66cc90ef4a2ee1c9163ba2e","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"3-4 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-8m1","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T12:05:45Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"9c58b194194ce8b19c5c75f47ef304c4116e43cea9a137ae9127d99c275f2946","created_at":"2026-01-13T11:58:03Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add generic tests (not Oban-specific) to verify: 1) Map parameter encoding in test/ecto_integration_test.exs - test plain maps (not structs) are encoded to JSON before NIF calls, test nested maps, test mixed parameter types. 2) column_default/1 edge cases in test/ecto_migration_test.exs - test with nil, booleans, strings, numbers, fragments, AND unexpected types like empty maps {}. These are generic adapter features that happen to be triggered by Oban but are not Oban-specific functionality.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-8wn","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add tests for map parameter encoding and column_default edge cases","updated_at":"2026-01-13T12:05:45Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:26Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"8a6ad04c77de4f29cbec5389a46d0f7c09c5f674b00032bca91d132e61233cc5","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Memory usage stays constant while streaming (not loading all into memory)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-94l","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test Scenarios","updated_at":"2026-01-12T11:58:26Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"08daaaf674b6105ac90349dd292d96ba2925068fc5d171bb2b546d0d4e485c9e","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"test/savepoint_replication_test.exs (new file)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-96d","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test File","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c4a09ea8bf2598d89a78b055dd48b1bff1979f88ea680d66f9be40fb2e302253","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"**Rust Tests (native/ecto_libsql/src/tests/)** - Low-level correctness","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-9bc","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Documentation to Create","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"55c88a20c3faec3a9d5469627f0933e59cdca678ebf46f15bb5d11dcca537bc7","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"2-3 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-9c6","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T14:30:26Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2b26e230643c8438f108340af5d69b9ab32512e0a71e2a361642b655587278de","created_at":"2026-01-14T14:23:23Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Some SQLite query features are not supported: `selected_as()` / GROUP BY with aliases and `identifier()` fragments. These appear to be SQLite database limitations, not adapter issues.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-9dx","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"SQLite query feature limitations documentation","updated_at":"2026-01-14T14:30:26Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"d17a0357803329034fedc37394b3e72733551c0265736ae040116292dc69a333","created_at":"2026-01-01T22:55:01Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-9j1","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Optimise LRU cache eviction for large caches","updated_at":"2026-01-01T22:55:01Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-05T15:00:09Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"bbddfe64b84a7f2348b540bc8ef24ce79e51e7164762a46a6ef193822aecda11","created_at":"2025-12-30T17:43:58Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"New in libSQL 3.45. Binary encoding of JSON for faster processing. 5-10% smaller than text JSON. Backwards compatible with text JSON - automatically converted between formats. All JSON functions work with both text and JSONB.\n\nCould provide performance benefits for JSON-heavy applications. May require new Ecto type or option.\n\nEffort: 2-3 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-a17","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"JSONB Binary Format Support","updated_at":"2026-01-05T15:00:09Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:52:44Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"eb045c0c60d83aa681e7bb9136704ba487b9937031a8d5149decb73d50c2a2d5","created_at":"2025-12-30T17:43:31Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Refactor cursor implementation to use true streaming instead of loading all rows into memory.\n\n**Problem**: Current cursor implementation loads ALL rows into memory upfront (lib.rs:1074-1100), then paginates through the buffer. This causes high memory usage for large datasets.\n\n**Current (Memory Issue)**:\n```rust\n// MEMORY ISSUE (lib.rs:1074-1100):\nlet rows = query_result.into_iter().collect::\u003cVec\u003c_\u003e\u003e(); // ← Loads everything!\n```\n\n**Impact**:\n- ✅ Works fine for small/medium datasets (\u003c 100K rows)\n- ⚠️ High memory usage for large datasets (\u003e 1M rows)\n- ❌ Cannot stream truly large datasets (\u003e 10M rows)\n\n**Example**:\n```elixir\n# Current: Loads 1 million rows into RAM\ncursor = Repo.stream(large_query)\nEnum.take(cursor, 100) # Only want 100, but loaded 1M!\n\n# Desired: True streaming, loads on-demand\ncursor = Repo.stream(large_query)\nEnum.take(cursor, 100) # Only loads 100 rows\n```\n\n**Fix Required**:\n1. Refactor to use libsql Rows async iterator\n2. Stream batches on-demand instead of loading all upfront\n3. Store iterator state in cursor registry\n4. Fetch next batch when cursor is fetched\n5. Update CursorData structure to support streaming\n\n**Files**:\n- native/ecto_libsql/src/cursor.rs (major refactor)\n- native/ecto_libsql/src/models.rs (update CursorData struct)\n- test/ecto_integration_test.exs (add streaming tests)\n- NEW: test/performance_test.exs (memory usage benchmarks)\n\n**Acceptance Criteria**:\n- [ ] Cursors stream batches on-demand\n- [ ] Memory usage stays constant regardless of result size\n- [ ] Can stream 10M+ rows without OOM\n- [ ] Performance: Streaming vs loading all benchmarked\n- [ ] All existing cursor tests pass\n- [ ] New tests verify streaming behaviour\n\n**Test Requirements**:\n```elixir\ntest \"cursor streams 1M rows without loading all into memory\" do\n # Insert 1M rows\n # Declare cursor\n # Verify memory usage \u003c 100MB while streaming\n # Verify all rows eventually fetched\nend\n```\n\n**References**:\n- LIBSQL_FEATURE_MATRIX_FINAL.md section 9\n- FEATURE_CHECKLIST.md Cursor Methods\n\n**Priority**: P1 - Critical for large dataset processing\n**Effort**: 4-5 days (major refactor)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-aob","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Implement True Streaming Cursors","updated_at":"2026-01-17T08:52:44Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"63a327122851ec6b8258f6bfbb9ac239990708c03058935720c69f83667f1aed","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1-2 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-av5","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-08T21:34:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"64872c309b87591fa9ca0484eaaf0b271374bf0a2b60a3192b13f997736f217f","created_at":"2026-01-14T18:39:44Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Consider adding helper functions for common type coercion scenarios.\n\n**Common Edge Cases:**\n- Float to Decimal conversion (when query returns REAL instead of expected DECIMAL)\n- String to number coercion for loose user input\n- NULL vs empty value handling (e.g., empty string vs nil)\n- Boolean-like values ('yes'/'no', 't'/'f', etc.)\n\n**Potential Implementation:**\n- Add optional coercion module with explicit helpers\n- Document when to use vs relying on Ecto's built-in casting\n- Keep default behavior strict (explicit over implicit)\n\n**References:**\n- TYPE_LOADER_DUMPER_AUDIT.md suggests this as optional enhancement\n- Similar to ecto_enum approach: explicit helpers when needed\n\n**Considerations:**\n- May not be needed - Ecto.Changeset.cast/3 handles most cases\n- Gather user feedback first before implementing\n- Could be separate library if there's demand","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-b21","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Add type coercion helpers for common edge cases","updated_at":"2026-01-14T18:39:44Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-14T14:30:19Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b03b7b039653d30d33e4ba7a8de0278cc57165cfd37b877929c1d073c366ab1b","created_at":"2026-01-14T14:23:23Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Tests within the same module are not properly isolated. Multiple tests accumulate data affecting each other. Test modules currently share the same database file within a module run.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-bro","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test isolation in compatibility tests","updated_at":"2026-01-14T14:30:19Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c3e6befd32b70f9296a58820b60438d5dbf4d167756b48c2d77640ea20b9a7a8","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"2-3 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-bun","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-08T21:34:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c95570a910f3e5958bec43e3040059e0378a7e0ecdf1d7220e5d0e12e2ba97ef","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Identify redundant tests (basic type binding in Elixir)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-c05","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Work Required","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T12:56:39Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"aadb03cdc4dd10fc790b0c6c13c05b01b073de9b858a6e2c2ece3da0ed3aec28","created_at":"2026-01-08T12:55:19Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"These two test files have significant overlap in testing prepared statements.\n\nstatement_features_test.exs (836 lines): Tests column_count, column_name, parameter_count, parameter_name, reset_stmt, get_stmt_columns, error handling\n\nprepared_statement_test.exs (464 lines): Tests preparation, execution, introspection, lifecycle, error handling\n\nDuplicate tests exist for column_count, column_name, parameter_count, and error handling.\n\nstatement_features_test.exs has newer tests (reset_stmt, get_stmt_columns, parameter_name) that should be in the canonical prepared_statement_test.exs.\n\nAction:\n1. Copy unique tests from statement_features_test.exs into prepared_statement_test.exs\n2. Reorganize test groups for clarity\n3. Delete statement_features_test.exs\n\nThis reduces test file count and eliminates duplication.\n\nEffort: 30 minutes","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-c7g","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Merge statement_features_test.exs into prepared_statement_test.exs","updated_at":"2026-01-08T12:56:39Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"e27417126eb3a917d31a8c12c0d1a85fd3b3307a8ef3b443781cb5c696a55d14","created_at":"2026-01-08T21:34:57Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Create comprehensive performance benchmarks to track ecto_libsql performance.\n\nFrom TEST_AUDIT_REPORT.md item 9 - no performance benchmarks currently exist.\n\n**Benchmark Categories to Implement**:\n1. Prepared statement performance (100 executions)\n2. Cursor streaming memory usage (1M rows)\n3. Concurrent connections throughput (10, 50, 100 conns)\n4. Transaction throughput (ops/sec)\n5. Batch operations performance (manual vs native vs transactional)\n6. Statement cache performance (hit rate, eviction)\n7. Replication sync performance (time per N changes)\n\n**Files to Create**:\n- benchmarks/prepared_statements_bench.exs\n- benchmarks/cursor_streaming_bench.exs\n- benchmarks/concurrent_connections_bench.exs\n- benchmarks/transactions_bench.exs\n- benchmarks/batch_operations_bench.exs\n- benchmarks/statement_cache_bench.exs\n- benchmarks/replication_bench.exs\n\n**Implementation**: Add benchee deps, create mix alias, document baselines in PERFORMANCE.md\n\n**Estimated Effort**: 2-3 days\n\n**Impact**: Track performance across versions, validate improvements, identify bottlenecks","design":"","due_at":null,"ephemeral":0,"estimated_minutes":960,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-cbv","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Add performance benchmark tests","updated_at":"2026-01-08T21:34:57Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T21:52:54Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"abbcecba47d7f797e6218a42975434ddf36f1222f8d278f7efe1522116a862a3","created_at":"2026-01-08T21:34:40Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add tests for savepoint behavior when used with replication/remote sync.\n\nFrom TEST_AUDIT_REPORT.md item 9: 'Savepoint + replication interaction' - identified as under-tested.\n\n**Test Scenarios**:\n1. Savepoints in replica mode with sync\n2. Savepoint rollback synchronizes with remote\n3. Nested savepoints with remote sync\n4. Savepoints with failed sync scenarios\n5. Concurrent savepoints don't interfere\n6. Savepoints across sync boundaries\n\n**Test File**: test/savepoint_replication_test.exs (new)\n\n**Estimated Effort**: 3-4 hours\n\n**Related**: replication_integration_test.exs (existing), savepoint_test.exs (existing)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":210,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-crt","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test savepoint + replication interaction","updated_at":"2026-01-08T21:52:54Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T21:41:12Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"1d3f2b4158209d0411b7d7e3036cb636c59970a346826bb0b36e28997c4ae567","created_at":"2026-01-08T21:34:51Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add comprehensive error handling tests to Rust NIF layer to verify it returns errors instead of panicking.\n\nFrom TEST_AUDIT_REPORT.md item 6: 'Add Rust Tests for Error Scenarios' - critical for BEAM stability.\n\n**Test Scenarios**:\n1. Invalid resource IDs (connection, statement, transaction, cursor)\n2. Parameter validation (count mismatch, type mismatch)\n3. Constraint violations (NOT NULL, UNIQUE, FOREIGN KEY, CHECK)\n4. Transaction errors (operations after commit, double rollback)\n5. Query syntax errors (invalid SQL, non-existent table/column)\n6. Resource exhaustion (too many prepared statements/cursors)\n\n**Test File**: native/ecto_libsql/src/tests/error_handling_tests.rs (new)\n\n**Estimated Effort**: 1-2 hours\n\n**Impact**: Verifies Rust layer doesn't crash on invalid inputs, critical for BEAM stability (no panics allowed)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":90,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-d3o","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add Rust tests for error scenarios","updated_at":"2026-01-08T21:41:12Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:57:16Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"0a2945d38bc2e649b8d3b0d55618f1c6c75e43fe755e04ae896fc0d718460260","created_at":"2026-01-08T21:34:35Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add tests for connection recovery and resilience after network/connection failures.\n\nFrom TEST_AUDIT_REPORT.md item 9: 'Recovery from connection errors' - identified as under-tested.\n\n**Test Scenarios**:\n1. Connection loss during query execution\n2. Automatic reconnection on stale/idle connections\n3. Retry logic with backoff for transient errors\n4. Connection timeout handling\n5. Network partition recovery\n6. Connection state after error (no partial transactions)\n\n**Test File**: test/connection_recovery_test.exs (new)\n\n**Estimated Effort**: 2-3 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":150,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-d63","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test connection error recovery","updated_at":"2026-01-12T11:57:16Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T11:58:47Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a27e96782dd2a9893b7845f2e7c9635212d9cb4670a138c6d9325858def5d9cc","created_at":"2026-01-13T11:57:42Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add documentation for Oban integration to README.md and AGENTS.md. Must include: 1) Migration setup requiring explicit SQLite3 migrator (Oban.Migration.up(version: 1, migrator: Oban.Migrations.SQLite)), 2) Why migrator must be specified (Oban doesn't auto-detect ecto_libsql), 3) Note that ecto_libsql is fully SQLite-compatible. Add example migration code and note in compatibility/integrations section.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-dcb","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Document Oban integration in README and AGENTS.md","updated_at":"2026-01-13T11:58:47Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:52:16Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"8e3041735017bf11647dc19c876e9a9214ec3aa476d047bdb52f1c1eab14342f","created_at":"2025-12-30T17:45:42Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add max_write_replication_index() NIF to track maximum write frame for replication monitoring.\n\n**Context**: The libsql API provides max_write_replication_index() for tracking the highest frame number that has been written. This is useful for monitoring replication lag and coordinating replica sync.\n\n**Current Status**: \n- ⚠️ LibSQL 0.9.29 provides the API\n- ⚠️ Not yet wrapped in ecto_libsql\n- Identified in LIBSQL_FEATURE_MATRIX_FINAL.md section 5\n\n**Use Case**:\n```elixir\n# Primary writes data\n{:ok, _} = Repo.query(\"INSERT INTO users (name) VALUES ('Alice')\")\n\n# Track max write frame on primary\n{:ok, max_write_frame} = EctoLibSql.Native.max_write_replication_index(primary_state)\n\n# Sync replica to that frame\n:ok = EctoLibSql.Native.sync_until(replica_state, max_write_frame)\n\n# Now replica is caught up to primary's writes\n```\n\n**Benefits**:\n- Monitor replication lag accurately\n- Coordinate multi-replica sync\n- Ensure read-after-write consistency\n- Track write progress for analytics\n\n**Implementation Required**:\n\n1. **Add NIF** (native/ecto_libsql/src/replication.rs):\n ```rust\n /// Get the maximum replication index that has been written.\n ///\n /// # Returns\n /// - {:ok, frame_number} - Success\n /// - {:error, reason} - Failure\n #[rustler::nif(schedule = \"DirtyIo\")]\n pub fn max_write_replication_index(conn_id: \u0026str) -\u003e NifResult\u003cu64\u003e {\n let conn_map = safe_lock(\u0026CONNECTION_REGISTRY, \"max_write_replication_index\")?;\n let conn_arc = conn_map\n .get(conn_id)\n .ok_or_else(|| rustler::Error::Term(Box::new(\"Connection not found\")))?\n .clone();\n drop(conn_map);\n\n let result = TOKIO_RUNTIME.block_on(async {\n let conn_guard = safe_lock_arc(\u0026conn_arc, \"max_write_replication_index conn\")\n .map_err(|e| format!(\"{:?}\", e))?;\n \n conn_guard\n .db\n .max_write_replication_index()\n .await\n .map_err(|e| format!(\"Failed to get max write replication index: {:?}\", e))\n })?;\n\n Ok(result)\n }\n ```\n\n2. **Add Elixir wrapper** (lib/ecto_libsql/native.ex):\n ```elixir\n @doc \"\"\"\n Get the maximum replication index that has been written.\n \n Returns the highest frame number that has been written to the database.\n Useful for tracking write progress and coordinating replica sync.\n \n ## Examples\n \n {:ok, max_frame} = EctoLibSql.Native.max_write_replication_index(state)\n :ok = EctoLibSql.Native.sync_until(replica_state, max_frame)\n \"\"\"\n def max_write_replication_index(_conn_id), do: :erlang.nif_error(:nif_not_loaded)\n \n def max_write_replication_index_safe(%EctoLibSql.State{conn_id: conn_id}) do\n case max_write_replication_index(conn_id) do\n {:ok, frame} -\u003e {:ok, frame}\n {:error, reason} -\u003e {:error, reason}\n end\n end\n ```\n\n3. **Add tests** (test/replication_integration_test.exs):\n ```elixir\n test \"max_write_replication_index tracks writes\" do\n {:ok, state} = connect()\n \n # Initial max write frame\n {:ok, initial_frame} = EctoLibSql.Native.max_write_replication_index(state)\n \n # Perform write\n {:ok, _, _, state} = EctoLibSql.handle_execute(\n \"INSERT INTO test (data) VALUES (?)\",\n [\"test\"], [], state\n )\n \n # Max write frame should increase\n {:ok, new_frame} = EctoLibSql.Native.max_write_replication_index(state)\n assert new_frame \u003e initial_frame\n end\n ```\n\n**Files**:\n- native/ecto_libsql/src/replication.rs (add NIF)\n- lib/ecto_libsql/native.ex (add wrapper)\n- test/replication_integration_test.exs (add tests)\n- AGENTS.md (update API docs)\n\n**Acceptance Criteria**:\n- [ ] max_write_replication_index() NIF implemented\n- [ ] Safe wrapper in Native module\n- [ ] Tests verify frame number increases on writes\n- [ ] Tests verify frame number coordination\n- [ ] Documentation updated\n- [ ] API added to AGENTS.md\n\n**Dependencies**:\n- Related to el-g5l (Replication Integration Tests)\n- Should be tested together\n\n**References**:\n- LIBSQL_FEATURE_MATRIX_FINAL.md section 5 (line 167)\n- libsql API: db.max_write_replication_index()\n\n**Priority**: P1 - Important for replication monitoring\n**Effort**: 0.5-1 day (straightforward NIF addition)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-djv","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Implement max_write_replication_index() NIF","updated_at":"2026-01-17T08:52:16Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T21:43:45Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"12b3a9b375804dfa006e757baa1aa9e062ef2d1dddae9defa967922e0b32652b","created_at":"2026-01-08T21:34:25Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Implement comprehensive tests for cursor streaming behavior with large result sets (1M+).\n\nFrom TEST_AUDIT_REPORT.md item 9: 'Large result sets with streaming' - identified as under-tested.\n\n**Test Scenarios**:\n1. Memory usage stays constant while streaming (not loading all into memory)\n2. Cursor batch fetching with different batch sizes (100, 1000, 10000 rows)\n3. Cursor lifecycle (declare → fetch → close)\n4. Streaming 100K, 1M, and 10M row datasets without OOM\n5. Cursors with WHERE clause filtering on large datasets\n\n**Test File**: test/cursor_streaming_test.exs (new)\n\n**Estimated Effort**: 2-3 hours\n\n**Related**: el-aob (Implement True Streaming Cursors - feature)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":150,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-doo","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test cursor streaming with large result sets","updated_at":"2026-01-08T21:43:45Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"6aaa95ed01bec2ed797259f369a385a49b0baed6dc5c9879c2ed1b0c7d17b53f","created_at":"2025-12-30T17:46:15Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Create comprehensive performance benchmarks to track ecto_libsql performance and identify bottlenecks.\n\n**Context**: No performance benchmarks exist. Need to establish baselines and track performance across versions. Critical for validating performance improvements (like statement reset fix).\n\n**Benchmark Categories**:\n\n**1. Prepared Statement Performance**:\n```elixir\n# Measure impact of statement re-preparation bug\nbenchmark \"prepared statement execution\" do\n stmt = prepare(\"INSERT INTO bench VALUES (?, ?)\")\n \n # Before fix: ~30-50% slower\n # After fix: baseline\n Benchee.run(%{\n \"100 executions\" =\u003e fn -\u003e \n for i \u003c- 1..100, do: execute(stmt, [i, \"data\"])\n end\n })\nend\n```\n\n**2. Cursor Streaming Memory**:\n```elixir\nbenchmark \"cursor memory usage\" do\n # Current: Loads all into memory\n # After streaming fix: Constant memory\n \n cursor = declare_cursor(\"SELECT * FROM large_table\")\n \n :erlang.garbage_collect()\n {memory_before, _} = :erlang.process_info(self(), :memory)\n \n Enum.take(cursor, 100)\n \n {memory_after, _} = :erlang.process_info(self(), :memory)\n memory_used = memory_after - memory_before\n \n # Assert memory \u003c 10MB for 1M row table\n assert memory_used \u003c 10_000_000\nend\n```\n\n**3. Concurrent Connections**:\n```elixir\nbenchmark \"concurrent connections\" do\n Benchee.run(%{\n \"10 connections\" =\u003e fn -\u003e parallel_queries(10) end,\n \"50 connections\" =\u003e fn -\u003e parallel_queries(50) end,\n \"100 connections\" =\u003e fn -\u003e parallel_queries(100) end,\n })\nend\n```\n\n**4. Transaction Throughput**:\n```elixir\nbenchmark \"transaction throughput\" do\n Benchee.run(%{\n \"1000 transactions/sec\" =\u003e fn -\u003e\n for i \u003c- 1..1000 do\n Repo.transaction(fn -\u003e\n Repo.query(\"INSERT INTO bench VALUES (?)\", [i])\n end)\n end\n end\n })\nend\n```\n\n**5. Batch Operations**:\n```elixir\nbenchmark \"batch operations\" do\n queries = for i \u003c- 1..1000, do: \"INSERT INTO bench VALUES (\\#{i})\"\n \n Benchee.run(%{\n \"manual batch\" =\u003e fn -\u003e execute_batch(queries) end,\n \"native batch\" =\u003e fn -\u003e execute_batch_native(queries) end,\n \"transactional batch\" =\u003e fn -\u003e execute_transactional_batch(queries) end,\n })\nend\n```\n\n**6. Statement Cache Performance**:\n```elixir\nbenchmark \"statement cache\" do\n Benchee.run(%{\n \"1000 unique statements\" =\u003e fn -\u003e\n for i \u003c- 1..1000 do\n prepare(\"SELECT * FROM bench WHERE id = \\#{i}\")\n end\n end\n })\nend\n```\n\n**7. Replication Sync Performance**:\n```elixir\nbenchmark \"replica sync\" do\n # Write to primary\n for i \u003c- 1..10000, do: insert_on_primary(i)\n \n # Measure sync time\n Benchee.run(%{\n \"sync 10K changes\" =\u003e fn -\u003e \n sync(replica)\n end\n })\nend\n```\n\n**Implementation**:\n\n1. **Add benchee dependency** (mix.exs):\n ```elixir\n {:benchee, \"~\u003e 1.3\", only: :dev}\n {:benchee_html, \"~\u003e 1.0\", only: :dev}\n ```\n\n2. **Create benchmark files**:\n - benchmarks/prepared_statements_bench.exs\n - benchmarks/cursor_streaming_bench.exs\n - benchmarks/concurrent_connections_bench.exs\n - benchmarks/transactions_bench.exs\n - benchmarks/batch_operations_bench.exs\n - benchmarks/statement_cache_bench.exs\n - benchmarks/replication_bench.exs\n\n3. **Add benchmark runner** (mix.exs):\n ```elixir\n def cli do\n [\n aliases: [\n bench: \"run benchmarks/**/*_bench.exs\"\n ]\n ]\n end\n ```\n\n4. **CI Integration**:\n - Run benchmarks on PRs\n - Track performance over time\n - Alert on regression \u003e 20%\n\n**Baseline Targets** (to establish):\n- Prepared statement execution: X ops/sec\n- Cursor streaming: Y MB memory for Z rows\n- Transaction throughput: 1000+ txn/sec\n- Concurrent connections: 100 connections\n- Batch operations: Native 20-30% faster than manual\n\n**Files**:\n- mix.exs (add benchee dependency)\n- benchmarks/*.exs (benchmark files)\n- .github/workflows/benchmarks.yml (CI integration)\n- PERFORMANCE.md (document baselines and results)\n\n**Acceptance Criteria**:\n- [ ] Benchee dependency added\n- [ ] 7 benchmark categories implemented\n- [ ] Benchmarks run via mix bench\n- [ ] HTML reports generated\n- [ ] Baselines documented in PERFORMANCE.md\n- [ ] CI runs benchmarks on PRs\n- [ ] Regression alerts configured\n\n**Test Requirements**:\n```bash\n# Run all benchmarks\nmix bench\n\n# Run specific benchmark\nmix run benchmarks/prepared_statements_bench.exs\n\n# Generate HTML report\nmix run benchmarks/prepared_statements_bench.exs --format html\n```\n\n**Benefits**:\n- Track performance across versions\n- Validate performance improvements\n- Identify bottlenecks\n- Catch regressions early\n- Document performance characteristics\n\n**References**:\n- FEATURE_CHECKLIST.md section \"Test Coverage Priorities\" item 6\n- LIBSQL_FEATURE_COMPARISON.md section \"Performance and Stress Tests\"\n\n**Dependencies**:\n- Validates fixes for el-2ry (statement performance bug)\n- Validates fixes for el-aob (streaming cursors)\n\n**Priority**: P3 - Nice to have, tracks quality over time\n**Effort**: 2-3 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-e42","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Add Performance Benchmark Tests","updated_at":"2025-12-30T17:46:15Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T14:46:51Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2e9b0ce99dcdde1c98ec947e8e73c51f3be9be13681d0cfaf5e77f0584da0755","created_at":"2026-01-13T11:53:07Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Boolean values in raw query parameters (e.g., Repo.all(from u in User, where: u.active == ^true)) may not be encoded to SQLite's 0/1 format. Verify if dumpers handle this case, or if encode_param needs explicit boolean handling. Add tests for boolean query parameters and implement encoding if needed (true -\u003e 1, false -\u003e 0).","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-e9r","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add boolean encoding support in query parameters","updated_at":"2026-01-13T14:46:51Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"7cb094c635aa0be2d32a002c7a0020f8533bb5b63022332925222e00e63f58b5","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"el-aob (Implement True Streaming Cursors - feature)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-f0x","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Related","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T21:52:54Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f1ff470b40fefafdd9b44bc0af193aee69565b453ecc43972f13760ee46a9205","created_at":"2026-01-08T21:34:31Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add tests for connection pool behavior when under concurrent load.\n\nFrom TEST_AUDIT_REPORT.md item 9: 'Connection pool behavior under load' - identified as under-tested.\n\n**Test Scenarios**:\n1. Concurrent connections at different pool sizes (5, 10, 50, 100)\n2. Connection exhaustion and queue behavior \n3. Connection recovery after failure/close\n4. Load distribution across pool connections\n5. Long-running queries don't block quick queries\n6. Pool cleanup and resource leak prevention\n\n**Test File**: test/pool_load_test.exs (new)\n\n**Estimated Effort**: 2-3 hours","design":"","due_at":null,"ephemeral":0,"estimated_minutes":150,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-fd8","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test connection pool behavior under load","updated_at":"2026-01-08T21:52:54Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T13:37:36Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"3740adbaf0647ec7f98ac3e71395af1808ded1dd47b1539ce09b53cd6ce7071e","created_at":"2025-12-30T17:35:52Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Not implemented in ecto_libsql. libSQL 3.45.1 fully supports EXPLAIN and EXPLAIN QUERY PLAN for query optimiser insight.\n\nDesired API:\n query = from u in User, where: u.age \u003e 18\n {:ok, plan} = Repo.explain(query)\n # Or: Ecto.Adapters.SQL.explain(Repo, :all, query)\n\nPRIORITY: Recommended as #3 in implementation order - quick win for debugging.\n\nEffort: 2-3 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-ffc","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"EXPLAIN Query Support","updated_at":"2026-01-08T13:37:36Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T13:02:28Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a1d32899ef6cf6b2f56e5a2e4a83882844c685473fb7efd78f4b4cce2b2c0ca1","created_at":"2026-01-06T19:20:27Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Set status to in-progress","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-ffc.1","is_template":0,"issue_type":"event","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"State change: status → in-progress","updated_at":"2026-01-08T13:02:28Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T17:52:29Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"e6319d8800246161dfa3fbca629012e4e7f95ad84f5bd64cd8b0e1f271b45d3a","created_at":"2025-12-30T18:05:53Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"## Problem\n\nThe property test for binary data handling is failing when the generated binary is a single null byte ().\n\n## Failure Details\n\n\n\n**File**: test/fuzz_test.exs:736\n**Test**: property binary data handling round-trips binary data correctly\n\n## Root Cause\n\nWhen a single null byte () is stored in the database as a BLOB and retrieved, it's being returned as an empty string () instead of the original binary.\n\nThis suggests a potential issue with:\n1. Binary encoding/decoding in the Rust NIF layer (decode.rs)\n2. Type conversion in the Elixir loaders/dumpers\n3. Handling of edge case binaries (single null byte, empty blobs)\n\n## Impact\n\n- Property-based test failures indicate the binary data handling isn't robust for all valid binary inputs\n- Applications storing binary data with null bytes may experience data corruption\n- Affects blob storage reliability\n\n## Reproduction\n\n\n\n## Investigation Areas\n\n1. **native/ecto_libsql/src/decode.rs** - Check Value::Blob conversion\n2. **lib/ecto/adapters/libsql.ex** - Check binary loaders/dumpers\n3. **native/ecto_libsql/src/query.rs** - Verify blob retrieval logic\n4. **Test edge cases**: , , , \n\n## Expected Behavior\n\nAll binaries (including single null byte) should round-trip correctly:\n- Store → Retrieve \n- Store → Retrieve \n- Store → Retrieve \n\n## Related Code\n\n- test/fuzz_test.exs:736-753\n- native/ecto_libsql/src/decode.rs (blob handling)\n- lib/ecto/adapters/libsql.ex (type loaders/dumpers)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-fpi","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Fix binary data round-trip property test failure for single null byte","updated_at":"2026-01-13T17:52:29Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:57:16Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"55d0fb23f249c38840a7c44792a1740329d43d8f14c4c74a222999dc9609060d","created_at":"2025-12-30T17:42:37Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add comprehensive integration tests for replication features.\n\n**Context**: Replication features are implemented but have minimal test coverage (marked as ⚠️ in FEATURE_CHECKLIST.md).\n\n**Required Tests** (test/replication_integration_test.exs):\n- sync_until() - frame-specific sync\n- flush_replicator() - force pending writes \n- max_write_replication_index() - write tracking\n- replication_index() - current frame tracking\n\n**Test Scenarios**:\n1. Monitor replication lag via frame numbers\n2. Sync to specific frame number\n3. Flush pending writes and verify frame number\n4. Track max write frame across operations\n\n**Files**:\n- NEW: test/replication_integration_test.exs\n- Reference: FEATURE_CHECKLIST.md line 212-242\n- Reference: LIBSQL_FEATURE_MATRIX_FINAL.md section 5\n\n**Acceptance Criteria**:\n- [ ] All 4 replication NIFs have comprehensive tests\n- [ ] Tests cover happy path and edge cases\n- [ ] Tests verify frame number progression\n- [ ] Tests validate sync behaviour\n\n**Priority**: P1 - Critical for Turso use cases\n**Effort**: 2-3 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-g5l","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Replication Integration Tests","updated_at":"2026-01-12T11:57:16Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T14:46:51Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"162ff414701df0b27eb4ac7c4ffa8a5b0889a07cff7f05626c99d9f7dc095d1e","created_at":"2026-01-13T11:53:07Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"The atom :null is sometimes used in Elixir code to represent SQL NULL. Verify if SQLite/LibSQL handles :null atom correctly, or if it should be converted to nil. Add encode_param(:null) clause if conversion is needed. Also consider if other atoms should be handled or should raise an error for better debugging.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-gwo","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add atom encoding support for :null in query parameters","updated_at":"2026-01-13T14:46:51Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T14:46:52Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2107b7fd9c405f40e6fc61af7e0c1708d498bdedaffa898c09595125bea49f0e","created_at":"2026-01-13T11:53:07Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Nested structures in query parameters (e.g., maps/lists containing DateTime/Decimal values) are not recursively encoded. Document in AGENTS.md that users should pre-encode nested structures before passing to queries. Example: %{metadata: %{created_at: DateTime.utc_now()}} will fail. Add to limitations section with workaround examples.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-h0i","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Document limitations for nested structures with temporal types","updated_at":"2026-01-13T14:46:52Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"18589531ed30f5c6ea9bce80186ddc8b4673b04178befc5ad411f0665a73347e","created_at":"2025-12-30T17:35:53Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Not implemented. Generate rows from functions, series generation, CSV parsing. Examples: generate_series(1, 10), csv_table(path, schema). Effort: 4-5 days (if building custom extension).","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-h48","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Table-Valued Functions (via Extensions)","updated_at":"2025-12-30T17:36:48Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:57:16Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"67d1b7d66ae8fb2d9e9faf35daf93e604a081c18965bb5c418100bf0f1f70af6","created_at":"2025-12-30T17:43:00Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add comprehensive functional tests for connection reset and interrupt features.\n\n**Context**: reset_connection and interrupt_connection are implemented but only have basic tests (marked as ⚠️ in FEATURE_CHECKLIST.md).\n\n**Required Tests** (expand test/connection_features_test.exs or create new):\n\n**Reset Tests**:\n- Reset maintains database connection\n- Reset allows connection reuse in pool\n- Reset doesn't close active transactions\n- Reset clears temporary state\n- Reset multiple times in succession\n\n**Interrupt Tests**:\n- Interrupt cancels long-running query\n- Interrupt allows query restart after cancellation\n- Interrupt doesn't affect other connections\n- Interrupt during transaction behaviour\n- Concurrent interrupts on different connections\n\n**Files**:\n- EXPAND/NEW: test/connection_features_test.exs\n- Reference: FEATURE_CHECKLIST.md line 267-287\n- Reference: LIBSQL_FEATURE_COMPARISON.md section 3\n\n**Test Examples**:\n```elixir\ntest \"reset maintains database connection\" do\n {:ok, state} = connect()\n {:ok, state} = reset_connection(state)\n # Verify connection still works\n {:ok, _, _, _} = query(state, \"SELECT 1\")\nend\n\ntest \"interrupt cancels long-running query\" do\n {:ok, state} = connect()\n # Start long query in background\n task = Task.async(fn -\u003e query(state, \"SELECT sleep(10)\") end)\n # Interrupt after 100ms\n Process.sleep(100)\n interrupt_connection(state)\n # Verify query was cancelled\n assert {:error, _} = Task.await(task)\nend\n```\n\n**Acceptance Criteria**:\n- [ ] Reset functional tests comprehensive\n- [ ] Interrupt functional tests comprehensive\n- [ ] Tests verify connection state after reset/interrupt\n- [ ] Tests verify connection pool behaviour\n- [ ] Tests cover edge cases and error conditions\n\n**Priority**: P1 - Important for production robustness\n**Effort**: 2 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-i0v","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Connection Reset and Interrupt Functional Tests","updated_at":"2026-01-12T11:57:16Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"7182485ed168d3553f677ab13651c8041e20e96ae02499d2fc8e6486d001d764","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"test/connection_recovery_test.exs (new file)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-i3j","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test File","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"fc6de3cde01e3dccb7e4d587627fc00192609eaca184def5ce0e018dfe0ff471","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"- Verifies Rust layer doesn't crash on invalid inputs","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-i9r","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Impact","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:54:46Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b00dea74c1aaee7f74eec83b0f75c457b73d885f41bea696dfb07e48b30311fe","created_at":"2025-12-30T17:35:51Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Not supported in migrations. SQLite 3.31+ (2020), libSQL 3.45.1 fully supports GENERATED ALWAYS AS syntax with both STORED and virtual variants.\n\nDesired API:\n create table(:users) do\n add :first_name, :string\n add :last_name, :string\n add :full_name, :string, generated: \"first_name || ' ' || last_name\", stored: true\n end\n\nPRIORITY: Recommended as #4 in implementation order.\n\nEffort: 3-4 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-ik6","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Generated/Computed Columns","updated_at":"2026-01-17T08:54:46Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"46b086a389a909d8e7f43e99afa6b49c5f0ac510751072f374bee1ce77fa5534","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"- Add benchee (~1.3) and benchee_html dependencies","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-jlb","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Implementation","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T12:26:37Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a6d7e5ed9ffd768efc7ad48079c678acc58e6ef1208477a008ee2a2126d2daee","created_at":"2026-01-12T12:26:25Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-l4i","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"TEST: Verify beads sync works","updated_at":"2026-01-12T12:26:37Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"fcbbe908a3d3fc5af78da70a14ecadb96c44daf87c3cc18ffb6f53492fda4387","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"native/ecto_libsql/src/tests/error_handling_tests.rs (new file)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-lkm","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test File","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T12:57:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"cf2e66377b5bacc4fdba350c04bfa949b14f7b0e1bab7fa7dceb0bab2cd7ed7b","created_at":"2026-01-08T12:55:29Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"ecto_libsql_test.exs (681 lines) is a mixed bag of tests. It contains:\n\nTests that should be moved:\n- 'vector' test → belongs in vector_geospatial_test.exs\n- 'prepare and execute a simple select' → belongs in prepared_statement_test.exs\n- 'create table' → belongs in ecto_migration_test.exs\n- 'transaction and param' → belongs in savepoint_test.exs or ecto_sql_transaction_compat_test.exs\n- 'explain query' → belongs in explain_query_test.exs\n\nTests to keep (these are legitimate smoke tests):\n- 'connection remote replica'\n- 'ping connection'\n\nAfter consolidation:\n1. Rename to smoke_test.exs to clarify it's a smoke test file\n2. Add documentation explaining it's for basic sanity checking\n3. Keep line count to ~100-150 lines max\n\nEffort: 45 minutes\nImpact: Reduce maintenance burden, clearer test intent, eliminates false duplication signals","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-m1w","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Clean up ecto_libsql_test.exs - move tests to appropriate files","updated_at":"2026-01-08T12:57:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a38e69e1ef8129ffaf964bae4a5cbb1ef3248a24e2665ab8746f2f1f11695c75","created_at":"2026-01-02T17:08:57Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"## Location\n`lib/ecto_libsql/native.ex` lines 508-518\n\n## Current Behaviour\n`evict_oldest_entries/0` calls `:ets.tab2list/1`, loading all 1000 entries into memory, then sorts by access time. This is O(n log n) on every cache overflow.\n\nWith max 1000 entries and evictions removing 500 at a time, this runs infrequently enough to be acceptable, but worth noting for future optimisation if cache size increases.\n\n## Suggested Alternative\nUse a separate `:ordered_set` table keyed by access time for O(1) oldest entry lookup.\n\nHowever, the current implementation is adequate for the documented 1000-entry limit - only pursue if cache size needs to increase significantly.\n\n## Priority\nP4 (backlog) - Only optimise if profiling shows this is a bottleneck.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-m99","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Optimise ETS cache eviction to avoid O(n log n) scan","updated_at":"2026-01-02T17:09:04Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f00613c0b295cf33c4803f778a26cb2774575c06979999ad1eb8559ed5dfd539","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Concurrent connections at different pool sizes (5, 10, 50, 100)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-mla","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test Scenarios","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"e00befc1ea76bf6ec0e59b6a52ca5478a0bdb2e7365c6f496a62822a112c9c4b","created_at":"2026-01-14T18:39:35Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add documentation to AGENTS.md explaining :bitstring type limitations.\n\n**Details:**\n- SQLite only supports byte-aligned BLOB storage\n- :bitstring type allows non-byte-aligned bit sequences\n- This is a SQLite limitation, not an adapter issue\n\n**Documentation Updates:**\n- Add to limitations section in AGENTS.md\n- Explain workaround: pad to byte boundary or use different approach\n- Mention in type mapping table\n\n**References:**\n- TYPE_LOADER_DUMPER_AUDIT.md identifies this as unsupported\n- Standard practice: document limitations clearly for users","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-mzq","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"drew.robinson@gmail.com","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Document :bitstring limitations","updated_at":"2026-01-14T18:39:35Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:51:54Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"73b73458bda6fe8654832ebc5228077cfa97042b79b4ce7e86cfb5eade99d381","created_at":"2025-12-30T17:35:51Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"INSERT ... ON CONFLICT not implemented in ecto_libsql. SQLite 3.24+ (2018), libSQL 3.45.1 fully supports all conflict resolution modes: INSERT OR IGNORE, INSERT OR REPLACE, REPLACE, INSERT OR FAIL, INSERT OR ABORT, INSERT OR ROLLBACK.\n\nDesired API:\n Repo.insert(changeset, on_conflict: :replace_all, conflict_target: [:email])\n Repo.insert(changeset, on_conflict: {:replace, [:name, :updated_at]}, conflict_target: [:email])\n\nPRIORITY: Recommended as #2 in implementation order - common pattern, high value.\n\nEffort: 4-5 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-ndz","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"UPSERT Support (INSERT ... ON CONFLICT)","updated_at":"2026-01-17T08:51:54Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a96e1c0180e9dcb0980609e6d004bf34207a8a20109284bf698bc3150a1950c8","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Prepared statement performance (100 executions)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-nms","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Benchmark Categories","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:35:11Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"49b06ae2239998c13bd9b7a5aaa2d299d42c4698143a6c9633b6a37ec30f8ed6","created_at":"2025-12-30T17:43:48Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add support for named parameters in queries (:name, @name, $name syntax).\n\n**Context**: LibSQL supports named parameters but ecto_libsql only supports positional (?). This is marked as high priority in FEATURE_CHECKLIST.md.\n\n**Current Limitation**:\n```elixir\n# Only positional parameters work:\nquery(\"INSERT INTO users VALUES (?, ?)\", [1, \"Alice\"])\n\n# Named parameters don't work:\nquery(\"INSERT INTO users (id, name) VALUES (:id, :name)\", %{id: 1, name: \"Alice\"})\n```\n\n**LibSQL Support**:\n- :name syntax (standard SQLite)\n- @name syntax (alternative)\n- $name syntax (PostgreSQL-like)\n\n**Benefits**:\n- Better developer experience\n- Self-documenting queries\n- Order-independent parameters\n- Matches PostgreSQL Ecto conventions\n\n**Implementation Required**:\n\n1. **Add parameter_name() NIF**:\n - Implement in native/ecto_libsql/src/statement.rs\n - Expose parameter_name(stmt_id, index) -\u003e {:ok, name} | {:error, reason}\n\n2. **Update query parameter handling**:\n - Accept map parameters: %{id: 1, name: \"Alice\"}\n - Convert named params to positional based on statement introspection\n - Maintain backwards compatibility with positional params\n\n3. **Update Ecto.Adapters.LibSql.Connection**:\n - Generate SQL with named parameters for better readability\n - Convert Ecto query bindings to named params\n\n**Files**:\n- native/ecto_libsql/src/statement.rs (add parameter_name NIF)\n- lib/ecto_libsql/native.ex (wrapper for parameter_name)\n- lib/ecto_libsql.ex (update parameter handling)\n- lib/ecto/adapters/libsql/connection.ex (generate named params)\n- test/statement_features_test.exs (tests marked :skip)\n\n**Existing Tests**:\nTests already exist but are marked :skip (mentioned in FEATURE_CHECKLIST.md line 1)\n\n**Acceptance Criteria**:\n- [ ] parameter_name() NIF implemented\n- [ ] Queries accept map parameters\n- [ ] All 3 syntaxes work (:name, @name, $name)\n- [ ] Backwards compatible with positional params\n- [ ] Unskip and pass existing tests\n- [ ] Add comprehensive named parameter tests\n\n**Examples**:\n```elixir\n# After implementation:\nRepo.query(\"INSERT INTO users (id, name) VALUES (:id, :name)\", %{id: 1, name: \"Alice\"})\nRepo.query(\"UPDATE users SET name = @name WHERE id = @id\", %{id: 1, name: \"Bob\"})\n```\n\n**References**:\n- FEATURE_CHECKLIST.md section \"High Priority (Should Implement)\" item 1\n- Test file with :skip markers\n\n**Priority**: P1 - High priority, improves developer experience\n**Effort**: 2-3 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-nqb","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Implement Named Parameters Support","updated_at":"2026-01-17T08:35:11Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:55:53Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"86c4f759c5e1da144e09cd68dac70ee9d4d60016f93b107699ca9371a2ffdef7","created_at":"2025-12-30T17:35:53Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"SQLite supports but Ecto DSL doesn't. Index only subset of rows, smaller/faster indexes, better for conditional uniqueness. Desired API: create index(:users, [:email], unique: true, where: \"deleted_at IS NULL\"). Effort: 2-3 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-o8r","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Partial Index Support in Migrations","updated_at":"2026-01-17T08:55:53Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a418b4393e8bcc48b11fd394770de900db130b4d457ce0965db7e9ed53372ead","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. JSONB round-trip correctness (text → JSONB → text)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-olq","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test Scenarios","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T12:02:53Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"fda360b0b9fa1dbde6f1698e46b722a86ce0d577540252b6b90252605b076b7c","created_at":"2026-01-13T11:57:42Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"PROBLEM: Oban passes job args as plain Elixir maps, but Rust NIF cannot serialize map types, causing 'Unsupported argument type' errors. SOLUTION: Add encode_parameters/1 function in lib/ecto_libsql/native.ex to convert plain maps (not structs) to JSON strings before passing to NIF. Must be called in: 1) do_query/6 before query_args call, 2) do_execute_with_trx/6 before query_with_trx_args and execute_with_transaction calls. IMPACT: Blocks Oban job insertion with complex args. REFERENCE: See Fix 1 in feedback document for exact implementation.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-oxv","is_template":0,"issue_type":"bug","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Fix map parameter encoding to JSON before NIF calls","updated_at":"2026-01-13T12:02:53Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"ea8db81da8d2d196a56af73baf9aa9576f101a77313d02332c972c91e84275df","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"test/pool_load_test.exs (new file)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-oya","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test File","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"f80d6f123d089a37a4982a176b0a9297583df69f3616cc7b7bb3dc935570a0a3","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Reduce test maintenance, focus on higher-level scenarios","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-pez","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Impact","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"092d64d8e8d9642a99c5d66ed3c21342ab40f79ae503ae66bf03967eaff22c64","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Connection loss during query execution","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-phd","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test Scenarios","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T14:46:51Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c53716ed64a8c51c3df14eaf78c318712bfa2416768d1ce45cbb8e3208a97ee0","created_at":"2026-01-13T11:53:07Z","created_by":"Drew Robinson","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Query parameters may contain Ecto.UUID structs (e.g., Repo.get_by(User, uuid: %Ecto.UUID{...})). Currently these pass through without encoding, which may cause NIF errors. Add encode_param(%Ecto.UUID{}) clause to convert to string representation. Check if Ecto.UUID.dump/1 or to_string/1 is appropriate.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-pre","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Add UUID encoding support in query parameters","updated_at":"2026-01-13T14:46:51Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T12:56:50Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2d317dcaff71dbed993892d88f7cce96b5d563f5964ec81fba73513b038f1f7d","created_at":"2026-01-08T12:55:24Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Both test EXPLAIN query functionality with overlapping test cases.\n\nexplain_query_test.exs (262 lines): Comprehensive Ecto setup with full test coverage\nexplain_simple_test.exs (115 lines): Simpler test setup (appears to be a debugging artifact from development)\n\nAction:\n1. Review explain_simple_test.exs for any unique test cases\n2. Move any unique tests to explain_query_test.exs\n3. Delete explain_simple_test.exs\n4. Keep explain_query_test.exs as the canonical EXPLAIN test file\n\nEffort: 15 minutes\nImpact: Remove redundant test file, single source of truth for EXPLAIN testing","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-q7e","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Consolidate explain_query_test.exs and explain_simple_test.exs","updated_at":"2026-01-08T12:56:50Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"35424df63d3698c9cc4b35a94472ca994a84966bf5a24c4f5700df7800077f80","created_at":"2025-12-30T17:35:52Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Not exposed. Better query planning, automatic index selection, performance optimisation. Desired API: EctoLibSql.Native.analyze(state), EctoLibSql.Native.analyze_table(state, \"users\"), and config auto_analyze: true for post-migration. Effort: 2 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-qjf","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":4,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"ANALYZE Statistics Collection","updated_at":"2025-12-30T17:36:47Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:57:16Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"e7f6f92aaf8e89f7199153a04d9a30704f668eed9fba7c1b18d8a5bddf94520a","created_at":"2025-12-30T17:42:49Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Expand statement introspection tests to cover edge cases and complex scenarios.\n\n**Context**: Statement introspection features (parameter_count, column_count, column_name) are implemented but only have basic happy-path tests (marked as ⚠️ in FEATURE_CHECKLIST.md).\n\n**Required Tests** (expand test/statement_features_test.exs):\n- Parameter count with 0 parameters\n- Parameter count with many parameters (\u003e10)\n- Parameter count with duplicate parameters\n- Column count for SELECT *\n- Column count for complex JOINs with aliases\n- Column count for aggregate functions\n- Column names with AS aliases\n- Column names for expressions and computed columns\n- Column names for all types (INTEGER, TEXT, BLOB, REAL)\n\n**Files**:\n- EXPAND: test/statement_features_test.exs (or create new file)\n- Reference: FEATURE_CHECKLIST.md line 245-264\n- Reference: LIBSQL_FEATURE_COMPARISON.md section 2\n\n**Test Examples**:\n```elixir\n# Edge case: No parameters\nstmt = prepare(\"SELECT * FROM users\")\nassert parameter_count(stmt) == 0\n\n# Edge case: Many parameters\nstmt = prepare(\"INSERT INTO users VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\")\nassert parameter_count(stmt) == 10\n\n# Edge case: SELECT * column count\nstmt = prepare(\"SELECT * FROM users\")\nassert column_count(stmt) == actual_column_count\n\n# Edge case: Complex JOIN\nstmt = prepare(\"SELECT u.id, p.name AS profile_name FROM users u JOIN profiles p ON u.id = p.user_id\")\nassert column_name(stmt, 1) == \"profile_name\"\n```\n\n**Acceptance Criteria**:\n- [ ] All edge cases tested\n- [ ] Tests verify correct counts and names\n- [ ] Tests cover complex queries (JOINs, aggregates, expressions)\n- [ ] Tests validate column name aliases\n\n**Priority**: P1 - Important for tooling/debugging\n**Effort**: 1-2 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-qvs","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Statement Introspection Edge Case Tests","updated_at":"2026-01-12T11:57:16Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c8a571351bb4a7edfc09f6d36dd5cd48c241db39559dd93264e4f52a3b36de41","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"2-3 days","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-r7j","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Estimated Effort","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"ebd0fc47f44a876c317417e074d7b4e809cc8ccb4fcd4ecdc466ce7ad990cdd9","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"el-a17 (JSONB Binary Format Support - feature, closed)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-trm","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Related","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-13T17:51:42Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b5fe13aac933c84a7906d61894565184167240ceb905d793b7702718c0a740f0","created_at":"2026-01-08T21:35:08Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Remove duplicate basic parameter binding tests from Elixir since Rust already covers them.\n\nFrom TEST_AUDIT_REPORT.md item 8: 'Reduce Redundant Parameter Binding Tests' - Rust tests integers, floats, text, NULL, BLOB.\n\n**Work Required**:\n1. Identify redundant tests (basic type binding in Elixir)\n2. Remove Elixir duplicates\n3. Keep Elixir tests for:\n - Named parameters (unique to Elixir)\n - Complex scenarios (maps, nested)\n - Ecto-specific coercion\n\n**Files to Check**:\n- ecto_libsql_test.exs (after cleanup)\n- prepared_statement_test.exs\n- Other test files with parameter binding\n\n**Estimated Effort**: 30 minutes\n\n**Impact**: Reduce test maintenance, focus on higher-level scenarios","design":"","due_at":null,"ephemeral":0,"estimated_minutes":30,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-v3v","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Reduce redundant parameter binding tests","updated_at":"2026-01-13T17:51:42Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"9e465735061ed473573e9a7475d2f3b8e785f5bcfecf833655d380131d832029","created_at":"2025-12-30T17:35:53Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"SQLite supports but awkward in Ecto. Index computed values, case-insensitive searches, JSON field indexing. Desired API: create index(:users, [], expression: \"LOWER(email)\", unique: true) or via fragment. Effort: 3 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-vnu","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Expression Indexes","updated_at":"2025-12-30T17:36:47Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"6cadb15bc1cfcb238a4586d5e48a600f3f5bc004211018e1c9aa90d48b609d3f","created_at":"2025-12-30T17:43:58Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"libSQL 3.45.1 has full window function support: OVER, PARTITION BY, ORDER BY, frame specifications (ROWS BETWEEN, RANGE BETWEEN). Currently works via fragments but could benefit from dedicated query helpers.\n\nDesired API:\n from u in User,\n select: %{\n name: u.name,\n running_total: over(sum(u.amount), partition_by: u.category, order_by: u.date)\n }\n\nEffort: 4-5 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-wee","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":3,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"Window Functions Query Helpers","updated_at":"2025-12-30T17:43:58Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T21:42:15Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"3bcb55ad1d715314393b5019085e0fe0ca794662431a865f1ce7e48f5bc94886","created_at":"2026-01-08T21:34:46Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Verify JSONB binary format works correctly and compare performance vs text JSON.\n\nFrom TEST_AUDIT_REPORT.md item 9: 'JSON with JSONB binary format' - identified as possibly under-tested.\n\n**Test Scenarios**:\n1. JSONB round-trip correctness (text → JSONB → text)\n2. JSONB and text JSON compatibility (same results)\n3. JSONB storage size efficiency (5-10% smaller expected)\n4. JSONB query performance vs text JSON\n5. JSONB with large objects (10MB+)\n6. JSONB modification (json_set, json_replace) preserves format\n7. JSONB array operations\n\n**Test File**: Extend test/json_helpers_test.exs with JSONB-specific scenarios\n\n**Estimated Effort**: 2-3 hours\n\n**Related**: el-a17 (JSONB Binary Format Support - feature, closed)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":150,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-wtl","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test JSONB binary format operations","updated_at":"2026-01-08T21:42:15Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"a0b22600e96a358c2889028e25f8d190e79bd38a340692e424d57f49d9ccb534","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"1. Invalid resource IDs (connection, statement, transaction, cursor)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-wvb","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Test Scenarios","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T12:57:50Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"2f4f0558492f91e5eb4007a3e9045c16957594bf4564878f7afd63fbcda828f9","created_at":"2026-01-08T12:55:37Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Both files test error handling with potential duplication:\n\nerror_demo_test.exs (146 lines): Demonstration tests showing that errors are handled gracefully (no VM crashes)\nerror_handling_test.exs (250 lines): Comprehensive error handling tests\n\nNeed to determine:\n1. Do these test the same scenarios? (likely yes, with different focus)\n2. Is there duplication that needs consolidation?\n3. Should one be merged into the other?\n\nAction:\n1. Review both files side-by-side for duplication\n2. If same scope: merge into error_handling_test.exs and delete error_demo_test.exs\n3. If different scope: clarify names (maybe 'error_demo_test.exs' → 'error_no_crash_demo_test.exs')\n\nEffort: 30 minutes\nImpact: Clearer error testing strategy, reduce maintenance","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-x0d","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Clarify relationship between error_demo_test.exs and error_handling_test.exs","updated_at":"2026-01-08T12:57:50Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"6375044387b7278792a54c61cecfd5d2967beb98c54d7088ac07006dec6d0ff2","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"- benchmarks/prepared_statements_bench.exs","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-x8b","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Files to Create","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"297bc4a8508c7bdd16d86b8e885af912aa8aed0d580cfe5c10e32c8f873a4123","created_at":"2025-12-30T17:35:54Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Works for single operations, not batches. libSQL 3.45.1 supports RETURNING clause on INSERT/UPDATE/DELETE.\n\nDesired API:\n {count, rows} = Repo.insert_all(User, users, returning: [:id, :inserted_at])\n # Returns all inserted rows with IDs\n\nPRIORITY: Recommended as #9 in implementation order.\n\nEffort: 3-4 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-xih","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"open","target":"","timeout_ns":0,"title":"RETURNING Enhancement for Batch Operations","updated_at":"2026-01-05T14:41:54Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T14:13:19Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"6f60e493b6a4a35cec6fdeaece4c75c6b3b2184f3ac66da59d1f8e0216404fd9","created_at":"2025-12-30T17:45:14Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add support for authorizer hooks to enable row-level security and multi-tenant applications.\n\n**Context**: Authorizer hooks allow fine-grained access control at the SQL operation level. Essential for multi-tenant applications and row-level security (RLS).\n\n**Missing API** (from FEATURE_CHECKLIST.md):\n- authorizer() - Register callback that approves/denies SQL operations\n\n**Use Cases**:\n\n**1. Multi-Tenant Row-Level Security**:\n```elixir\n# Enforce tenant isolation at database level\nEctoLibSql.set_authorizer(repo, fn action, table, column, _context -\u003e\n case action do\n :read when table == \"users\" -\u003e\n if current_tenant_can_read?(table) do\n :ok\n else\n {:error, :unauthorized}\n end\n \n :write when table in [\"users\", \"posts\"] -\u003e\n if current_tenant_can_write?(table) do\n :ok\n else\n {:error, :unauthorized}\n end\n \n _ -\u003e :ok\n end\nend)\n```\n\n**2. Column-Level Access Control**:\n```elixir\n# Restrict access to sensitive columns\nEctoLibSql.set_authorizer(repo, fn action, table, column, _context -\u003e\n if column == \"ssn\" and !current_user_is_admin?() do\n {:error, :forbidden}\n else\n :ok\n end\nend)\n```\n\n**3. Audit Sensitive Operations**:\n```elixir\n# Log all DELETE operations\nEctoLibSql.set_authorizer(repo, fn action, table, _column, _context -\u003e\n if action == :delete do\n AuditLog.log_delete(current_user(), table)\n end\n :ok\nend)\n```\n\n**4. Prevent Dangerous Operations**:\n```elixir\n# Block DROP TABLE in production\nEctoLibSql.set_authorizer(repo, fn action, _table, _column, _context -\u003e\n if action in [:drop_table, :drop_index] and production?() do\n {:error, :forbidden}\n else\n :ok\n end\nend)\n```\n\n**SQLite Authorizer Actions**:\n- :read - SELECT from table/column\n- :insert - INSERT into table\n- :update - UPDATE table/column\n- :delete - DELETE from table\n- :create_table, :drop_table\n- :create_index, :drop_index\n- :alter_table\n- :transaction\n- And many more...\n\n**Implementation Challenge**:\nSimilar to update_hook, requires Rust → Elixir callbacks with additional complexity:\n- Authorizer must return result synchronously (blocking)\n- Called very frequently (every SQL operation)\n- Performance critical (adds overhead to all queries)\n- Thread-safety for concurrent connections\n\n**Implementation Options**:\n\n**Option 1: Synchronous Callback (Required)**:\n- Authorizer MUST return result synchronously\n- Block Rust thread while waiting for Elixir\n- Use message passing with timeout\n- Handle timeout as :deny\n\n**Option 2: Pre-Compiled Rules (Performance)**:\n- Instead of arbitrary Elixir callback\n- Define rules in config\n- Compile to Rust decision tree\n- Much faster but less flexible\n\n**Proposed Implementation (Hybrid)**:\n\n1. **Add NIF** (native/ecto_libsql/src/connection.rs):\n ```rust\n #[rustler::nif]\n fn set_authorizer(conn_id: \u0026str, pid: Pid) -\u003e NifResult\u003cAtom\u003e {\n // Store pid in connection metadata\n // Register libsql authorizer\n // On auth check: send sync message to pid, wait for response\n }\n \n #[rustler::nif]\n fn remove_authorizer(conn_id: \u0026str) -\u003e NifResult\u003cAtom\u003e\n ```\n\n2. **Add Elixir wrapper** (lib/ecto_libsql/native.ex):\n ```elixir\n def set_authorizer(state, callback_fn) do\n pid = spawn(fn -\u003e authorizer_loop(callback_fn) end)\n set_authorizer_nif(state.conn_id, pid)\n end\n \n defp authorizer_loop(callback_fn) do\n receive do\n {:authorize, from, action, table, column, context} -\u003e\n result = callback_fn.(action, table, column, context)\n send(from, {:auth_result, result})\n authorizer_loop(callback_fn)\n end\n end\n ```\n\n3. **Rust authorizer implementation**:\n ```rust\n fn authorizer_callback(action: i32, table: \u0026str, column: \u0026str) -\u003e i32 {\n // Send message to Elixir pid\n // Wait for response with timeout (100ms)\n // Return SQLITE_OK or SQLITE_DENY\n // On timeout: SQLITE_DENY (safe default)\n }\n ```\n\n**Performance Considerations**:\n- ⚠️ Adds ~1-5ms overhead per SQL operation\n- Critical for read-heavy workloads\n- Consider caching auth decisions\n- Consider pre-compiled rules for performance-critical paths\n\n**Files**:\n- native/ecto_libsql/src/connection.rs (authorizer implementation)\n- native/ecto_libsql/src/models.rs (store authorizer pid)\n- lib/ecto_libsql/native.ex (wrapper and authorizer process)\n- lib/ecto/adapters/libsql.ex (public API)\n- test/authorizer_test.exs (new tests)\n- AGENTS.md (update API docs)\n\n**Acceptance Criteria**:\n- [ ] set_authorizer() NIF implemented\n- [ ] remove_authorizer() NIF implemented\n- [ ] Authorizer can approve operations (return :ok)\n- [ ] Authorizer can deny operations (return {:error, reason})\n- [ ] Authorizer receives correct action types\n- [ ] Authorizer timeout doesn't crash VM\n- [ ] Performance overhead \u003c 5ms per operation\n- [ ] Comprehensive tests including error cases\n- [ ] Multi-tenant example in documentation\n\n**Test Requirements**:\n```elixir\ntest \"authorizer can block SELECT operations\" do\n EctoLibSql.set_authorizer(repo, fn action, _table, _column, _context -\u003e\n if action == :read do\n {:error, :forbidden}\n else\n :ok\n end\n end)\n \n assert {:error, _} = Repo.query(\"SELECT * FROM users\")\nend\n\ntest \"authorizer allows approved operations\" do\n EctoLibSql.set_authorizer(repo, fn _action, _table, _column, _context -\u003e\n :ok\n end)\n \n assert {:ok, _} = Repo.query(\"SELECT * FROM users\")\nend\n\ntest \"authorizer timeout defaults to deny\" do\n EctoLibSql.set_authorizer(repo, fn _action, _table, _column, _context -\u003e\n Process.sleep(200) # Timeout is 100ms\n :ok\n end)\n \n assert {:error, _} = Repo.query(\"SELECT * FROM users\")\nend\n```\n\n**References**:\n- FEATURE_CHECKLIST.md section \"Medium Priority\" item 5\n- LIBSQL_FEATURE_MATRIX_FINAL.md section 10\n- libsql API: conn.authorizer()\n- SQLite authorizer docs: https://www.sqlite.org/c3ref/set_authorizer.html\n\n**Dependencies**:\n- Similar to update_hook implementation\n- Can share callback infrastructure\n\n**Priority**: P2 - Enables advanced security patterns\n**Effort**: 5-7 days (complex synchronous Rust→Elixir callback)\n**Complexity**: High (performance-critical, blocking callbacks)\n**Security**: Critical - must handle timeouts safely","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-xiy","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Implement Authorizer Hook for Row-Level Security","updated_at":"2026-01-08T14:13:19Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-08T14:12:15Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"27b81158fb73f95226c09303c717ea42d0f0caf133663d8c36820d189480c2a0","created_at":"2025-12-30T17:44:40Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Add support for update hooks to enable change data capture and real-time notifications.\n\n**Context**: Update hooks allow applications to receive notifications when database rows are modified. Critical for real-time updates, cache invalidation, and event sourcing patterns.\n\n**Missing API** (from FEATURE_CHECKLIST.md):\n- add_update_hook() - Register callback for INSERT/UPDATE/DELETE operations\n\n**Use Cases**:\n\n**1. Real-Time Updates**:\n```elixir\n# Broadcast changes via Phoenix PubSub\nEctoLibSql.set_update_hook(repo, fn action, _db, table, rowid -\u003e\n Phoenix.PubSub.broadcast(MyApp.PubSub, \"table:\\#{table}\", {action, rowid})\nend)\n```\n\n**2. Cache Invalidation**:\n```elixir\n# Invalidate cache on changes\nEctoLibSql.set_update_hook(repo, fn _action, _db, table, rowid -\u003e\n Cache.delete(\"table:\\#{table}:row:\\#{rowid}\")\nend)\n```\n\n**3. Audit Logging**:\n```elixir\n# Log all changes for compliance\nEctoLibSql.set_update_hook(repo, fn action, db, table, rowid -\u003e\n AuditLog.insert(%{action: action, db: db, table: table, rowid: rowid})\nend)\n```\n\n**4. Event Sourcing**:\n```elixir\n# Append to event stream\nEctoLibSql.set_update_hook(repo, fn action, _db, table, rowid -\u003e\n EventStore.append(table, %{type: action, rowid: rowid})\nend)\n```\n\n**Implementation Challenge**: \nCallbacks from Rust → Elixir are complex with NIFs. Requires:\n1. Register Elixir pid/function reference in Rust\n2. Send messages from Rust to Elixir process\n3. Handle callback results back in Rust (if needed)\n4. Thread-safety considerations for concurrent connections\n\n**Implementation Options**:\n\n**Option 1: Message Passing (Recommended)**:\n- Store Elixir pid in connection registry\n- Send messages to pid when updates occur\n- Elixir process handles messages asynchronously\n- No blocking in Rust code\n\n**Option 2: Synchronous Callback**:\n- Store function reference in registry\n- Call Elixir function from Rust\n- Wait for result (blocking)\n- More complex, potential deadlocks\n\n**Proposed Implementation (Option 1)**:\n\n1. **Add NIF** (native/ecto_libsql/src/connection.rs):\n ```rust\n #[rustler::nif]\n fn set_update_hook(conn_id: \u0026str, pid: Pid) -\u003e NifResult\u003cAtom\u003e {\n // Store pid in connection metadata\n // Register libsql update hook\n // On update: send message to pid\n }\n \n #[rustler::nif]\n fn remove_update_hook(conn_id: \u0026str) -\u003e NifResult\u003cAtom\u003e\n ```\n\n2. **Add Elixir wrapper** (lib/ecto_libsql/native.ex):\n ```elixir\n def set_update_hook(state, callback_fn) do\n pid = spawn(fn -\u003e update_hook_loop(callback_fn) end)\n set_update_hook_nif(state.conn_id, pid)\n end\n \n defp update_hook_loop(callback_fn) do\n receive do\n {:update, action, db, table, rowid} -\u003e\n callback_fn.(action, db, table, rowid)\n update_hook_loop(callback_fn)\n end\n end\n ```\n\n3. **Update connection lifecycle**:\n - Clean up hook process on connection close\n - Handle hook process crashes gracefully\n - Monitor hook process\n\n**Files**:\n- native/ecto_libsql/src/connection.rs (hook implementation)\n- native/ecto_libsql/src/models.rs (store hook pid in LibSQLConn)\n- lib/ecto_libsql/native.ex (wrapper and hook process)\n- lib/ecto/adapters/libsql.ex (public API)\n- test/update_hook_test.exs (new tests)\n- AGENTS.md (update API docs)\n\n**Acceptance Criteria**:\n- [ ] set_update_hook() NIF implemented\n- [ ] remove_update_hook() NIF implemented\n- [ ] Hook receives INSERT notifications\n- [ ] Hook receives UPDATE notifications\n- [ ] Hook receives DELETE notifications\n- [ ] Hook process cleaned up on connection close\n- [ ] Hook errors don't crash BEAM VM\n- [ ] Comprehensive tests including error cases\n- [ ] Documentation with examples\n\n**Test Requirements**:\n```elixir\ntest \"update hook receives INSERT notifications\" do\n ref = make_ref()\n EctoLibSql.set_update_hook(repo, fn action, db, table, rowid -\u003e\n send(self(), {ref, action, db, table, rowid})\n end)\n \n Repo.query(\"INSERT INTO users (name) VALUES ('Alice')\")\n \n assert_receive {^ref, :insert, \"main\", \"users\", rowid}\nend\n\ntest \"update hook doesn't crash VM on callback error\" do\n EctoLibSql.set_update_hook(repo, fn _, _, _, _ -\u003e\n raise \"callback error\"\n end)\n \n # Should not crash\n Repo.query(\"INSERT INTO users (name) VALUES ('Alice')\")\nend\n```\n\n**References**:\n- FEATURE_CHECKLIST.md section \"Medium Priority\" item 6\n- LIBSQL_FEATURE_MATRIX_FINAL.md section 10\n- libsql API: conn.update_hook()\n\n**Dependencies**:\n- None (can implement independently)\n\n**Priority**: P2 - Enables real-time and event-driven patterns\n**Effort**: 5-7 days (complex Rust→Elixir callback mechanism)\n**Complexity**: High (requires careful thread-safety design)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-xkc","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Implement Update Hook for Change Data Capture","updated_at":"2026-01-08T14:12:15Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:57:52Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"deadce99f0147461fc4819150938622e3f844878fb334b1b8974369d25348a0c","created_at":"2026-01-01T14:16:51Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-yr6","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":1,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Strengthen security test validation","updated_at":"2026-01-12T11:57:52Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"c03cb295b22308a3f7923fb8d775eefe0356b24f11403d954b47f2b3ad072899","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"TESTING.md (create or update)","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-z8d","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"File","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-17T08:54:46Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"df54f25c09e729a26adff555a322dd710c1e1b61786b5b876cbf3c056c9af386","created_at":"2025-12-30T17:35:52Z","created_by":"drew","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Not supported in migrations. SQLite 3.37+ (2021), libSQL 3.45.1 fully supports STRICT tables. Allowed types: INT, INTEGER, BLOB, TEXT, REAL. Rejects NULL types, unrecognised types, and generic types like TEXT(50) or DATE.\n\nDesired API:\n create table(:users, strict: true) do\n add :id, :integer, primary_key: true\n add :name, :string # Now MUST be text, not integer!\n end\n\nPRIORITY: Recommended as #5 in implementation order.\n\nEffort: 2-3 days.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-z8u","is_template":0,"issue_type":"feature","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"STRICT Tables (Type Enforcement)","updated_at":"2026-01-17T08:54:46Z","waiters":"","wisp_type":"","work_type":"mutex"} +{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":"2026-01-12T11:58:17Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"d36727f09f56e0fe183670397b8b10b056b542ce8a4bec985ec17ad22a0f4642","created_at":"2026-01-08T21:34:13Z","created_by":"","crystallizes":0,"defer_until":null,"delete_reason":"","deleted_at":null,"deleted_by":"","description":"Track performance across versions, validate improvements, identify bottlenecks","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"el-zba","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"original_type":"","owner":"","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":null,"status":"closed","target":"","timeout_ns":0,"title":"Impact","updated_at":"2026-01-12T11:58:17Z","waiters":"","wisp_type":"","work_type":"mutex"} diff --git a/.beads/backup/labels.jsonl b/.beads/backup/labels.jsonl new file mode 100644 index 0000000..e4dd3d0 --- /dev/null +++ b/.beads/backup/labels.jsonl @@ -0,0 +1,4 @@ +{"issue_id":"el-ffc","label":"status:in-progress"} +{"issue_id":"el-yr6","label":"security"} +{"issue_id":"el-yr6","label":"testing"} +{"issue_id":"el-yr6","label":"tests"} diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..79de9c1 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,23 @@ +name: GitHub Actions Security Analysis with zizmor 🌈 + +on: + push: + branches: ["main"] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor 🌈 + uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 diff --git a/.gitignore b/.gitignore index de6a37a..0c4d98e 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,7 @@ z_ecto_libsql_test* # Implementation summaries and temporary docs TEST_AUDIT_REPORT.md TEST_COVERAGE_ISSUES_CREATED.md + +# Dolt database files (added by bd init) +.dolt/ +*.db