# Re-Test memory-journal-mcp — Text Search

**Scope:** FTS5 search, phrase/prefix/boolean operators, LIKE fallback, hybrid auto-mode, date range, cross-DB merging, and filter parameters.

**Prerequisites:**

- Confirm MCP server instructions were auto-received before starting.
- **Use direct MCP tools exclusively.** Do NOT use Code Mode (`mj_execute_code`) for these tests. Code Mode tests are handled separately in the `codemode` track. If you must use a script to supplement a test, use a standard Node/shell script.
- Seed data from `test-seed.md` must be present. MCP server instructions auto-injected.

**Workflow after testing:**

1. Create a plan to fix any issues found or potential improvement opportunities, including changes to `constants/server-instructions.ts` or this file. **If you encounter parameter or tool hallucinations during testing, intercept them gracefully in the server code (e.g., `codemode.ts`) so future agents succeed automatically.**
2. Use `code-map.md` as a source of truth and ensure fixes comply with the `mcp-builder` skill.
3. If you made code changes/fixes, update `UNRELEASED.md` and commit without pushing. If tests pass cleanly, do NOT update `UNRELEASED.md`. Then, stop so the **USER** can verify with `npm run lint`, `npm run typecheck`, `npm run test`, and `npm run test:e2e`.
4. After user completes verification, re-test fixes with direct MCP calls.
5. Provide a very brief final summary.
   - **Include Total Token Estimate:** Sum the `_meta.tokenEstimate` from all tool responses (or read `memory://metrics/summary`) and report the total estimated tokens that actually entered the context window during this test pass.

---

## Phase 3: Text Search

| Test                  | Command/Action                                                                                                      | Expected Result                                                                                                                                                                               |
| --------------------- | ------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| FTS5 search           | `search_entries(project_number: 5, query: "architecture")`                                                          | ≥ 1 result — S1 and S11 both indexed; BM25 may rank team entry first; use `search_entries(project_number: 5, query: "authentication architecture")` to target S1 specifically                 |
| FTS5 phrase           | `search_entries(project_number: 5, query: "\"error handling\"")`                                                    | ≥ 1 result (S2) — exact phrase match only; the query param value must contain the literal quotes as part of the string: `"error handling"` (not JSON escape sequences)                        |
| FTS5 prefix           | `search_entries(project_number: 5, query: "auth*")`                                                                 | ≥ 2 results (S1, S8) — matches "authentication", "authorization", etc.                                                                                                                        |
| FTS5 boolean NOT      | `search_entries(project_number: 5, query: "deploy NOT staging", mode: "fts")`                                       | Returns S3, S11 but NOT S5 (S5 contains "staging")                                                                                                                                            |
| FTS5 boolean OR       | `search_entries(project_number: 5, query: "deploy OR release", mode: "fts")`                                        | ≥ 2 results (S3, S4, S5 expected)                                                                                                                                                             |
| FTS5 fallback         | `search_entries(project_number: 5, query: "test's", mode: "fts")`                                                   | ≥ 1 result (S6) — LIKE fallback, single quotes are FTS5-unsafe                                                                                                                                |
| FTS5 special chars    | `search_entries(project_number: 5, query: "100%", mode: "fts")`                                                     | ≥ 1 result (S6) — LIKE fallback, `%` is FTS5-unsafe                                                                                                                                           |
| Hybrid auto-mode      | `search_entries(project_number: 5, query: "how did we fix performance")`                                            | Auto-mode heuristics trigger RRF hybrid bridging FTS+Vector, expected to return S7                                                                                                            |
| Forced semantic mod   | `search_entries(project_number: 5, query: "improving performance", mode: "semantic")`                               | Bypasses FTS5, running pure vector similarity, expects S7/S10                                                                                                                                 |
| Date range            | `search_by_date_range(project_number: 5, start_date: "2026-01-01", end_date: "2026-01-31")`                         | Returns `structuredContent` array                                                                                                                                                             |
| Cross-DB search       | `search_entries(project_number: 5, query: "test")`                                                                  | Results include `source: 'personal' \| 'team'` marker on each entry                                                                                                                           |
| Cross-DB date         | `search_by_date_range(project_number: 5, start_date: "2026-01-01", end_date: "2026-12-31")`                         | Results include `source` marker merging personal + team entries                                                                                                                               |
| Invalid date fmt      | `search_by_date_range(project_number: 5, start_date: "Jan 1", end_date: "Jan 31")`                                  | Structured error: `{ success: false, error: "..." }` with YYYY-MM-DD hint                                                                                                                     |
| Filter by issue       | `search_entries(project_number: 5, issue_number: 44)`                                                               | Returns entries linked to issue #44                                                                                                                                                           |
| Filter by PR status   | `search_entries(project_number: 5, pr_status: "merged")`                                                            | Returns entries with `prStatus: "merged"`                                                                                                                                                     |
| Filter by workflow    | `search_entries(project_number: 5, workflow_run_id: <N>)`                                                           | Returns entries linked to workflow run                                                                                                                                                        |
| Filter by project     | `search_entries(project_number: 5)`                                                                                 | Returns entries linked to project #5                                                                                                                                                          |
| Filter by is_personal | `search_entries(project_number: 5, query: "test", is_personal: true)`                                               | Only personal entries returned                                                                                                                                                                |
| Filter by tags        | `search_entries(project_number: 5, tags: ["testing"])`                                                              | Only entries with "testing" tag returned (S6, S9)                                                                                                                                             |
| Filter by entry_type  | `search_entries(project_number: 5, entry_type: "planning")`                                                         | Only entries with entry_type "planning" returned (S6, S13)                                                                                                                                    |
| Filter by date        | `search_entries(project_number: 5, start_date: "2026-01-01", end_date: "2026-12-31")`                               | Results constrained to the specified date range                                                                                                                                               |
| Date range + type     | `search_by_date_range(project_number: 5, start_date: "2026-01-01", end_date: "2026-12-31", entry_type: "planning")` | Only "planning" entries in date range                                                                                                                                                         |
| Date range + tags     | `search_by_date_range(project_number: 5, start_date: "2026-01-01", end_date: "2026-12-31", tags: ["test"])`         | Only entries with "test" tag in date range                                                                                                                                                    |
| Date range + personal | `search_by_date_range(project_number: 5, start_date: "2026-01-01", end_date: "2026-12-31", is_personal: true)`      | Only personal entries in date range                                                                                                                                                           |
| Date range + project  | `search_by_date_range(start_date: "2026-01-01", end_date: "2026-12-31", project_number: 5)`                         | Only project #5 entries in date range                                                                                                                                                         |
| Inverted date range   | `search_by_date_range(project_number: 5, start_date: "2026-12-31", end_date: "2026-01-01")`                         | Returns `{ success: false, error: "Invalid date range: start_date (...) is after end_date (...)", code: "VALIDATION_ERROR", suggestion: "Ensure start_date is before or equal to end_date" }` |
| Query too long        | `search_entries(project_number: 5, query: "a".repeat(300))`                                                         | Returns `{ success: false, error: "..." }` with Zod validation hint                                                                                                                           |
| Importance sort       | `search_entries(project_number: 5, query: "architecture", sort_by: "importance")`                                   | Results sorted by `importanceScore` DESC with `importanceScore` field on every entry                                                                                                          |
| Importance sort rcnt  | `get_recent_entries(project_number: 5, limit: 5, sort_by: "importance")`                                            | Results sorted by `importanceScore` DESC with `importanceScore` field on every entry                                                                                                          |
| Importance sort date  | `search_by_date_range(project_number: 5, start_date: "2026-01-01", end_date: "2026-12-31", sort_by: "importance")`  | Results sorted by `importanceScore` DESC with `importanceScore` field on every entry                                                                                                          |
| Default no overhead   | `search_entries(project_number: 5, query: "architecture")`                                                          | No `importanceScore` field present on entries (default `sort_by: 'timestamp'`)                                                                                                                |

> [!TIP]
> **Token Conservation in Code Mode:** When testing search across dozens of queries via `mj_execute_code`, do NOT append the full `res` objects to your results array. Map responses to `{ success: true, count: res.entries?.length || 0 }` to prevent returning massive JSON payloads (megabytes in size) that artificially inflate `_meta.tokenEstimate`.

> [!NOTE]
> **Cross-DB Search Behavior:** When a team DB is present, per-DB queries fetch `limit × 2` (capped at 500) to prevent BM25 ranking in one DB from silently dropping entries before the cross-DB merge. The user's requested `limit` is applied after merging.
>
> **Code Mode API Group Structure:** When testing `mj_execute_code`, methods are bound to specific groups. Key mapping: `listTags` → `mj.core`, `mergeTags` → `mj.admin`, `getStatistics` → `mj.analytics`. Use `mj.help()` or `mj.<group>.help()` to discover available methods per group.

---

## Success Criteria

> **Important:** Copy these success criteria into your internal task artifact and track your progress there. Do not check off items in this file.

- `search_entries` with `mode: 'auto'` correctly identifies conversational prompts and bridges keyword + semantic via Reciprocal Rank Fusion
- `search_entries` `mode: 'fts'` phrase, prefix, and boolean operators work correctly
- `search_entries` gracefully falls back to LIKE for FTS5-unsafe queries (single quotes, `%`)
- `search_entries` filters work: `issue_number`, `pr_status`, `workflow_run_id`, `project_number`, `is_personal`, `tags`, `entry_type`, `start_date`, `end_date`
- `search_by_date_range` filters work: `entry_type`, `tags`, `is_personal`, `project_number`
- `search_by_date_range` rejects non-YYYY-MM-DD date strings with structured errors
- `search_entries` rejects queries exceeding 250 characters with structured Zod errors
- Cross-DB merging includes `source: 'personal' | 'team'` marker
- `search_entries` with `sort_by: 'importance'` returns entries with `importanceScore` field sorted descending
- `get_recent_entries` with `sort_by: 'importance'` returns entries with `importanceScore` field sorted descending
- `search_by_date_range` with `sort_by: 'importance'` returns entries with `importanceScore` field sorted descending
- Default `sort_by` (timestamp) produces zero overhead — no `importanceScore` field present
