#!/usr/bin/env bash # 06_vector_add_search.sh — GOLAKE-060 + GOLAKE-061. # Vector add + search round-trip. Synthetic dim=4 unit basis vectors, # no embedd dependency — this is the contract layer. # # GOLAKE-060: add succeeds + lookup-by-id returns the inserted IDs # GOLAKE-061: nearest-neighbor search — inserted vector ranks #1 vs itself set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/../lib/env.sh" source "${SCRIPT_DIR}/../lib/http.sh" source "${SCRIPT_DIR}/../lib/assert.sh" CASE_ID="GOLAKE-060-061" CASE_NAME="Vector add + lookup + nearest-neighbor" CASE_TYPE="contract" if [ "${1:-}" = "--metadata-only" ]; then return 0 2>/dev/null || exit 0; fi INDEX_NAME="proof_contract_idx" # Idempotent prelude — clean any prior run state. 404 is fine. proof_delete "$CASE_ID" "pre_clean" \ "${PROOF_GATEWAY_URL}/v1/vectors/index/${INDEX_NAME}" >/dev/null # Create the index. vectord returns 201. proof_post "$CASE_ID" "create_index" "${PROOF_GATEWAY_URL}/v1/vectors/index" \ "application/json" \ "{\"name\":\"${INDEX_NAME}\",\"dimension\":4}" >/dev/null proof_assert_eq "$CASE_ID" "create index → 201" "201" \ "$(proof_status_of "$CASE_ID" "create_index")" # Add three deterministic vectors. Unit basis vectors so search recall # is unambiguous: searching for [1,0,0,0] must return v1 first. # vectord wants {"items": [...]}, NOT {"vectors": [...]}. add_body='{"items":[ {"id":"v1","vector":[1,0,0,0]}, {"id":"v2","vector":[0,1,0,0]}, {"id":"v3","vector":[0,0,1,0]} ]}' proof_post "$CASE_ID" "add_vectors" \ "${PROOF_GATEWAY_URL}/v1/vectors/index/${INDEX_NAME}/add" \ "application/json" "$add_body" >/dev/null proof_assert_eq "$CASE_ID" "add vectors → 200" "200" \ "$(proof_status_of "$CASE_ID" "add_vectors")" # Lookup-by-id (GOLAKE-060 evidence). The /index/{name} GET returns # {"params": {...}, "length": N}. proof_get "$CASE_ID" "get_index" \ "${PROOF_GATEWAY_URL}/v1/vectors/index/${INDEX_NAME}" >/dev/null proof_assert_eq "$CASE_ID" "get index → 200" "200" \ "$(proof_status_of "$CASE_ID" "get_index")" length=$(jq -r '.length' \ "${PROOF_REPORT_DIR}/raw/http/${CASE_ID}/get_index.body") proof_assert_eq "$CASE_ID" "index length = 3 after add" "3" "$length" # Search — query is identical to v1; expect v1 at rank 1 with distance ≈ 0. search_body='{"vector":[1,0,0,0],"k":3}' proof_post "$CASE_ID" "search" \ "${PROOF_GATEWAY_URL}/v1/vectors/index/${INDEX_NAME}/search" \ "application/json" "$search_body" >/dev/null proof_assert_eq "$CASE_ID" "search → 200" "200" \ "$(proof_status_of "$CASE_ID" "search")" # Search response shape: {"results": [{"id","distance","metadata?"}]}. search_body_path="${PROOF_REPORT_DIR}/raw/http/${CASE_ID}/search.body" top1_id=$(jq -r '.results[0].id' "$search_body_path") proof_assert_eq "$CASE_ID" "top-1 id = v1 (self-recall)" "v1" "$top1_id" top1_dist=$(jq -r '.results[0].distance' "$search_body_path") proof_assert_lt "$CASE_ID" "top-1 distance < 0.001 (cosine self ≈ 0)" \ "$top1_dist" "0.001" # Cleanup — vectord returns 204 No Content on delete success. proof_delete "$CASE_ID" "post_clean" \ "${PROOF_GATEWAY_URL}/v1/vectors/index/${INDEX_NAME}" >/dev/null proof_assert_status_in "$CASE_ID" "delete index → 200 or 204" "200 204" "post_clean"