- Plexer: always-active terminal multiplexing. NativePlexerController/ TmuxPlexerController share one interface, chosen by which env var is already set (TAI_PLEXER_SOCKET vs TMUX_PANE); NativePlexerBackend is the pty.fork()-based wrapper (raw mode, SIGWINCH resize forwarding, a Unix-socket control protocol, continuous session logging, a Ctrl+A prefix key distinct from tmux's own Ctrl+B). Auto-wraps new interactive shells via a .bashrc hook mirroring the existing command-not-found install pattern, gated on $TMUX/$TAI_PLEXER_ACTIVE/ a $HOME/.tai-no-plexer bypass. New plexer_send/read/type/status tools, env-gated on a detected controller. - tests/container.py + docker-compose.yml + tests/docker/Dockerfile: a disposable podman/docker image for the tests that must never touch a real machine (installing the plexer hook into a real ~/.bashrc, a live tmux session). Required suite (test_seal.py, test_tai.py, tests/plexer.py, tests/bashrc.py) gates the result; tests/rag.py runs as best-effort since its real-LLM dependency can't be satisfied inside a disposable container with no nested engine. - tests/bashrc.py: real end-to-end hook install + bash -i auto-wrap, anti-recursion, tmux-active skip, non-interactive skip, a live TmuxPlexerController test, and an install/uninstall round-trip. - tests/plexer.py: NativePlexerBackend under a real pty, plus a dedicated robustness pass (file permissions, >64KB payload framing, a malformed control request, a genuine self-deadlock fix in the write-to-child path). - tai_test.py: the shared async process-driving framework (pipe/pty transport, group-wide kill-and-reap, a StdinBridge for --interactive) both test files build on. - ChatClient last-resort backend: when there's no local opencode CLI but the sandbox box is already running, install opencode into it (official Node tarball, not apt -- apt's nodejs/npm pull in webpack/babel/jest as transitive deps) and route through it instead of giving up. Only reachable after the first retry round already failed everything else; never creates the box itself. TmuxPlexerController.status() fixed to use `list-panes` instead of `display-message -p`, which silently fell back to the default pane instead of erroring against a dead target. - Fix 4 pre-existing test_tai.py bugs surfaced by running it in a clean environment for the first time: two stale mock signatures (subprocess.run's stdout= kwarg, Chat.complete's deadline= kwarg), one $TMUX-dependent catalog assertion, one tty-dependent color assertion. - research/: a sourced research archive (Source Dossier/Citation Card/Conclusion tiers) backing the above design decisions, with its own audit.py consistency checker. - .gitignore: fix a blanket *.md rule that was about to silently drop the entire research/ archive from version control; add __pycache__/ and the one-off debug transcript this session started from. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2.9 KiB
2.9 KiB
sqlite_search: Full-Text, Vector, and Hybrid Search for ActiveRecord
Metadata
| Field | Value |
|---|---|
| Institute | Molodetz Institute — Research & Retrieval Division |
| Subject | sqlite-rag-hybrid-search |
| Document type | Source Dossier |
| Tier | 1 |
| Source title | sqlite_search: Full-text, vector, and hybrid search for ActiveRecord, without leaving SQLite |
| Source URL | https://github.com/radioactive-labs/sqlite_search |
| Source publisher / author | GitHub organization radioactive-labs |
| Source type | repository |
| Source date (published) | Unknown — not stated |
| Date researched | 2026-10-09 |
| Researched by | tai (agent), model: Claude Sonnet 5, on behalf of retoor |
| Research method | web search synthesis |
| Confidence | Medium |
| Status | Active |
Summary
A Ruby/ActiveRecord library adding full-text, vector, and hybrid search to a Rails app using only SQLite — no separate search service. Notably, its query parser strips FTS5 operator syntax out of untrusted input before it reaches SQLite, as a deliberate safety measure.
Key findings
- Demonstrates the "search stays inside SQLite, no external search service" philosophy applied at the application-framework level (ActiveRecord/Rails), not just in a one-off script.
- Explicitly sanitizes/strips FTS5 query operator syntax from user input — a concrete, named security practice (preventing FTS5 MATCH syntax injection from untrusted search boxes).
- Offers full-text, vector, and hybrid modes as named options, implying all three were considered legitimate, supported configurations by the library's authors, not just a single blessed path.
Pros / strengths
- The input-sanitization detail is a genuinely transferable, concrete security practice independent of the Ruby ecosystem: raw user text should never be handed to FTS5's MATCH operator unescaped.
- Evidence that "hybrid search entirely inside one SQLite file" is being productized as a reusable library, not only hand-rolled per project.
Cons / limitations / caveats
- Ruby/ActiveRecord-specific; no architectural detail on its vector-search implementation (which extension, if any) was recoverable from the search synthesis.
- No benchmark or production-scale evidence was found during this research pass — only the existence and stated feature set of the library.
Relevance to tai
The FTS5-input-sanitization point directly reinforced existing tai behavior worth double-checking: tai.py's own fts_escape()/fts_match() functions already tokenize and quote search terms before building the FTS5 MATCH expression, which is the same defensive pattern this library implements independently. This source corroborated that tai's existing approach (never passing raw user text into a MATCH clause) matches recognized practice elsewhere.
Changelog
| Date | Change |
|---|---|
| 2026-10-09 | Initial dossier created. |