Skip to content

fix(index): wire startup check, repair healthcheck and CMD_TARGET#97

Merged
fvadicamo merged 6 commits into
developfrom
chore/coderabbit-followups
Jul 13, 2026
Merged

fix(index): wire startup check, repair healthcheck and CMD_TARGET#97
fvadicamo merged 6 commits into
developfrom
chore/coderabbit-followups

Conversation

@fvadicamo

@fvadicamo fvadicamo commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-ups from the review of PRs #93/#95, plus two deployment bugs found while shipping v0.6.0 to the system instance.

Fixes

1. check_provider_registration was dead code (vektra-index/startup.py, vektra-app/main.py)
ARCH-057 step 5 was documented but never wired: nothing called the function outside its own unit test, and it hardcoded embedding/sentence-transformers while the app registers embedding/default plus the active alias. It is now called at the end of _step_5_register_providers and checks the default alias, so it validates the provider that is actually in use. A regression test pins the TEI-only registry case (CodeRabbit flagged this as "TEI startup will fail" — it does not, precisely because the function was dead; the real defect was the orphaned validator).

2. Qdrant healthcheck never passed (docker-compose.yml)
The check shelled out to curl, which the qdrant/qdrant image does not ship, so the container was reported unhealthy in every deployment using the qdrant profile (confirmed on a live stack: exec: "curl": executable file not found in $PATH). Nothing depends on it being healthy today, so nothing was breaking — but it would break the moment someone added depends_on: condition: service_healthy. Replaced with a bash-only /dev/tcp probe of /healthz, verified against the pinned v1.17.0 image by flipping the live container from unhealthy to healthy. The TEI healthcheck was audited too: that image does ship curl, so it is unchanged.

3. CMD_TARGET=migrate never worked (Dockerfile, docker/entrypoint.sh)
The entrypoint documented two invocation forms, but the Dockerfile's CMD ["server"] was always passed as $1, which the precedence chain favours over the env var. The env-var form therefore booted a full server instead of running a one-shot migration. This is not theoretical: it hung the deployment script during the v0.6.0 rollout, waiting forever on a migration container that had turned itself into a web server. Removed the CMD (the entrypoint already defaults to server with no argument); docker run <image> migrate is unaffected.

4. Tests no longer mock the SQLAlchemy ORM class (vektra-index/tests/test_pgvector_unit.py)
Two tests patched DocumentChunkOrm and asserted on constructor kwargs, which hides real mapping errors and violates this repo's own path instructions. They now capture the real ORM instances through the session.add intercept and assert on their attributes.

5. Docs corrected (docs/reference/api.md, docs/getting-started/index.md, .claude/CLAUDE.md)
The ingest reference documented the sync response status as "indexed" (the code returns new/exists/alias; indexed is an async job status) and omitted Markdown from the supported formats even though MarkdownExtractor has always been registered for text/markdown. More importantly, the agent instructions claimed chunk text lives in the Postgres document_chunks table: that table is written only by the pgvector provider, so with VEKTRA_VECTOR_STORE_PROVIDER=qdrant it is empty for every namespace and the Qdrant payload is the sole source of chunk text. That is also the root cause of the reindex no-op tracked under BUG-021 — run_reindex reads the empty table, re-embeds nothing, and reports success. Recorded in the backlog entry.

Backlog

BUG-021 annotated with the root cause above, and with a parity gap found in review: QdrantVectorStoreProvider.retrieve() does not scope by index_version, unlike the pgvector provider. Not fixed here.

Verification

  • make lint clean (ruff, mypy, import-linter 8/8 contracts).
  • make test: 699 passed, 3 skipped, no regressions.
  • Healthcheck fix verified on the running container, not just in the file.
  • The CMD removal was verified by grepping every compose file, workflow and doc for a dependency on the image's default command (none: docker-compose.yml already sets CMD_TARGET: server as an env var) and by testing the entrypoint dispatch logic in isolation. Not verified with a full image build.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved startup validation for configured embedding and vector providers.
    • Fixed Docker migration commands so they no longer unintentionally start the server.
    • Improved Qdrant health checks in environments without curl.
    • Corrected Qdrant storage and synchronization guidance.
  • Documentation

    • Added Markdown support to the ingest API documentation.
    • Clarified ingest statuses, including new, duplicate, and alias results.
    • Updated ingestion examples to reflect the new status.
  • Tests

    • Added coverage for alternative embedding providers and deterministic chunk relationships.

fvadicamo and others added 5 commits July 13, 2026 13:28
check_provider_registration hardcoded the embedding provider name to
"sentence-transformers" and was never called outside its own unit test,
so it silently stopped protecting startup once TEI mode
(VEKTRA_EMBEDDING_PROVIDER=tei) was added. Call it at the end of
_step_5_register_providers with the configured vector_store_provider and
sparse_embedding_provider, and check the "default" alias instead of the
hardcoded name so it validates whichever embedding provider is active.
Adds a regression test for a TEI-only registry (default + tei, no
sentence-transformers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The qdrant/qdrant:v1.17.0 image has no curl, so
["CMD", "curl", "--fail", "http://localhost:6333/healthz"] always failed
with "exec: curl: executable file not found in $PATH", permanently
reporting the container unhealthy in every deployment using the qdrant
profile (verified on a live stack). bash is present in the image, so
switch to a bash-only check that speaks raw HTTP over /dev/tcp and greps
the status line. Verified empirically: recreated the live container with
the new healthcheck and it flipped from unhealthy (FailingStreak 10235)
to healthy (ExitCode 0) on the first probe. Also audited the TEI
healthcheck (same curl pattern) against the pinned cpu-1.6 image: curl
is present there, so it is left unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The entrypoint's CMD_TARGET="${1:-${CMD_TARGET:-server}}" prefers the
positional arg over the env var, but the Dockerfile's CMD ["server"]
meant Docker always passed "server" as $1, so CMD_TARGET could never
win: `CMD_TARGET=migrate docker run image` silently booted a full server
instead of running the one-shot migration (this hung a production
deployment script). Remove the Dockerfile CMD - the entrypoint already
defaults CMD_TARGET to "server" when no argument is given, so
`docker run image` and `docker run image migrate` are unaffected. Fix
the comment that claimed both forms already worked.

Verified: grepped the repo for anything relying on the image's default
CMD (compose files, CI workflows, scripts, docs) - nothing does;
docker-compose.yml already sets CMD_TARGET=server as an env var for the
vektra service, so its runtime behavior is unaffected. Confirmed the
dispatch logic itself in isolation (sh, no Docker) for all four
invocation combinations, including reproducing the prior bug for
contrast. Did not perform a full image build (expensive, out of scope
per instructions).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
test_store_honors_deterministic_ids_and_parent_linkage and
test_store_falls_back_to_random_id_on_non_uuid patched DocumentChunkOrm
and asserted on the mock's call_args_list, which tests the mock's
bookkeeping rather than the real ORM mapping and hides mapping errors.
.coderabbit.yaml's path instructions explicitly forbid mocking
SQLAlchemy ORM classes. Both tests now let store() construct real
DocumentChunkOrm instances and capture them via a session.add side
effect (_make_session() already stubs session.add as a MagicMock),
asserting on the captured instances' .id/.parent_id attributes instead.

Also annotates BUG-021 in .s2s/BACKLOG.md with a related parity gap
found in the same review: QdrantVectorStoreProvider.retrieve() filters
by namespace_id only, unlike PgvectorProvider.retrieve() which also
filters by index_version. Not fixed here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…t chunk-storage claim

The sync ingest response returns new/exists/alias, never "indexed" (that is an
async job status), and MarkdownExtractor has always handled text/markdown.

The agent instructions claimed chunk text lives in document_chunks; that table is
written only by PgvectorProvider, so in Qdrant mode it is empty for every namespace
and the Qdrant payload is the only source of chunk text. Recorded in BUG-021 as the
root cause of the reindex no-op: run_reindex reads that empty table and reports
success after writing nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fvadicamo fvadicamo added bug Something isn't working documentation Improvements or additions to documentation labels Jul 13, 2026
@github-actions github-actions Bot added component:index vektra-index component infra Infrastructure, Docker, deployment labels Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@fvadicamo, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 27 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a33eadb2-8f74-48a3-935b-d2f08727bc87

📥 Commits

Reviewing files that changed from the base of the PR and between 68852d4 and 2ebc1b5.

⛔ Files ignored due to path filters (1)
  • .s2s/BACKLOG.md is excluded by !.s2s/**
📒 Files selected for processing (2)
  • CHANGELOG.md
  • docs/getting-started/index.md
📝 Walkthrough

Walkthrough

The PR updates provider startup validation, Docker command dispatch and Qdrant healthchecks, Qdrant and ingest documentation, changelog entries, and pgvector tests that inspect persisted ORM instances.

Changes

Core runtime and documentation updates

Layer / File(s) Summary
Provider registration validation
vektra-app/src/vektra_app/main.py, vektra-index/src/vektra_index/startup.py, vektra-index/tests/test_startup.py
Startup now validates configured providers using the default embedding alias, with coverage for TEI-only registration.
Docker dispatch and Qdrant healthcheck
Dockerfile, docker/entrypoint.sh, docker-compose.yml
The entrypoint controls command dispatch, while the Qdrant healthcheck uses Bash TCP probing instead of curl.
Qdrant and ingest documentation
.claude/CLAUDE.md, docs/reference/api.md, docs/getting-started/index.md, CHANGELOG.md
Documentation covers Qdrant payload storage, reindex behavior, Markdown ingestion, ingest statuses, and the listed fixes.
ORM persistence test assertions
vektra-index/tests/test_pgvector_unit.py
Tests inspect ORM objects captured by session.add for IDs and parent linkage instead of mocking ORM constructors.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: component:core

Suggested reviewers: francescoscalzo

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main changes to startup validation, healthchecks, and CMD_TARGET handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/coderabbit-followups

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces several bug fixes, test refactorings, and documentation updates. Key changes include activating the check_provider_registration startup check using the 'default' embedding alias, updating the Qdrant healthcheck in docker-compose.yml to use a bash-only TCP check, and removing the default CMD in the Dockerfile to ensure the CMD_TARGET environment variable works as intended. Additionally, unit tests were updated to avoid mocking SQLAlchemy ORM classes, and documentation was corrected regarding API response statuses and chunk storage behavior. There are no review comments to evaluate, and I have no further feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@fvadicamo fvadicamo merged commit bc467dd into develop Jul 13, 2026
20 checks passed
@fvadicamo fvadicamo deleted the chore/coderabbit-followups branch July 13, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working component:index vektra-index component documentation Improvements or additions to documentation infra Infrastructure, Docker, deployment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant