Skip to content

The demo backend matches a query the way a real workspace does - #35

Merged
illodev merged 2 commits into
mainfrom
fix/t-0202-demo-search-parity
Aug 7, 2026
Merged

The demo backend matches a query the way a real workspace does#35
illodev merged 2 commits into
mainfrom
fix/t-0202-demo-search-parity

Conversation

@illodev

@illodev illodev commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Draining the review queue. Five cards closed; one code change, four verifications.

T-0202 — the demo and the server disagreed about what a search matches

api.demo.ts answered q with a case-insensitive includes over the raw title, body, path and id. The server tokenizes: a body matches by whole word, only a title falls back to a substring, and identity and metadata count too. A partial word therefore found a body on the hosted demo and nothing against a real workspace — and the demo is what most readers see first.

The card's scope was one site short. It named the three record list endpoints. The palette's lexical branch had a rule of its own again — id and title only, weighted 100/50/25/10 — and the server answers a lexical search with the same ranker as its list routes (searchProjectRecordsHybrid falls through to searchProjectRecords with no provider configured). So the palette in the demo could not find a record by a word in its body at all, and ranked what it did find in an order the server never produces. All four sites go through one mirror now.

The mirror carries the filter grammar and negation too, not just the tokenizer — leaving those out would have kept status:draft and -term matching as literal substrings, a disagreement the card does not mention.

It is a mirror rather than an import because the server module reaches the filesystem and the demo is a static bundle. What keeps a mirror honest is the test, and that is criterion 3 of the card: demo-parity drives both implementations over one fixture across 23 queries and compares ordered results, instead of asserting each against expectations written twice — which is how these two drifted in the first place.

Verified: against the live server on this repo's 55 memory records, 12 queries agree on count and on ordered ids — whole words, partials, metadata-only hits, status:/tag: filters, a negation, an accented pair. In the built static demo in Chromium: surviving finds the one body that carries it, survivi finds nothing, and the palette reaches a body-only word. Mutation-proven three ways; restoring the substring body match reports the card's exact symptom (nvoic finds a record in the demo and not on the server).

Four cards that were shipped but never proven

review means verification is pending. No code changed for these — each now carries a run.

T-0215 232 accepted (word, flag) pairs — the card's own number — every one printed by its word's --help in the shipped dist binary, word-bounded so --to is not satisfied by --tags. Mutating the generator to drop one flag per row fails the suite naming the exact flags.
T-0216 changelog patch on a committed fragment now gives a one-line diff, which is the card's own reproduction. A record whose blank line was spliced away by hand gets it back — and so do doc patch and memory patch, so the fix really is in the one shared writer. CRLF survives with 0 bare LF; the same body twice leaves content and mtime untouched.
T-0217 Fube live: 0 errors, 0 link findings across 1784 cards. A zero can also mean the scan never ran, so the same 180-file tree went into a scratch workspace: 683 link warnings without routeRoots, 4 with it.
T-0195 Its last criterion was one the previous agent deferred to you rather than tick, and rightly. Resolved as: one sentence per corpus is what the criterion asks for, since the defect was three sentences for one corpus. Its blocker was T-0202, so the record sentence is now true of both backends.

Two of my own expectations were the false thing, not the code

Both are recorded on the cards rather than quietly worked around:

  • I checked the whole-word rule with window against windows, expecting the partial to find nothing. It finds four records on both backends — a title carries Windows, and the title fallback matches a substring by design. A discriminator has to be a token no title carries and whose prefix no title token contains; surviving/survivi, picked from the snapshot rather than guessed.
  • I checked that a tag finds cards with filters. It finds none, because its only card is closed and the work strip hides closed cards by default — the filter doing its job.

Still in review, and genuinely so

T-0214. Its open criterion is Fube's own deployed board, and Fube's T-2150 is itself in review there. Not ours to close.


pnpm run check green — 471 + 10 tests, strict ratchet with no regressions. doctor 0 errors 0 warnings, memory verify and changelog verify clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D3LTdq3mzMAQ98rwBegGjU

illodev and others added 2 commits August 7, 2026 22:39
T-0202. `api.demo.ts` answered `q` with a case-insensitive `includes` over the
raw title, body, path and id. The server tokenizes: a body matches by whole
word, only a title falls back to a substring, and identity and metadata count
too. So a partial word found a body on the hosted demo and nothing against a
real workspace — and T-0195 had to write a placeholder that was exactly true of
the server and merely understated for the demo.

The card scoped this to the three record list endpoints and there was a fourth
site with a rule of its own: the palette's lexical branch scored id and title
only, weighted 100/50/25/10. The server answers a lexical search with the same
ranker as its list routes, so the palette in the demo could not find a record by
a word in its body at all, and ranked what it did find in an order the server
never produces. All four go through one mirror now, filters and negation
included — leaving those out would have kept `status:draft` and `-term` matching
as literal substrings.

Mirrored rather than imported because the server module reaches the filesystem
and the demo is a static bundle. What keeps a mirror honest is the test:
`demo-parity` drives both implementations over one fixture across 23 queries and
compares ordered results, rather than asserting each against expectations
written twice — which is how these two drifted in the first place.

Verified against the live server on this repo's 55 memory records: 12 queries
agree on count and on ordered ids, including partials, metadata-only hits,
`status:`/`tag:` filters, a negation and an accented pair. In the built static
demo in Chromium, `surviving` finds the one body carrying it, `survivi` finds
nothing, and the palette reaches a body-only word. Mutation-proven three ways.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3LTdq3mzMAQ98rwBegGjU
`review` means verification is pending, and these four had been shipped without
anyone going back to prove them. Nothing changed in the code; what changed is
that each now carries a run.

T-0215 — 232 accepted (word, flag) pairs, the card's own number, and every one
is printed by its word's `--help` in the shipped dist binary, word-bounded so
`--to` is not satisfied by `--tags`.

T-0216 — `changelog patch` on a committed fragment gives a one-line diff, which
is the card's own reproduction. A record whose blank line was spliced away by
hand gets it back, and so do `doc patch` and `memory patch`, so the fix is in
the shared writer rather than one of the four call sites. CRLF survives with no
bare LF, and the same body twice leaves the mtime alone.

T-0217 — Fube live: 0 errors and 0 link findings over 1784 cards. A zero can
also mean the scan never ran, so the same 180-file tree was copied into a
scratch workspace: 683 link warnings without `routeRoots`, 4 with it.

T-0195 — the last criterion was one the previous agent deferred to the owner
rather than tick, and rightly. Resolved as: one sentence per corpus is what it
asks for, since the defect was three sentences for one corpus. Its blocker was
T-0202, so the record sentence is now true of both backends and not only of the
server.

Two of my own expectations turned out to be the false thing rather than the
code, and both are recorded on the cards. `window` finds four records on both
backends, because a title carries `Windows` and the title fallback matches a
substring by design. And the tag `filters` finds nothing because its only card
is closed and the work strip hides closed cards — the filter doing its job.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3LTdq3mzMAQ98rwBegGjU
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
workfile Ready Ready Preview Aug 7, 2026 8:41pm
workfile-site Ready Ready Preview Aug 7, 2026 8:41pm

@illodev
illodev merged commit b6eadd0 into main Aug 7, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant