Skip to content

refactor: eliminate fragile error-string matching#951

Merged
cpcloud merged 8 commits intomicasa-dev:mainfrom
cpcloud:worktree-magical-plotting-catmull
Apr 18, 2026
Merged

refactor: eliminate fragile error-string matching#951
cpcloud merged 8 commits intomicasa-dev:mainfrom
cpcloud:worktree-magical-plotting-catmull

Conversation

@cpcloud
Copy link
Copy Markdown
Collaborator

@cpcloud cpcloud commented Apr 18, 2026

Summary

  • prepareFTSQuery collapses to the canonical phrase-wrap escape documented by SQLite's FTS5 author: each whitespace-separated token becomes a quoted phrase (with internal " doubled) suffixed with * for prefix matching, AND-joined. The output is always syntactically valid, so isFTSSyntaxError, balanced-delimiter validation, and the safe-fallback helper all delete. Drops user-facing FTS5 operator support (AND/OR/NOT/parens/quotes); partial operator syntax mid-keystroke errored anyway in a type-as-you-go search box.
  • isNetworkError matches via errors.Is against syscall.ECONNREFUSED, ENETUNREACH, and EHOSTUNREACH -- nothing else. Go's syscall package maps platform-specific connection errors to these cross-platform sentinels, so this works on Linux, macOS, and Windows alike.
  • Doc claim about FTS5 operators removed to match the new behavior.
  • Two existing tests in client_test.go previously fabricated bare connection errors via errors.New("dial tcp: connection refused"); updated to wrap real syscall.ECONNREFUSED so they exercise the same path production traffic hits.

Reference: SQLite forum — escaping punctuation in FTS queries (Dan Kennedy)

@cpcloud cpcloud added refactor Code restructuring without behavior change data Data layer, models, database llm LLM and chat features labels Apr 18, 2026
@cpcloud cpcloud changed the title refactor: replace fragile error string-matching with typed checks refactor: eliminate fragile error-string matching Apr 18, 2026
@cpcloud cpcloud added the documentation Improvements or additions to documentation label Apr 18, 2026
cpcloud added 7 commits April 18, 2026 06:06
Harden prepareFTSQuery to validate balanced quotes and parens and to
require at least one alphanumeric term; unbalanced or term-less inputs
fall back to a sanitized word search instead of reaching SQLite as bad
syntax. Replace isFTSSyntaxError with a typed check on *sqlite.Error
narrowed by SQLITE_ERROR before any message inspection.
isNetworkError now tries errors.Is against syscall.ECONNREFUSED,
ENETUNREACH, and EHOSTUNREACH first, catching wrapped stdlib errors
that don't format with the previously-matched substrings (notably
"no route to host"). The string fallback remains for provider chains
that drop the syscall layer, e.g. Windows connectex.
prepareFTSQuery now applies the escape pattern documented by SQLite's
FTS5 author: split on whitespace, wrap each token as a quoted phrase
with internal " doubled, append * for prefix matching, AND-join. The
output is always syntactically valid FTS5, so the isFTSSyntaxError
safety net, balanced-delimiter validation, hasFTSTerms guard, and
ftsSafeWordsQuery fallback all go away. Drops user-facing FTS5
operator support (AND/OR/NOT/parens/quotes) -- partial operator syntax
mid-keystroke errored anyway in a type-as-you-go search box.
The document search no longer interprets AND/OR/NOT/parens/quotes as
FTS5 operators -- each whitespace-separated word is wrapped as a phrase
and prefix-matched. Update the description to match.
The earlier commit kept four substring checks "for Windows connectex
compatibility" -- an aspirational claim from the original comment that
was never verified. Go's syscall package maps platform-specific
connection errors to the cross-platform sentinels (ECONNREFUSED,
ENETUNREACH, EHOSTUNREACH), so errors.Is works on every supported
platform. If a CI failure on Windows ever proves otherwise, restore
the fallback with a real reproducer attached.

Update the two existing tests that fabricated bare errors via
errors.New("dial tcp: connection refused") to wrap real syscall
sentinels instead, and delete TestIsNetworkErrorString which only
existed to exercise the removed fallback.
TestSearchDocumentsBadSyntaxGraceful previously ran against an empty
store, so it only proved that malformed inputs do not error -- a
regression that broadened them into matching real terms would still
pass. Insert a document whose tokens share no prefix with any test
query so the no-match assertion is meaningful.

TestIsNetworkError gains an explicit case asserting that a bare error
whose message contains "connection refused" is NOT classified as a
network error, pinning the new contract that only wrapped syscall
sentinels qualify.
The reviewer noted that TestSearchDocumentsBadSyntaxGraceful does not
catch the case where a malformed input like "(kitchen" normalizes to
the inner term "kitchen". In our model that is the intended behavior:
phrase-wrap escape relies on the FTS5 tokenizer to extract usable terms
from whatever surrounding punctuation the user typed mid-keystroke.

Add an explicit positive test asserting that "(kitchen", "kitchen)",
'"kitchen', and "kitchen*" all match a document containing "kitchen",
so a future reader doesn't try to "fix" this by routing malformed
input to no-results.
@cpcloud cpcloud force-pushed the worktree-magical-plotting-catmull branch from fe878fd to 7209684 Compare April 18, 2026 10:09
@cpcloud cpcloud enabled auto-merge (squash) April 18, 2026 10:10
CI on windows-latest failed with TestPingServerDown / TestListModelsServerDown
asserting "cannot reach" in the error message. The actual error was

    Get "http://127.0.0.1:1/v1/models": dial tcp 127.0.0.1:1:
    connectex: No connection could be made because the target machine
    actively refused it.

net/http on Windows wraps connectex errors in a way that drops the
syscall.Errno layer, so errors.Is(err, syscall.ECONNREFUSED) returns
false even for a refused connection. The earlier comment that called
this scenario aspirational was wrong -- it is real. Restore the
original four substring checks (connection refused, actively refused,
host is unreachable, network is unreachable) after the typed checks.
@cpcloud cpcloud merged commit c7c7a55 into micasa-dev:main Apr 18, 2026
28 checks passed
@cpcloud cpcloud deleted the worktree-magical-plotting-catmull branch April 18, 2026 10:50
@cpcloud
Copy link
Copy Markdown
Collaborator Author

cpcloud commented Apr 18, 2026

Pushed WSA-typed errno fix on top — pinging GitHub to sync the new HEAD.

cpcloud added a commit that referenced this pull request Apr 18, 2026
## Summary

- Follow-up to #951. The Windows CI failure resolved there with a
string-matching fallback has a deeper, fully typed fix.
- `syscall.ECONNREFUSED` on Windows is defined as \`APPLICATION_ERROR |
iota\` (an invented value, not the real WSA error code), so
\`errors.Is(err, syscall.ECONNREFUSED)\` *always* returns false on
Windows for real network errors. Same for \`ENETUNREACH\` and
\`EHOSTUNREACH\`.
- Split \`connectionErrnos\` across build-tagged files: POSIX uses the
standard \`syscall.*\` constants; Windows uses
\`golang.org/x/sys/windows.WSAECONNREFUSED\` / \`WSAENETUNREACH\` /
\`WSAEHOSTUNREACH\` (already a direct dep, exposed as \`syscall.Errno\`
so \`errors.Is\` works).
- \`isNetworkError\` collapses to a simple loop over the
platform-specific list; the substring fallback comes out. Zero string
matching on either platform.
- \`AGENTS.md\`: codify "no \`err.Error()\` substring classification"
with the Windows errno caveat documented inline so the next agent
doesn't fall into the same trap.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

data Data layer, models, database documentation Improvements or additions to documentation llm LLM and chat features refactor Code restructuring without behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant