Appearance
Handoff: SVC-014-search-legacy-owner-rebuild-proof search-service legacy owner rebuild proof
Scope
- Service/area:
search-service - Task pack:
docs/agents/service-tasks/search-service.md - Goal: accept the bounded
SVC-014next slice by proving the current legacy route ownership mapping, rebuild/projection proof, and route rollback notes before any public search route rehearsal or promotion. - Non-goals: default gateway route promotion, synthetic
/api/searchcompatibility routes, public catalog ownership changes, live browser parity, owner-service canonical writes, or legacy source edits under/Users/velikho/Desktop/WORKING/HOCTAPAZ/.
Docs Read
go-platform/docs/agents/service-tasks/search-service.mdgo-platform/docs/agents/handoffs/SVC-014-projection-worker-runtime-fixtures.mdgo-platform/docs/agents/service-agent-next-slice-queue.jsongo-platform/docs/agents/service-agent-next-slice-closeout-ledger.jsongo-platform/docs/agents/agent-handoff-format.mdgo-platform/docs/architecture/legacy-api-map.mdgo-platform/docs/contracts/legacy/legacy-route-snapshot.jsongo-platform/docs/qa/search-route-rehearsals.mdgo-platform/docs/api/search-service.md
Legacy Evidence Read
/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/questions/questions.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/exams/exams.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/courses/courses.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/storage/storage.controller.ts/Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/public/public-catalog.controller.ts- Legacy source was read-only evidence only; no file under
/Users/velikho/Desktop/WORKING/HOCTAPAZ/was edited.
Files Changed
go-platform/docs/agents/handoffs/SVC-014-search-legacy-owner-rebuild-proof.mdgo-platform/docs/agents/service-agent-next-slice-closeout-ledger.jsongo-platform/docs/agents/service-agent-next-slice-closeout-ledger.mdgo-platform/docs/agents/service-agent-accepted-handoffs.mdgo-platform/docs/qa/service-agent-goal-completion-audit.mdgo-platform/docs/qa/service-agent-readiness-smoke.mdgo-platform/Makefilego-platform/services/search-service/internal/usecase/projection_freshness_worker.gogo-platform/services/search-service/internal/usecase/projection_freshness_worker_test.gogo-platform/scripts/test/search-projection-pull-worker-postgres-isolated.shgo-platform/docs/qa/search-projection-freshness-latency-proof.mdgo-platform/docs/qa/search-projection-pull-worker-postgres-proof.md
Behavior Implemented
- The optional freshness worker records
latencyMsfor each owner source from sync-state lookup through owner fetch, copied-index mutation, and checkpoint persistence. Its initial and interval ticks emit those per-source fields in structured logs. - A disposable restart/resume harness builds the real search-service binary, applies only search-service migrations to a fresh PostgreSQL container, and supplies deterministic local Question Bank, Course, Exam, and Document owner fixtures. The first process stops with four
PARTIALcursors; the replacement process resumes each cursor and ends at fourSYNCEDcheckpoints. - Existing search-service rebuild APIs remain copied-index/projection paths for
QUESTION,COURSE,EXAM, andDOCUMENT; owner services keep canonical writes and authorization decisions. - The legacy route ownership mapping remains explicit:
docs/contracts/legacy/legacy-route-snapshot.jsoncontains/api/public/catalogand/api/public/catalog/:type/:slugunderPublicCatalogController, anddocs/architecture/legacy-api-map.mdmaps the current public catalog paths tocourse-service, notsearch-service. search-servicehas native/v1/searchand/v1/rebuild/...routes for service tests, migration tooling, and future adapters only; no public gateway route is accepted in this slice.
Tests And Verification
- Command:
GOTOOLCHAIN=go1.25.11 go test ./services/search-service/... -count=1 - Result: passed.
- Evidence: search-service command/server, domain, HTTP, repository, and usecase packages passed; HTTP/repository/usecase packages executed tests.
- Command:
make test-search-rebuild - Result: passed.
- Evidence:
SEARCH_REBUILD_SELF_TEST=1 python3 scripts/migration/rebuild-question-search-index.pyindexed paginatedcourse,document,exam, andquestion-bankprojection pages and ended withok search rebuild self-test. - Command:
make test-search-routes - Result: passed.
- Evidence:
scripts/test/search-route-rehearsal-guard.shconfirmed default/api/publicremains legacy-proxied, noroutes.search*.jsonfile exists, no gateway route targetssearch-service, and no synthetic/api/searchroute is introduced. - Command:
SEARCH_REBUILD_SELF_TEST=1 python3 scripts/migration/rebuild-question-search-index.py - Result: passed.
- Evidence: direct self-test repeated paginated rebuild loops for
course,document,exam, andquestion-banksources and ended withok search rebuild self-test. - Command:
GOTOOLCHAIN=go1.25.11 go test -race ./services/search-service/internal/http ./services/search-service/internal/usecase ./services/search-service/internal/repository -count=1 - Result: passed.
- Current rebuild recheck: the current external self-test rebuilt two pages for each
course,document,exam, andquestion-bankowner projection, carriedcursor-1tocursor-2, and ended each source withhasMore=false. Captured output and assertions are inoutput/qa/search-legacy-owner-rebuild-proof-20260713/. - Current route-boundary recheck:
make test-search-routesstill confirmed/api/publicislegacy_proxy, noroutes.search*.jsonor/api/searchroute exists, and no gateway target points at search-service. No Gateway or browser runtime was started because there is still no real legacy public route mapped to search-service. - Command:
make test-search-projection-freshness-latency - Result: passed.
- Evidence: the focused worker test held an owner response for 25 ms and asserted that
latencyMsincludes the delay; the HTTP configuration test still started all four owner fixtures concurrently. - Command:
SEARCH_PROJECTION_PULL_WORKER_POSTGRES_CONFIRM=disposable-postgres make test-search-projection-pull-worker-postgres - Result: passed.
- Evidence: artifact
output/qa/search-projection-pull-worker-postgres-29568-1784032579/summary.jsonrecords four first-startPARTIALcheckpoints/four documents, then four post-restartSYNCEDcheckpoints/eight documents withCOURSE:2,DOCUMENT:2,EXAM:2,QUESTION:2. Native/v1/searchand/v1/sync-state/*reads passed; worker logs includelatencyMs. - Current runtime recheck (2026-07-15): the full source suite, rebuild and route guards, freshness-latency tests, and the disposable PostgreSQL event replay/fence integration passed again. The fresh pull-worker artifact
output/qa/search-projection-pull-worker-postgres-53184-1784092988/summary.jsonrecords the same fourPARTIALfirst-start checkpoints/four copied rows and fourSYNCEDpost-restart checkpoints/eight rows withCOURSE:2,DOCUMENT:2,EXAM:2,QUESTION:2. The independent disposable Question Bank -> NATS JetStream -> Search smoke also passed durable restart recovery, duplicate replay, stale-order rejection, and DELETE tombstone protection. All containers used dynamic loopback ports and were removed by their harnesses; no Gateway route or caller database was used. - workflowVerification:
GOTOOLCHAIN=go1.25.11 go test ./services/search-service/... -count=1make test-search-rebuildmake test-search-routesSEARCH_REBUILD_SELF_TEST=1 python3 scripts/migration/rebuild-question-search-index.pymake test-search-projection-freshness-latencySEARCH_PROJECTION_PULL_WORKER_POSTGRES_CONFIRM=disposable-postgres make test-search-projection-pull-worker-postgres
- requiredVerification:
GOTOOLCHAIN=go1.25.11 go test ./services/search-service/... -count=1make test-search-rebuildmake test-search-routesSEARCH_REBUILD_SELF_TEST=1 python3 scripts/migration/rebuild-question-search-index.pymake test-search-projection-freshness-latencySEARCH_PROJECTION_PULL_WORKER_POSTGRES_CONFIRM=disposable-postgres make test-search-projection-pull-worker-postgres
Route State
- Legacy route:
/api/public/catalogand/api/public/catalog/:type/:slugremain legacy/public catalog routes mapped tocourse-service; question, exam, course, and storage controller routes remain mapped to their owner services. - Native route: search-service native
/v1/searchand/v1/rebuild/...routes remain internal/native service routes, not public compatibility routes. - Gateway state: no default gateway route table changed, no non-default search route table was added, and no gateway route targets
search-service. - Route default state: search route promotion requires exact legacy owner mapping plus rebuild/projection proof.
- Rollback: keep the current default
deploy/gateway/routes.jsonbroad/api/publiclegacy proxy. If a future non-default search route rehearsal is added, rollback must remove the non-default route table and prove default traffic still goes through the legacy/public catalog owner before promotion review.
Data Notes
- Tables touched: only
search_documentsandsearch_sync_statesin one fresh disposable PostgreSQL container; the fixture then removed that container. - Migration run: the harness applied the
Upportions of search-service's own migrations only to that disposable database. - Validation report:
output/qa/search-projection-pull-worker-postgres-29568-1784032579/summary.json; no live/shared data was migrated. - Required evidence covered: legacy route ownership mapping, rebuild/projection proof, route rollback notes.
- Current artifact:
output/qa/search-legacy-owner-rebuild-proof-20260713/contains only disposable self-test output; no database, default gateway table, or legacy source was changed.
Risks / Blockers
- Public search/catalog promotion remains blocked until a real legacy controller route is selected and mapped to
search-service. - Browser/runtime parity and rollback evidence remain required before any default route cutover.
- The new runtime proof is intentionally disposable; it is not a shared or production PostgreSQL integration run.
- Copied indexes must not bypass owner-service visibility rules, authorization decisions, or canonical source-of-truth writes.
Next Step
- Assign a real legacy route mapping for a non-default search route rehearsal with browser/runtime parity and rollback proof; do not create a synthetic
/api/searchroute merely to promote this internal worker proof.