Skip to content

Indexed memory search candidate evidence

Task: 59129a4a-9160-4cfd-a9c4-b58a073a848c, frozen version 26, read through HeyAira MCP before resuming work. Repository: h8v6/heyaira. Branch: heyaira/59129a4a. Base commit: 4f487bbdf9006cf0a37390205f1721d7c6bbeb71. Continued from candidate 6126ee56dcfacb00c11d97c4aa937ec9e67fa0c5. The memory-search implementation and database regressions are unchanged. This revision additionally fixes the deployment-test fixture's inheritance of the verifier's read-only Git object cache and adds a targeted regression. The fixes and evidence are amended into the single local candidate commit. The candidate is the local commit containing this evidence file; its SHA is recorded in the task_result receipt. No push, PR, merge, installation or deploy was performed. The only external write is the authorized task_result note/receipt.

Implementation and criterion evidence

  • summary-searchable: migrations/036_memories_search_vector.sql adds a stored generated search_vector using to_tsvector('simple', ...) over title, summary, body and rationale. Nullable summary/rationale use coalesce. tests/test_memory_search_e2e.py creates an entry through the real MCP memory_add tool with Summaryneedle only in its summary, then asserts that both MCP memory_search and the HTTP Bridge memory feed find exactly that entry. A foreign project's matching entry stays excluded.
  • index-used: The migration creates memories_search_vector_gin on memories USING GIN (search_vector). Both queries in src/heyaira/server.py filter directly on this column. The migration regression runs prior migrations in an isolated schema, inserts existing rows (including NULL summary/rationale), applies the new migration using the real migration runner, and asserts stored-generated tsvector metadata, a valid GIN index, backfilled values, summary matches, repeated startup, and automatic regeneration after a summary update.
  • results-unchanged: Existing memory-search tests are untouched. A static comparison against the base server file confirms exactly two replacements of the inline to_tsvector expressions with column references. The plainto_tsquery('simple', ...) calls, ordering clauses, projections, filters and pagination are unchanged. The new E2E regression compares both endpoints to the original SQL for title/body/rationale matches, case and multiword queries, no matches, kind filters, pagination, empty queries and timestamp ties. It also checks MCP tag filtering.
  • suite: passed locally. The literal frozen v26 command exited 0: 290 passed, 109 skipped, 3 deselected in 453.36s (0:07:33). Only its two ignored Server Beat files and three deselected cases are excluded. The added fixture regression ran successfully; the two memory-search database regressions skipped. Database behavior remains for PostgreSQL CI.

Actual checks on the Mac

Python 3.12.7 and the installed Python packages are readable in this resumed session. No packages were installed. The full validation uses the literal frozen command from the repository root, with no worker-side environment overrides:

PYTHONPATH=src python -m pytest -q -p no:cacheprovider \
    --ignore=tests/test_server_beat_audit.py \
    --ignore=tests/test_server_beat_corrections.py \
    --deselect tests/test_deployment_guard.py::test_required_override_passes_preflight \
    --deselect tests/test_deployment_guard.py::test_missing_extra_compose_file_is_rejected \
    --deselect tests/test_ios_companion_contract.py::test_inbox_logic_check_passes_with_the_swift_toolchain

The frozen exclusions are unchanged. The deployment tests still invoke real Git against standalone synthetic repositories. Their child processes now receive a copy of the caller's environment with Git repository-location variables removed, so Git uses the fixture's own repository and object directory. The normal test environment is unchanged outside those child processes. The existing deployment assertions are unchanged. The new regression supplies a fake Docker availability check that exits 99 if called; the dry-run must never invoke it. No Docker daemon, deployment or production operation ran.

Actual full-suite output includes:

SKIPPED [2] tests/test_memory_search_e2e.py:35: set HEYAIRA_SUMMARY_E2E_DATABASE_URL for isolated PostgreSQL
290 passed, 109 skipped, 3 deselected in 453.36s (0:07:33)

Static comparison against the base exited 0:

PASS: exactly two server predicate replacements; all other server text unchanged from base.

git diff --check passed. Existing memory-search tests are unchanged. The only modified existing test file is tests/test_deployment_guard.py, for the requested verifier correction. Git maintenance checks use the Xcode Git binary directly. An AST comparison against 6126ee5 also passed:

PASS: all existing deployment test bodies unchanged; one read-only-cache regression added.

Verifier finding addressed

The latest verifier reported 7 failed, 282 passed, 109 skipped, 3 deselected on 6126ee5, with all seven failures at deployment-fixture git init. The harness exports GIT_OBJECT_DIRECTORY and GIT_ALTERNATE_OBJECT_DIRECTORIES for its candidate checkout. A verifier's cache can be read-only, so a synthetic repository inherits that location and attempts to create its object directories there. Selecting a writable temporary directory alone does not isolate this inherited Git state.

tests/test_deployment_guard.py now removes GIT_DIR, GIT_COMMON_DIR, GIT_WORK_TREE, GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY, GIT_ALTERNATE_OBJECT_DIRECTORIES and GIT_NAMESPACE from the environments of fixture Git commands and the deployment script tested against them. The parent environment, production script and search code are unchanged.

The new regression test_fixture_and_preflight_use_local_objects_with_read_only_parent_cache sets an inherited read-only synthetic object cache. It checks that the fixture commit is stored locally, the real dry-run resolves that commit, and the read-only cache and marker remain untouched. Before the fixture fix, running this regression failed at git init:

parent-objects/pack: Permission denied
1 failed in 0.14s

After the fix, the focused deployment-file run with its two frozen deselects passed:

9 passed, 2 deselected in 3.36s

That focused run used installed Xcode Git on PATH for speed; the full frozen command above uses the caller's PATH, including /usr/bin/git. A complete parent suite baseline was not run; the failing targeted regression provides the before-fix control.

Remaining verification

The two new database tests use the existing HEYAIRA_SUMMARY_E2E_DATABASE_URL, already supplied by scripts/test_postgres.py. They skip when that URL is unset in a provisioned local pytest environment. Both were collected and skipped in the successful local run. Independent review and the complete PostgreSQL CI suite remain pending; no acceptance is claimed by this worker.

PostgreSQL follow-up (after CI failure)

CI failed the migration regression: asyncpg returns PostgreSQL's "char" type (pg_attribute.attgenerated) as b's'. The test now selects attgenerated::text. It also applies every other migration before this one, not only those that sort before it, because 036_project_notification_events and 037_publication_missing_alert are already applied on deployed databases. With those changes scripts/test_postgres.py on a local PostgreSQL passed: 623 passed, zero skipped. Plain pytest: 395 passed, 228 skipped.

Query plans on 50,000 synthetic memories in one project (rare term, LIMIT 20): the stored column removes per-row to_tsvector (98 ms before, 17 ms after), but the planner still walks memories_project_created in order and filters, so the GIN index is not chosen for this query shape. In a generic plan, $2 = '' OR search_vector @@ ... cannot use it at all. With ordered index scans disabled, the GIN bitmap scan answers in about 1 ms. Results and ordering are unaffected; making the planner prefer the GIN index is a separate change.