When main moves every hour, a nightly-only indexer guarantees wrong answers by lunch. Freshness is an SLO, not a cron preference: define max staleness per corpus tier, then pick git webhooks versus batch reconcile accordingly. Day 27 wires the triggers, the generation markers, and the refuse-when-stale behavior that keeps citations honest.
⚡ TL;DR: Hot paths (SDK, auth, runbooks) reindex on push via webhook → queue → worker; cold wiki can stay nightly. Version the index with
git_sha+built_at. Refuse or warn when answers would use chunks older than your SLA.
Write the freshness SLO first
Example contract: “P95 answers for /packages/sdk/** use chunks ≤ 15 minutes behind origin/main.” Without that sentence, teams argue about architecture forever.
| Corpus | Cadence | Trigger |
|---|---|---|
packages/sdk, services/*/src |
≤15m | push webhook |
| ADRs / wiki | ≤24h | nightly reconcile |
| PDFs / designs | on upload | S3 event |
| Generated API refs | on release tag | CI publish job |
Publish the SLO next to your RAG runbook. Support engineers should see index_sha in the debug footer of every answer.
Webhook path that does not melt the monorepo
Validate signatures, enqueue changed paths only, and make consumers idempotent on (sha, path).
import hashlib, hmac, json
def handle_push(headers: dict, body: bytes, secret: str) -> dict:
sig = headers.get("X-Hub-Signature-256", "")
expect = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, expect):
raise PermissionError("bad_signature")
event = json.loads(body)
files = set()
for c in event.get("commits", []):
files.update(c.get("added", []))
files.update(c.get("modified", []))
for p in c.get("removed", []):
files.add("DELETE:" + p)
return {"sha": event["after"], "paths": sorted(files)}
reindex_all(repo) # burns money and creates long windows of partial consistency
Deletes matter. Tombestone removed paths or your index will cite ghosts that fail Day 26 quote checks after the file is gone.
Nightly reconcile is mandatory backup
Webhooks drop. GitHub outages happen. Nightly jobs compare the git tree to the index, fix orphans, and rebuild after embedding-model upgrades. Treat nightly as reconcile, not the only writer.
INDEX_META = {"git_sha": sha, "built_at": iso_now, "embed_model": "amazon.titan-embed-text-v2:0"}
def answer_allowed(meta: dict, max_age_min: int) -> bool:
return minutes_since(meta["built_at"]) <= max_age_min
Bedrock Knowledge Bases and multi-tenant notes
KB sync jobs are coarse. For SaaS, prefer S3 layouts you control (s3://corp-rag/{tenant}/...) plus metadata filters (Day 30), or self-managed OpenSearch/pgvector. Always stamp git_sha and tenant_id on documents. When freshness SLO burns, page data eng — not the model vendor.
Production checklist
- [ ] Freshness SLO documented per corpus tier
- [ ] Webhook signature verified; queue idempotent on
(sha, path) - [ ] Deletes propagate (tombstones), not only upserts
- [ ] Nightly reconcile compares tree vs index
- [ ] Answers expose index
git_shain debug footer - [ ] Alert when P95 staleness exceeds SLO
Series navigation
Last updated September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
