Fingerprint the Qdrant corpus by content, not by count

This commit is contained in:
2026-08-17 14:31:56 +07:00
parent c778f7e9cc
commit d1c9933b9e
8 changed files with 391 additions and 43 deletions
+48 -21
View File
@@ -26,12 +26,30 @@ jobs:
- name: Render default and production manifests
run: |
helm template default infra/helm/medical-chatbot > /tmp/default.yaml
# values-prod.yaml leaves the image tags empty on purpose: production
# must run an immutable commit SHA, supplied per deploy. Rendering it
# without one has to fail rather than fall back to a development tag,
# so assert that failure here — otherwise the guard could rot into a
# silent default and nobody would notice until a cutover.
if helm template production infra/helm/medical-chatbot \
--values infra/helm/medical-chatbot/values-prod.yaml \
> /tmp/untagged.yaml 2>/tmp/untagged.err; then
echo "::error::production render succeeded with no image tag; the immutable-tag guard is gone"
exit 1
fi
grep -q 'image.tag must be set to an immutable tag' /tmp/untagged.err
helm template production infra/helm/medical-chatbot \
--values infra/helm/medical-chatbot/values-prod.yaml \
--set aiService.image.tag="$GITHUB_SHA" \
--set web.image.tag="$GITHUB_SHA" \
> /tmp/production.yaml
grep -q 'ANSWER_MODEL_ID: "qwen.qwen3-next-80b-a3b"' /tmp/production.yaml
grep -q 'RERANK_ENABLED: "true"' /tmp/production.yaml
grep -q 'checksum/runtime-config:' /tmp/production.yaml
grep -q "image: \"ghcr.io/baovu2k4/vsf-duocthu-ai-service:$GITHUB_SHA\"" /tmp/production.yaml
grep -q "image: \"ghcr.io/baovu2k4/vsf-duocthu-web:$GITHUB_SHA\"" /tmp/production.yaml
# The practice cluster is only evidence for the production migration
# while it renders the same behavioural contract as production, so both
@@ -45,36 +63,45 @@ jobs:
--values infra/helm/medical-chatbot/values-practice-data.yaml \
> /tmp/practice-data.yaml
# A bare `grep -q` fails the step with no indication of which
# assertion broke, and `set -e` ignores a status inverted with `!`,
# so a `! grep -q` assertion can never fail at all. Both directions
# go through helpers that name the pattern and exit explicitly.
# `--` matters: a YAML list item pattern starts with `-`, which grep
# would otherwise parse as an option bundle.
expect() {
if ! grep -q -- "$2" "$1"; then
echo "::error::$1 is missing: $2"
exit 1
fi
}
refute() {
if grep -q -- "$2" "$1"; then
echo "::error::$1 must not contain: $2"
exit 1
fi
}
# Behavioural parity with the audited production runtime contract.
grep -q 'ANSWER_MODEL_ID: "qwen.qwen3-next-80b-a3b"' /tmp/practice-app.yaml
grep -q 'ANSWER_PROVIDER: "bedrock-converse"' /tmp/practice-app.yaml
grep -q 'EMBEDDING_PROVIDER: "cohere-v4"' /tmp/practice-app.yaml
grep -q 'EMBEDDING_DIMENSIONS: "1024"' /tmp/practice-app.yaml
grep -q 'EVIDENCE_MINIMUM_SCORE: "0.12"' /tmp/practice-app.yaml
grep -q 'RERANK_ENABLED: "true"' /tmp/practice-app.yaml
grep -q 'AWS_REGION: "us-east-1"' /tmp/practice-app.yaml
grep -q 'checksum/runtime-config:' /tmp/practice-app.yaml
grep -q 'host: readytochat.realvuxbaro.me' /tmp/practice-app.yaml
expect /tmp/practice-app.yaml 'ANSWER_MODEL_ID: "qwen.qwen3-next-80b-a3b"'
expect /tmp/practice-app.yaml 'ANSWER_PROVIDER: "bedrock-converse"'
expect /tmp/practice-app.yaml 'EMBEDDING_PROVIDER: "cohere-v4"'
expect /tmp/practice-app.yaml 'EMBEDDING_DIMENSIONS: "1024"'
expect /tmp/practice-app.yaml 'EVIDENCE_MINIMUM_SCORE: "0.12"'
expect /tmp/practice-app.yaml 'RERANK_ENABLED: "true"'
expect /tmp/practice-app.yaml 'AWS_REGION: "us-east-1"'
expect /tmp/practice-app.yaml 'checksum/runtime-config:'
expect /tmp/practice-app.yaml '- host: "readytochat.realvuxbaro.me"'
# The app release must own neither data StatefulSet: PostgreSQL and
# Qdrant belong to the data release, so an app-side sync failure or
# prune can never delete the corpus or the query history. Only those
# two use volumeClaimTemplates — the observability PVCs are the app
# release's own and are expected here.
#
# `set -e` ignores a command whose status is inverted with `!`, so
# every must-NOT-contain assertion is written as an explicit exit.
refute() {
if grep -q "$2" "$1"; then
echo "::error::$1 must not contain: $2"
exit 1
fi
}
refute /tmp/practice-app.yaml 'volumeClaimTemplates'
grep -q 'medical-chatbot-data-medical-chatbot-qdrant' /tmp/practice-app.yaml
expect /tmp/practice-app.yaml 'medical-chatbot-data-medical-chatbot-qdrant'
# ...and the data release must own nothing else.
refute /tmp/practice-data.yaml 'medical-chatbot-data-medical-chatbot-ai-service'
refute /tmp/practice-data.yaml 'kind: Ingress'
grep -q 'volumeClaimTemplates' /tmp/practice-data.yaml
expect /tmp/practice-data.yaml 'volumeClaimTemplates'