Document: loomworks-topic-filtered-recall-scoping-note-v0_1
Version: v0.1
Status: scoping note — settles the shape of the queued §18.2 work (topic-filtered / relevance-ranked recall) far enough to commission a Step 0 inspection brief and a CR. Four Operator decisions named below.
Date: 2026-07-28
Grounding: read-only inspection of engine main 1aac815 (router.py — _route_ask_about_past_input :1529, _route_recall_personal :1566, _PAST_INPUT_LIMIT :119, _PERSONAL_RECALL_LIMIT comment :121–127; prompt.py _format_past_input :481; classifier.py intent declarations :94–103); queued-directions v0.30 §18.2 (priority raised 2026-07-28 with V5 evidence); loomworks-vertical-vc-diligence-cc-inspection-report-v0_1 finding as carried in §18.2.
Design inputs on file: loomworks-recordable-facts-and-topic-investigation-v0_1 (topic-carrying assertions); loomworks-conversation-search-scoping-note-v0_1 (message-stream search — adjacent, distinct, unchanged by this note).
Today, when you ask the Companion what is on record, it does not search. It grabs the 50 most recent committed notes and hopes the answer is among them. Your question never selects anything. On a small engagement this looks fine; on a real one, anything said early falls outside the window and the Companion silently fails to find it — the exact probe a skeptical prospect would run. No search index has ever existed in the engine. A standing conduct rule already blocks live recall demonstrations in front of prospects until this lands.
This note settles the fix in two tiers. Tier 1 (this build): a real text search — the question's terms select the matching notes from the complete committed record, using PostgreSQL's built-in full-text search with an index. Early content becomes findable by its words. No new infrastructure. Tier 2 (later, after the topic investigation settles): recall by meaning, so "what's the weather note" finds "it is sunny" — which word-matching cannot do.
What you decide: the four decisions in the last section — the tier split, which recall path builds first, what happens on zero matches, and whether Tier 1 relaxes the no-demo rule.
From the venture-diligence vertical inspection (V5, engine 1aac815), carried into queued-directions v0.30 §18.2:
_PAST_INPUT_LIMIT = 50, router.py:119). The Operator's question never selects anything.to_tsvector, tsquery, ILIKE, pg_trgm.
| | ask_about_past_input (engagement) | recall_personal (personal, CR-2026-100) |
|---|---|---|
| Selection | 50 most recent committed (_PAST_INPUT_LIMIT) | Wholesale — complete personal set (_PERSONAL_RECALL_LIMIT, effectively-wholesale backstop) |
| Who composes | Responder (LLM focuses on the question) | Server-composed faithful readout (CR-2026-129 — exact list, real count, overflow disclosed) |
| Completeness posture | Source comment: a pagination cap, not a completeness contract | The CR's explicit "wholesale, no silent budget-clip" requirement |
| Question-driven? | No | No |
CR-2026-129's tests deliberately left ask_about_past_input untouched — "carved out to the semantic-recall foundation" (test_cr_2026_129_recall_truthfulness.py:182–202). This note is that reserved work being picked up.
Replace the recency window with query-driven selection over the complete committed set:
tsvector expression (or stored generated column) over assertion content with a GIN index. Native to the existing database; no external search service, no embeddings, no new dependency. Migration required (index; possibly a generated column) — migration discipline applies (single head; confirm current head at Step 0).tsquery against committed assertions on the engagement, return matches ranked (ts_rank) — not the 50 newest. Early content becomes findable by its words regardless of age or record size.What Tier 1 fixes: the keyword-shaped probe ("what did I say about the vesting schedule?") finds the early note. What it does not fix: the paraphrase-shaped probe ("what's the weather note" → "it is sunny"). That is Tier 2's gap, and it is honest to say so.
Recall by meaning rather than word overlap. Two candidate mechanisms, deliberately not chosen here: topic-carrying assertions (the recordable-facts investigation's direction — assertions carry a topic at commit time; recall matches on topic) and/or embedding search. The recordable-facts-and-topic investigation is the design input and must settle before any Tier 2 build — it touches Memory structure and the thesis. Tier 1 neither blocks nor prejudges it: a full-text index over content coexists with either mechanism.
Tier 1 targets _route_ask_about_past_input (the engagement path — where the evidenced failure lives and where record sizes are real). recall_personal stays wholesale: at personal-memory sizes the complete set is correct, its faithful readout already discloses overflow, and CR-2026-100's "wholesale is the foundation; any filter is a subset" framing holds. When personal size warrants, the same search path extends to it — a follow-on, not this build.
ask_about_past_input, recall_personal) are untouched; this is retrieval, not routing.list_assertions signature and whether search pushes into it or a sibling query; confirm current alembic head for the index migration; confirm the term-extraction seam (route-level vs a small shared helper); confirm _format_past_input's rendering path for the honest-selection readout; report any existing ts_rank/FTS usage anywhere (expected: none).loomworks-record/scoping-notes/; queued-directions §18.2 gains a cross-reference at its next bump.DUNIN7 — Done In Seven LLC — Miami, Florida Loomworks — topic-filtered recall — scoping note — v0.1 — 2026-07-28 Tier 1: deterministic full-text search (tsvector + GIN) replaces the 50-recent window on engagement recall — query-driven selection over the complete committed set, honest-selection readout, migration-bearing. Tier 2: semantic/topic recall, gated on the recordable-facts-and-topic investigation. Personal recall stays wholesale. Four Operator decisions: tier split, path scope, zero-match behavior, conduct-rule relaxation.