"""Full rebuild of market:catalog from the registry `main` (contracts/catalog-index.md). Drop partition, then upsert every `published` listing found under `listings/` on the registry's default branch. Invalid listing.json documents are skipped with a warning, not fatal — one bad listing must not block the rebuild (constitution VII: smallest thing that closes the loop). """ from __future__ import annotations import sys import httpx from jsonschema import Draft202012Validator from .client import MemoryClient, RegistryClient, build_embedded_text from .config import listing_schema_path, load_config def _load_validator() -> Draft202012Validator: import json with listing_schema_path().open() as f: schema = json.load(f) return Draft202012Validator(schema) def run(registry: RegistryClient, memory: MemoryClient, validator: Draft202012Validator) -> dict: """Execute one full reindex; returns counts for reporting.""" counts = {"scanned": 0, "invalid": 0, "not_published": 0, "upserted": 0} listing_ids = registry.list_listing_ids() memory.drop_partition() for listing_id in listing_ids: counts["scanned"] += 1 try: listing = registry.get_listing(listing_id) except (httpx.HTTPStatusError, ValueError) as exc: print(f"WARN: skipping {listing_id}: cannot read listing.json ({exc})", file=sys.stderr) counts["invalid"] += 1 continue errors = list(validator.iter_errors(listing)) if errors: print( f"WARN: skipping {listing_id}: schema violations: " + "; ".join(e.message for e in errors), file=sys.stderr, ) counts["invalid"] += 1 continue if listing.get("status") != "published": counts["not_published"] += 1 continue solution = registry.get_solution(listing_id) text = build_embedded_text(listing, solution) memory.upsert_document(listing_id, text, listing) counts["upserted"] += 1 return counts def main() -> None: config = load_config() validator = _load_validator() with RegistryClient( config.forgejo_url, config.forgejo_token, config.registry_repo, config.registry_ref, ) as registry, MemoryClient( config.memory_api_url, config.memory_api_key, config.memory_api_partition, partition_path=config.memory_api_partition_path, document_path=config.memory_api_document_path, ) as memory: counts = run(registry, memory, validator) print( f"reindex complete: scanned={counts['scanned']} " f"upserted={counts['upserted']} " f"not_published={counts['not_published']} " f"invalid_skipped={counts['invalid']}" ) if __name__ == "__main__": main()