[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71CEH4ZM13KMJMSA0K7NB8C":3,"skill-same:01M71CEH4ZM13KMJMSA0K7NB8C":109},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":78,"commitSha":79,"skillDirectory":80,"forksCount":81,"repositoryHtmlUrl":82,"repositoryHomepageUrl":6,"ownerHtmlUrl":83,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":84,"install":85,"resources":98},"01M71CEH4ZM13KMJMSA0K7NB8C","best-practices-arangodb",null,"Repo-specific ArangoDB best practices: leverage text_en analyzer (stop words, stemming, BM25), use AQL functions (LEVENSHTEIN_DISTANCE, TOKENS, NGRAM_SIMILARITY, COSINE_SIMILARITY), store domain knowledge in collections not Python code, and never duplicate DB capabilities.","cateyephotos\u002Fagent-skills","cateyephotos","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F110843613?v=4","01M71CCSDD8VJ7CZ4V1QQGBMA7","01M71CCSE1VRT329KB810CRC0V",0,"MIT","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills.pre-symlink-1776435493\u002Fbest-practices-arangodb\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"best practices","best practices arangodb","domain","cosine","stop","identity","ngram","python code","resilience","text","capabilities","code","query","knowledge","search","db","memory","never","bm25","leverage","2026-11-10 10:12:30",false,"---\nname: best-practices-arangodb\ndescription: >\n  Repo-specific ArangoDB best practices: leverage text_en analyzer (stop words, stemming, BM25),\n  use AQL functions (LEVENSHTEIN_DISTANCE, TOKENS, NGRAM_SIMILARITY, COSINE_SIMILARITY),\n  store domain knowledge in collections not Python code, and never duplicate DB capabilities.\ntriggers:\n  - best practices arangodb\n  - arangodb conventions\n  - arango search\n  - bm25 search\n  - stop words\n  - stemming\n  - levenshtein\n  - fuzzy matching\n  - domain terms\n  - entity classification\n  - aql query\nlicense: MIT\nmetadata:\n  database: ArangoDB\n  client: python-arango\n  defaults:\n    database_name: memory\n    analyzers: [\"text_en\", \"identity\"]\n    search_pattern: BM25 + cosine rerank\n    domain_collections: [\"domain_terms\", \"taxonomy_vocabulary\", \"sparta_controls\"]\n    max_python_stopwords: 0\n    max_hardcoded_domain_terms: 0\n    performance_budgets:\n      bm25_ms: 100\n      hybrid_ms: 300\n      entity_extraction_ms: 1000\n      exact_lookup_ms: 10\n\ntaxonomy:\n  - precision\n  - resilience\nprovides:\n  - best-practices-arangodb\n  - skill-validation\ncomposes:\n  - best-practices-python\n  - memory\n  - ingest-code\n---\n\n> STOP. READ THIS ENTIRE SKILL.MD BEFORE CALLING ANY ENDPOINT.\n\n# ArangoDB Best Practices (Project Skill)\n\nThis skill codifies rules for using ArangoDB correctly in this repo. The core principle:\n\n**ArangoDB's built-in features MUST be used instead of reimplementing them in Python.**\n\n## The Anti-Pattern This Skill Prevents\n\nPython code that duplicates what ArangoDB already does:\n- Stop word lists in Python (ArangoDB `text_en` already strips them)\n- Hand-rolled stemming in Python (ArangoDB `text_en` already stems via Snowball)\n- Regex patterns that classify entity types (ArangoDB collections already know what things are)\n- Hardcoded domain term lists (ArangoDB collections are the source of truth)\n- Python-side BM25\u002FTF-IDF scoring (ArangoDB `BM25()` does this server-side)\n- Python-side cosine similarity loops (ArangoDB `COSINE_SIMILARITY()` does this server-side)\n\nSee [references\u002Fanti-patterns-duplicated-functionality.md](references\u002Fanti-patterns-duplicated-functionality.md) for 7 real removed-from-codebase examples.\n\n## When to Apply\n\nUse this skill whenever you:\n- Write AQL queries or ArangoSearch code\n- Add new collections or ArangoSearch views\n- Work with entity extraction, intent mapping, or text search\n- Add domain knowledge (terms, keywords, frameworks) to the system\n- Implement fuzzy matching, spellcheck, or text classification\n\n## Rules (priority order)\n\n### 1. CRITICAL: No Stop Words in Python — `arango-no-python-stopwords`\n\nArangoDB's `text_en` analyzer already removes English stop words. NEVER maintain a stop word list in Python.\n\n```python\n# BAD — duplicates text_en analyzer\n_STOPWORDS = frozenset({\"the\", \"a\", \"an\", \"of\", \"for\", \"in\", ...})\n\n# GOOD — let ArangoDB handle it\ncursor = db.aql.execute(\"RETURN TOKENS(@query, 'text_en')\", bind_vars={\"query\": text})\n```\n\n### 2. CRITICAL: No Hand-Rolled Stemming — `arango-no-python-stemming`\n\nArangoDB's `text_en` uses Snowball stemming. NEVER strip suffixes in Python.\n\n```python\n# BAD\nfor suffix in (\"s\", \"es\", \"ing\", \"ed\", \"ly\"):\n    if word.endswith(suffix): stem = word[:-len(suffix)]\n\n# GOOD — ArangoDB stems automatically: \"recommendations\" matches \"recommendation\"\n```\n\n### 3. CRITICAL: Domain Knowledge in ArangoDB — `arango-no-hardcoded-domain-lists`\n\nDomain terms, keywords, frameworks belong in ArangoDB collections, not Python dicts\u002Ffrozensets.\n\nCollections: `domain_terms`, `taxonomy_vocabulary`, `sparta_controls`.\n\n### 4. CRITICAL: No Regex for Entity Classification — `arango-no-regex-classification`\n\nRegex is for TOKENIZATION (finding candidates). Classification (what is it?) comes from ArangoDB.\n\n**Exception**: `_extract_control_ids()` in trace.py uses regex to FIND ID-like patterns — that's tokenization, not classification.\n\n### 5. HIGH: Use AQL Functions — `arango-use-aql-functions`\n\n| Need | AQL Function | Python Anti-Pattern |\n|------|-------------|-------------------|\n| Fuzzy match | `LEVENSHTEIN_DISTANCE(a, b)` | `rapidfuzz` loop over all docs |\n| Tokens | `TOKENS(@q, 'text_en')` | Python stop words + stemming |\n| N-gram | `NGRAM_SIMILARITY(a, b, n)` | Hand-rolled comparison |\n| BM25 | `BM25(doc)` | Python TF-IDF |\n| Cosine | `COSINE_SIMILARITY(a, b)` | `numpy` dot product on all docs |\n\n### 6. HIGH: Two-Stage BM25 + Cosine Rerank — `arango-two-stage-search`\n\nNEVER brute-force scan all embeddings. BM25 top-100 → cosine rerank to top-k.\n\n### 7. HIGH: One AQL Round-Trip — `arango-single-roundtrip`\n\nNEVER split one server-side operation into two network round-trips.\n\n### 8. MEDIUM: Batch Exact Matches — `arango-batch-lookups`\n\nUse one AQL with `FOR cid IN @cids` instead of N individual queries.\n\n### 9. MEDIUM: Identity Analyzer for Exact Matches — `arango-identity-for-exact`\n\nUse `identity` (not `text_en`) for control IDs, framework names, categories.\n\n### 10. MEDIUM: Database Name is Always \"memory\" — `arango-db-name-memory`\n\nThe only database is `memory`. `ARANGO_DB=lessons` anywhere = bug.\n\n### 11. LOW: Cache Vocabulary Lookups — `arango-cache-vocab`\n\nModule-level `_cache: T | None = None` + `_get_*()` function.\n\n### 12. CRITICAL: BM25 Score > 0 Is NOT Grounding — `arango-bm25-not-grounding`\n\nBM25 ALWAYS returns results for any security-adjacent query. A score > 0 does NOT prove a term exists. ID-like terms must be grounded via exact `control_id` match or fuzzy edit distance.\n\n```python\n# BAD — BM25 returns results for \"X23-MUSTARD\" because it matches \"spoofing\"\n# GOOD — exact match: FOR c IN sparta_controls FILTER UPPER(c.control_id) == @cid\n```\n\n### 13. HIGH: Pre-Filter Before LEVENSHTEIN_DISTANCE — `arango-prefilter-fuzzy`\n\nFull table scan = 268ms. Length ±3 + prefix overlap pre-filter = 4ms.\n\n```aql\n-- GOOD — pre-filter reduces 9,337 → 61 candidates\nFOR c IN sparta_controls\n    LET cid = UPPER(c.control_id)\n    FILTER LENGTH(cid) >= LENGTH(@candidate) - 3 AND LENGTH(cid) \u003C= LENGTH(@candidate) + 3\n    FILTER LEFT(cid, 1) == LEFT(@candidate, 1) OR CONTAINS(cid, LEFT(@candidate, 2))\n    LET dist = LEVENSHTEIN_DISTANCE(cid, @candidate)\n    FILTER dist \u003C= 2\n    SORT dist LIMIT 3\n    RETURN {control_id: c.control_id, distance: dist}\n```\n\n### 14. HIGH: Concurrent Queries — `arango-concurrent-queries`\n\nArangoDB is multi-threaded. Use `ThreadPoolExecutor` for independent queries.\n\n```python\nwith ThreadPoolExecutor(max_workers=3) as pool:\n    f1 = pool.submit(lambda: list(db.aql.execute(\"...\")))\n    f2 = pool.submit(lambda: list(db.aql.execute(\"...\")))\n    results1, results2 = f1.result(), f2.result()\n```\n\n### 15. HIGH: Performance Budgets — `arango-performance-budgets`\n\n| Operation | Budget |\n|-----------|--------|\n| BM25 text search | \u003C100ms |\n| Hybrid search (BM25 + cosine) | \u003C300ms |\n| Entity extraction (full pipeline) | \u003C1s |\n| Exact lookup by indexed field | \u003C10ms |\n\nUse `tests\u002Ftest_memory_performance.py` to validate.\n\n### 16. MEDIUM: No DOCUMENT() in AQL Loops — `arango-no-document-in-loops`\n\n```aql\n-- BAD: DOCUMENT('lessons', key) in a loop\n-- GOOD: FOR l IN lessons FILTER l._key == key LIMIT 1\n```\n\n### 17. CRITICAL: Always Hybrid Search — `arango-always-hybrid-search`\n\nEvery retrieval query MUST use BM25 + semantic embedding + multi-hop graph traversal. NEVER use a single lane alone.\n\n```python\n# GOOD — via RecallSources\nfrom graph_memory.hybrid_search import hybrid_search_sparta_qra\nresults = hybrid_search_sparta_qra(query, db, embedding_service, k=12)\n```\n\nDirect single-lane queries only in unit tests and diagnostics.\n\n### 18. CRITICAL: No Silent Fallback — `arango-no-silent-fallback`\n\nWhen a search lane fails, the code MUST:\n1. **Log at `logger.error`** (NOT `logger.debug`) with exception context\n2. **Signal degradation** in the result so the caller knows\n3. **NEVER** silently return partial results as if complete\n4. **NEVER** use bare `except Exception:` — always capture as `except Exception as exc:`\n\n`logger.debug` in exception handlers is a silent fallback — invisible in production.\nThis rule applies to ALL AQL queries, view searches, and collection operations.\n\n### 18b. CRITICAL: Every Collection Searchable via \u002Frecall — `arango-recall-all-collections`\n\nEvery document collection with an ArangoSearch view MUST be searchable via `\u002Frecall`.\nUse scope routing to filter (e.g., `scope=sparta` → only `sparta_*` collections).\nNew collections MUST be added to both the ArangoSearch view AND `bm25_rank()`.\n`\u002Frecall` MUST return BM25 + semantic (cosine) + multi-hop graph traversal for ALL collections.\n\n### 18c. CRITICAL: Use @@coll bind variables — `arango-no-collection-interpolation`\n\nCollection names in AQL MUST use `@@coll` bind variables, NOT f-string interpolation.\n```python\n# WRONG — AQL injection risk\naql = f\"FOR d IN {collection} FILTER d._key == @key RETURN d\"\n\n# RIGHT — bind variable for collection\naql = \"FOR d IN @@coll FILTER d._key == @key RETURN d\"\ndb.aql.execute(aql, bind_vars={\"@coll\": collection, \"key\": key})\n```\n\n### 19. MEDIUM: Cache Errors Must Log — `arango-cache-must-log`\n\nDB-backed cache `except` blocks MUST log at `logger.error`. Cross-ref: `\u002Fbest-practices-python` rule `correctness-no-silent-fallback`.\n\n### 20. HIGH: No Null Filters for Backfills — `arango-no-null-backfill`\n\nNEVER use `FILTER doc.field == null` for batch backfill operations on large collections.\n\n**Why it fails:** As documents get updated, fewer match `field == null`. Without an index on the field, ArangoDB scans progressively more documents to find fewer matches. Rate degrades from 20\u002Fs → 7\u002Fs → worse.\n\n```aql\n-- BAD — progressively slower as nulls decrease (no index helps)\nFOR doc IN sparta_qra\n    FILTER doc.lineage == null\n    FILTER doc._key > @last_key\n    LIMIT 200\n    RETURN doc\n\n-- GOOD — version-based, indexable, constant performance\nFOR doc IN sparta_qra\n    FILTER doc.lineage.graph_version \u003C @target_version OR doc.lineage == null\n    FILTER doc._key > @last_key\n    LIMIT 200\n    RETURN doc\n```\n\n**Pattern:**\n1. Add a version field (e.g., `lineage.graph_version`)\n2. Create persistent index: `db.sparta_qra.ensureIndex({type: \"persistent\", fields: [\"lineage.graph_version\"]})`\n3. Query by version, not null\n4. Bump version constant when schema changes\n\n**Real incident (2026-04-13):** 171K QRA lineage backfill started at 20\u002Fs, degraded to 7\u002Fs by 50% completion. Root cause: unindexed `lineage == null` filter.\n\n### 21. HIGH: Sparse Vector Index UPDATE Bug — `arango-vector-update-bug`\n\nArangoDB versions \u003C 3.12.9 have a bug where sparse vector indexes block UPDATE operations on documents that don't have the vector field, even though `sparse: true` should allow this.\n\n**Error:** `[HTTP 400][ERR 10] vector field not present in document \u003Ckey>`\n\n**GitHub Issue:** [arangodb\u002Farangodb#22568](https:\u002F\u002Fgithub.com\u002Farangodb\u002Farangodb\u002Fissues\u002F22568)\n\n```python\n# BAD — fails on docs without embedding (versions \u003C 3.12.9)\ndb.aql.execute(\"UPDATE {_key: @key} WITH {lineage: @lin} IN sparta_qra\", ...)\n\n# WORKAROUND — drop index, update, recreate\ncoll = db.collection('sparta_qra')\nidx_config = next(i for i in coll.indexes() if i['type'] == 'vector')\ncoll.delete_index(idx_config['id'])\n\n# ... do all updates ...\n\ncoll.add_index({\n    'type': 'vector',\n    'name': idx_config['name'],\n    'fields': idx_config['fields'],\n    'params': idx_config['params'],\n    'sparse': True,\n    'inBackground': True\n})\n```\n\n**When to apply:** Any batch update to a collection with a sparse vector index where some docs lack the vector field.\n\n**Real incident (2026-04-14):** Lineage backfill failed on 269 docs without embeddings. Error message was misleading (\"vector field not present\") even when providing the embedding in the UPDATE — the index validates against the OLD document state.\n\n## Enforcement\n\n- **PostToolUse hook** `no-regex-silo.sh` fires on every Edit\u002FWrite to .py files\n- Catches: frozensets >10 entries, regex entity classifiers, stopword lists >50, hand-rolled stemming, files >800 lines\n- Exit code 2 = hard block with explanation\n\n## ArangoDB Features Reference\n\n| Feature | AQL Syntax | Purpose |\n|---------|-----------|---------|\n| Stop word removal | `TOKENS(@q, 'text_en')` | Returns only content words |\n| Snowball stemming | `TOKENS(@q, 'text_en')` | \"running\" -> \"run\" |\n| BM25 scoring | `BM25(doc)` | TF-IDF with length normalization |\n| Exact match | `ANALYZER(doc.field == @val, 'identity')` | No stemming\u002Fstop words |\n| Fuzzy match | `LEVENSHTEIN_DISTANCE(a, b)` | Edit distance between strings |\n| N-gram similarity | `NGRAM_SIMILARITY(a, b, n)` | Character n-gram overlap |\n| Cosine similarity | `COSINE_SIMILARITY(a, b)` | Vector distance (no ANN index) |\n| Token search | `SEARCH ANALYZER(doc.f IN TOKENS(@q, 'text_en'), 'text_en')` | Full-text search |\n\n## Key Collections\n\n| Collection | Purpose | Key Fields |\n|-----------|---------|------------|\n| `sparta_controls` | All controls (4,253) | `control_id`, `name`, `source_framework`, `description` |\n| `sparta_qra` | QRA corpus (90K+) | `question`, `answer`, `control_id`, `reasoning` |\n| `domain_terms` | Known non-control terms | `term`, `category` |\n| `taxonomy_vocabulary` | Bridge\u002Ftactical keywords | `term`, `vocabulary_type`, `bridge_concept`, `category` |\n| `lessons` | Memory lessons | `problem`, `solution`, `tags` |\n\n## Querying ArangoDB Documentation\n\nThe full ArangoDB docs-hugo documentation is ingested into `\u002Fmemory` via `\u002Fingest-code`.\n\n```bash\n# Query ArangoDB docs — same as any \u002Fmemory recall\n\u002Fmemory recall --q \"AQL BM25 scoring ArangoSearch\"\n\u002Fmemory recall --q \"graph traversal OUTBOUND depth\"\n\u002Fmemory recall --q \"text_en analyzer stop words stemming\"\n\n# First-time ingestion (or manual re-ingest)\n.\u002Frun.sh ingest\n\n# Nightly incremental update (wired into \u002Fmonitor-memory)\n.\u002Frun.sh update\n\n# Check cache state\n.\u002Frun.sh status\n```\n\nDocs are stored with scope `arangodb-docs` and cached on 12TB at `\u002Fmnt\u002Fstorage12tb\u002Fcache\u002Farangodb-docs-hugo`.\n\n## References (detailed content)\n\n- [references\u002Fcookbook-views.md](references\u002Fcookbook-views.md) — ArangoSearch view creation patterns\n- [references\u002Fcookbook-aql-patterns.md](references\u002Fcookbook-aql-patterns.md) — AQL recipes (UPSERT, traversal, BM25, batch)\n- [references\u002Fcookbook-debugging.md](references\u002Fcookbook-debugging.md) — Debugging AQL (explain, profile, check views)\n- [references\u002Fanti-patterns-duplicated-functionality.md](references\u002Fanti-patterns-duplicated-functionality.md) — 7 real removed anti-patterns with fixes\n","> STOP. READ THIS ENTIRE SKILL.MD BEFORE CALLING ANY ENDPOINT.\n\n# ArangoDB Best Practices (Project Skill)\n\nThis skill codifies rules for using ArangoDB correctly in this repo. The core principle:\n\n**ArangoDB's built-in features MUST be used instead of reimplementing them in Python.**\n\n## The Anti-Pattern This Skill Prevents\n\nPython code that duplicates what ArangoDB already does:\n- Stop word lists in Python (ArangoDB `text_en` already strips them)\n- Hand-rolled stemming in Python (ArangoDB `text_en` already stems via Snowball)\n- Regex patterns that classify entity types (ArangoDB collections already know what things are)\n- Hardcoded domain term lists (ArangoDB collections are the source of truth)\n- Python-side BM25\u002FTF-IDF scoring (ArangoDB `BM25()` does this server-side)\n- Python-side cosine similarity loops (ArangoDB `COSINE_SIMILARITY()` does this server-side)\n\nSee [references\u002Fanti-patterns-duplicated-functionality.md](references\u002Fanti-patterns-duplicated-functionality.md) for 7 real removed-from-codebase examples.\n\n## When to Apply\n\nUse this skill whenever you:\n- Write AQL queries or ArangoSearch code\n- Add new collections or ArangoSearch views\n- Work with entity extraction, intent mapping, or text search\n- Add domain knowledge (terms, keywords, frameworks) to the system\n- Implement fuzzy matching, spellcheck, or text classification\n\n## Rules (priority order)\n\n### 1. CRITICAL: No Stop Words in Python — `arango-no-python-stopwords`\n\nArangoDB's `text_en` analyzer already removes English stop words. NEVER maintain a stop word list in Python.\n\n```python\n# BAD — duplicates text_en analyzer\n_STOPWORDS = frozenset({\"the\", \"a\", \"an\", \"of\", \"for\", \"in\", ...})\n\n# GOOD — let ArangoDB handle it\ncursor = db.aql.execute(\"RETURN TOKENS(@query, 'text_en')\", bind_vars={\"query\": text})\n```\n\n### 2. CRITICAL: No Hand-Rolled Stemming — `arango-no-python-stemming`\n\nArangoDB's `text_en` uses Snowball stemming. NEVER strip suffixes in Python.\n\n```python\n# BAD\nfor suffix in (\"s\", \"es\", \"ing\", \"ed\", \"ly\"):\n    if word.endswith(suffix): stem = word[:-len(suffix)]\n\n# GOOD — ArangoDB stems automatically: \"recommendations\" matches \"recommendation\"\n```\n\n### 3. CRITICAL: Domain Knowledge in ArangoDB — `arango-no-hardcoded-domain-lists`\n\nDomain terms, keywords, frameworks belong in ArangoDB collections, not Python dicts\u002Ffrozensets.\n\nCollections: `domain_terms`, `taxonomy_vocabulary`, `sparta_controls`.\n\n### 4. CRITICAL: No Regex for Entity Classification — `arango-no-regex-classification`\n\nRegex is for TOKENIZATION (finding candidates). Classification (what is it?) comes from ArangoDB.\n\n**Exception**: `_extract_control_ids()` in trace.py uses regex to FIND ID-like patterns — that's tokenization, not classification.\n\n### 5. HIGH: Use AQL Functions — `arango-use-aql-functions`\n\n| Need | AQL Function | Python Anti-Pattern |\n|------|-------------|-------------------|\n| Fuzzy match | `LEVENSHTEIN_DISTANCE(a, b)` | `rapidfuzz` loop over all docs |\n| Tokens | `TOKENS(@q, 'text_en')` | Python stop words + stemming |\n| N-gram | `NGRAM_SIMILARITY(a, b, n)` | Hand-rolled comparison |\n| BM25 | `BM25(doc)` | Python TF-IDF |\n| Cosine | `COSINE_SIMILARITY(a, b)` | `numpy` dot product on all docs |\n\n### 6. HIGH: Two-Stage BM25 + Cosine Rerank — `arango-two-stage-search`\n\nNEVER brute-force scan all embeddings. BM25 top-100 → cosine rerank to top-k.\n\n### 7. HIGH: One AQL Round-Trip — `arango-single-roundtrip`\n\nNEVER split one server-side operation into two network round-trips.\n\n### 8. MEDIUM: Batch Exact Matches — `arango-batch-lookups`\n\nUse one AQL with `FOR cid IN @cids` instead of N individual queries.\n\n### 9. MEDIUM: Identity Analyzer for Exact Matches — `arango-identity-for-exact`\n\nUse `identity` (not `text_en`) for control IDs, framework names, categories.\n\n### 10. MEDIUM: Database Name is Always \"memory\" — `arango-db-name-memory`\n\nThe only database is `memory`. `ARANGO_DB=lessons` anywhere = bug.\n\n### 11. LOW: Cache Vocabulary Lookups — `arango-cache-vocab`\n\nModule-level `_cache: T | None = None` + `_get_*()` function.\n\n### 12. CRITICAL: BM25 Score > 0 Is NOT Grounding — `arango-bm25-not-grounding`\n\nBM25 ALWAYS returns results for any security-adjacent query. A score > 0 does NOT prove a term exists. ID-like terms must be grounded via exact `control_id` match or fuzzy edit distance.\n\n```python\n# BAD — BM25 returns results for \"X23-MUSTARD\" because it matches \"spoofing\"\n# GOOD — exact match: FOR c IN sparta_controls FILTER UPPER(c.control_id) == @cid\n```\n\n### 13. HIGH: Pre-Filter Before LEVENSHTEIN_DISTANCE — `arango-prefilter-fuzzy`\n\nFull table scan = 268ms. Length ±3 + prefix overlap pre-filter = 4ms.\n\n```aql\n-- GOOD — pre-filter reduces 9,337 → 61 candidates\nFOR c IN sparta_controls\n    LET cid = UPPER(c.control_id)\n    FILTER LENGTH(cid) >= LENGTH(@candidate) - 3 AND LENGTH(cid) \u003C= LENGTH(@candidate) + 3\n    FILTER LEFT(cid, 1) == LEFT(@candidate, 1) OR CONTAINS(cid, LEFT(@candidate, 2))\n    LET dist = LEVENSHTEIN_DISTANCE(cid, @candidate)\n    FILTER dist \u003C= 2\n    SORT dist LIMIT 3\n    RETURN {control_id: c.control_id, distance: dist}\n```\n\n### 14. HIGH: Concurrent Queries — `arango-concurrent-queries`\n\nArangoDB is multi-threaded. Use `ThreadPoolExecutor` for independent queries.\n\n```python\nwith ThreadPoolExecutor(max_workers=3) as pool:\n    f1 = pool.submit(lambda: list(db.aql.execute(\"...\")))\n    f2 = pool.submit(lambda: list(db.aql.execute(\"...\")))\n    results1, results2 = f1.result(), f2.result()\n```\n\n### 15. HIGH: Performance Budgets — `arango-performance-budgets`\n\n| Operation | Budget |\n|-----------|--------|\n| BM25 text search | \u003C100ms |\n| Hybrid search (BM25 + cosine) | \u003C300ms |\n| Entity extraction (full pipeline) | \u003C1s |\n| Exact lookup by indexed field | \u003C10ms |\n\nUse `tests\u002Ftest_memory_performance.py` to validate.\n\n### 16. MEDIUM: No DOCUMENT() in AQL Loops — `arango-no-document-in-loops`\n\n```aql\n-- BAD: DOCUMENT('lessons', key) in a loop\n-- GOOD: FOR l IN lessons FILTER l._key == key LIMIT 1\n```\n\n### 17. CRITICAL: Always Hybrid Search — `arango-always-hybrid-search`\n\nEvery retrieval query MUST use BM25 + semantic embedding + multi-hop graph traversal. NEVER use a single lane alone.\n\n```python\n# GOOD — via RecallSources\nfrom graph_memory.hybrid_search import hybrid_search_sparta_qra\nresults = hybrid_search_sparta_qra(query, db, embedding_service, k=12)\n```\n\nDirect single-lane queries only in unit tests and diagnostics.\n\n### 18. CRITICAL: No Silent Fallback — `arango-no-silent-fallback`\n\nWhen a search lane fails, the code MUST:\n1. **Log at `logger.error`** (NOT `logger.debug`) with exception context\n2. **Signal degradation** in the result so the caller knows\n3. **NEVER** silently return partial results as if complete\n4. **NEVER** use bare `except Exception:` — always capture as `except Exception as exc:`\n\n`logger.debug` in exception handlers is a silent fallback — invisible in production.\nThis rule applies to ALL AQL queries, view searches, and collection operations.\n\n### 18b. CRITICAL: Every Collection Searchable via \u002Frecall — `arango-recall-all-collections`\n\nEvery document collection with an ArangoSearch view MUST be searchable via `\u002Frecall`.\nUse scope routing to filter (e.g., `scope=sparta` → only `sparta_*` collections).\nNew collections MUST be added to both the ArangoSearch view AND `bm25_rank()`.\n`\u002Frecall` MUST return BM25 + semantic (cosine) + multi-hop graph traversal for ALL collections.\n\n### 18c. CRITICAL: Use @@coll bind variables — `arango-no-collection-interpolation`\n\nCollection names in AQL MUST use `@@coll` bind variables, NOT f-string interpolation.\n```python\n# WRONG — AQL injection risk\naql = f\"FOR d IN {collection} FILTER d._key == @key RETURN d\"\n\n# RIGHT — bind variable for collection\naql = \"FOR d IN @@coll FILTER d._key == @key RETURN d\"\ndb.aql.execute(aql, bind_vars={\"@coll\": collection, \"key\": key})\n```\n\n### 19. MEDIUM: Cache Errors Must Log — `arango-cache-must-log`\n\nDB-backed cache `except` blocks MUST log at `logger.error`. Cross-ref: `\u002Fbest-practices-python` rule `correctness-no-silent-fallback`.\n\n### 20. HIGH: No Null Filters for Backfills — `arango-no-null-backfill`\n\nNEVER use `FILTER doc.field == null` for batch backfill operations on large collections.\n\n**Why it fails:** As documents get updated, fewer match `field == null`. Without an index on the field, ArangoDB scans progressively more documents to find fewer matches. Rate degrades from 20\u002Fs → 7\u002Fs → worse.\n\n```aql\n-- BAD — progressively slower as nulls decrease (no index helps)\nFOR doc IN sparta_qra\n    FILTER doc.lineage == null\n    FILTER doc._key > @last_key\n    LIMIT 200\n    RETURN doc\n\n-- GOOD — version-based, indexable, constant performance\nFOR doc IN sparta_qra\n    FILTER doc.lineage.graph_version \u003C @target_version OR doc.lineage == null\n    FILTER doc._key > @last_key\n    LIMIT 200\n    RETURN doc\n```\n\n**Pattern:**\n1. Add a version field (e.g., `lineage.graph_version`)\n2. Create persistent index: `db.sparta_qra.ensureIndex({type: \"persistent\", fields: [\"lineage.graph_version\"]})`\n3. Query by version, not null\n4. Bump version constant when schema changes\n\n**Real incident (2026-04-13):** 171K QRA lineage backfill started at 20\u002Fs, degraded to 7\u002Fs by 50% completion. Root cause: unindexed `lineage == null` filter.\n\n### 21. HIGH: Sparse Vector Index UPDATE Bug — `arango-vector-update-bug`\n\nArangoDB versions \u003C 3.12.9 have a bug where sparse vector indexes block UPDATE operations on documents that don't have the vector field, even though `sparse: true` should allow this.\n\n**Error:** `[HTTP 400][ERR 10] vector field not present in document \u003Ckey>`\n\n**GitHub Issue:** [arangodb\u002Farangodb#22568](https:\u002F\u002Fgithub.com\u002Farangodb\u002Farangodb\u002Fissues\u002F22568)\n\n```python\n# BAD — fails on docs without embedding (versions \u003C 3.12.9)\ndb.aql.execute(\"UPDATE {_key: @key} WITH {lineage: @lin} IN sparta_qra\", ...)\n\n# WORKAROUND — drop index, update, recreate\ncoll = db.collection('sparta_qra')\nidx_config = next(i for i in coll.indexes() if i['type'] == 'vector')\ncoll.delete_index(idx_config['id'])\n\n# ... do all updates ...\n\ncoll.add_index({\n    'type': 'vector',\n    'name': idx_config['name'],\n    'fields': idx_config['fields'],\n    'params': idx_config['params'],\n    'sparse': True,\n    'inBackground': True\n})\n```\n\n**When to apply:** Any batch update to a collection with a sparse vector index where some docs lack the vector field.\n\n**Real incident (2026-04-14):** Lineage backfill failed on 269 docs without embeddings. Error message was misleading (\"vector field not present\") even when providing the embedding in the UPDATE — the index validates against the OLD document state.\n\n## Enforcement\n\n- **PostToolUse hook** `no-regex-silo.sh` fires on every Edit\u002FWrite to .py files\n- Catches: frozensets >10 entries, regex entity classifiers, stopword lists >50, hand-rolled stemming, files >800 lines\n- Exit code 2 = hard block with explanation\n\n## ArangoDB Features Reference\n\n| Feature | AQL Syntax | Purpose |\n|---------|-----------|---------|\n| Stop word removal | `TOKENS(@q, 'text_en')` | Returns only content words |\n| Snowball stemming | `TOKENS(@q, 'text_en')` | \"running\" -> \"run\" |\n| BM25 scoring | `BM25(doc)` | TF-IDF with length normalization |\n| Exact match | `ANALYZER(doc.field == @val, 'identity')` | No stemming\u002Fstop words |\n| Fuzzy match | `LEVENSHTEIN_DISTANCE(a, b)` | Edit distance between strings |\n| N-gram similarity | `NGRAM_SIMILARITY(a, b, n)` | Character n-gram overlap |\n| Cosine similarity | `COSINE_SIMILARITY(a, b)` | Vector distance (no ANN index) |\n| Token search | `SEARCH ANALYZER(doc.f IN TOKENS(@q, 'text_en'), 'text_en')` | Full-text search |\n\n## Key Collections\n\n| Collection | Purpose | Key Fields |\n|-----------|---------|------------|\n| `sparta_controls` | All controls (4,253) | `control_id`, `name`, `source_framework`, `description` |\n| `sparta_qra` | QRA corpus (90K+) | `question`, `answer`, `control_id`, `reasoning` |\n| `domain_terms` | Known non-control terms | `term`, `category` |\n| `taxonomy_vocabulary` | Bridge\u002Ftactical keywords | `term`, `vocabulary_type`, `bridge_concept`, `category` |\n| `lessons` | Memory lessons | `problem`, `solution`, `tags` |\n\n## Querying ArangoDB Documentation\n\nThe full ArangoDB docs-hugo documentation is ingested into `\u002Fmemory` via `\u002Fingest-code`.\n\n```bash\n# Query ArangoDB docs — same as any \u002Fmemory recall\n\u002Fmemory recall --q \"AQL BM25 scoring ArangoSearch\"\n\u002Fmemory recall --q \"graph traversal OUTBOUND depth\"\n\u002Fmemory recall --q \"text_en analyzer stop words stemming\"\n\n# First-time ingestion (or manual re-ingest)\n.\u002Frun.sh ingest\n\n# Nightly incremental update (wired into \u002Fmonitor-memory)\n.\u002Frun.sh update\n\n# Check cache state\n.\u002Frun.sh status\n```\n\nDocs are stored with scope `arangodb-docs` and cached on 12TB at `\u002Fmnt\u002Fstorage12tb\u002Fcache\u002Farangodb-docs-hugo`.\n\n## References (detailed content)\n\n- [references\u002Fcookbook-views.md](references\u002Fcookbook-views.md) — ArangoSearch view creation patterns\n- [references\u002Fcookbook-aql-patterns.md](references\u002Fcookbook-aql-patterns.md) — AQL recipes (UPSERT, traversal, BM25, batch)\n- [references\u002Fcookbook-debugging.md](references\u002Fcookbook-debugging.md) — Debugging AQL (explain, profile, check views)\n- [references\u002Fanti-patterns-duplicated-functionality.md](references\u002Fanti-patterns-duplicated-functionality.md) — 7 real removed anti-patterns with fixes\n",{"name":5,"license":14,"composes":43,"metadata":46,"provides":62,"taxonomy":64,"triggers":66,"description":77},[44,34,45],"best-practices-python","ingest-code",{"client":47,"database":48,"defaults":49},"python-arango","ArangoDB",{"analyzers":50,"database_name":34,"search_pattern":52,"domain_collections":53,"performance_budgets":57,"max_python_stopwords":13,"max_hardcoded_domain_terms":13},[51,23],"text_en","BM25 + cosine rerank",[54,55,56],"domain_terms","taxonomy_vocabulary","sparta_controls",{"bm25_ms":58,"hybrid_ms":59,"exact_lookup_ms":60,"entity_extraction_ms":61},100,300,10,1000,[5,63],"skill-validation",[65,26],"precision",[19,67,68,69,70,71,72,73,74,75,76],"arangodb conventions","arango search","bm25 search","stop words","stemming","levenshtein","fuzzy matching","domain terms","entity classification","aql query","Repo-specific ArangoDB best practices: leverage text_en analyzer (stop words, stemming, BM25), use AQL functions (LEVENSHTEIN_DISTANCE, TOKENS, NGRAM_SIMILARITY, COSINE_SIMILARITY), store domain knowledge in collections not Python code, and never duplicate DB capabilities.\n","main","92d3cf5de96f8f7e1668e121e82a95faaa78a995","skills.pre-symlink-1776435493\u002Fbest-practices-arangodb",2,"https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills","https:\u002F\u002Fgithub.com\u002Fcateyephotos","2026-06-13 12:05:20",{"source":8,"skillName":5,"commands":86,"prompt":96,"zipAvailable":97},[87,90,93],{"tool":88,"command":89},"npx","npx skills add cateyephotos\u002Fagent-skills --skill best-practices-arangodb",{"tool":91,"command":92},"bunx","bunx skills add cateyephotos\u002Fagent-skills --skill best-practices-arangodb",{"tool":94,"command":95},"pnpm","pnpm dlx skills add cateyephotos\u002Fagent-skills --skill best-practices-arangodb","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills.pre-symlink-1776435493\u002Fbest-practices-arangodb\u002FSKILL.md",true,[99,105],{"path":100,"resourceType":101,"resourceTypeDesc":102,"sizeBytes":103,"sourceHtmlUrl":104},"run.sh","SCRIPT","脚本",3575,"https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills.pre-symlink-1776435493\u002Fbest-practices-arangodb\u002Frun.sh",{"path":106,"resourceType":101,"resourceTypeDesc":102,"sizeBytes":107,"sourceHtmlUrl":108},"sanity.sh",1079,"https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills.pre-symlink-1776435493\u002Fbest-practices-arangodb\u002Fsanity.sh",[110,136,166,192,217,235],{"skillNo":111,"name":112,"nameZh":6,"description":113,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":114,"categories":115,"keywords":116,"publishedAt":38},"01M71DBADHWPBVJWYQT9VAD1XE","best-practices-streamdeck","Project-specific best practices for the Stream Deck control suite: icon format constraints, socket vs config boundaries, widget button lifecycle, page creation via build_page, ArangoDB fallback patterns, and NVIS palette compliance.","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills\u002Fbest-practices-streamdeck\u002FSKILL.md",[],[18,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135],"best practices streamdeck","creation","for","project","page","static","deck","socket","lifecycle","monitor","typer","best-practices","icon","task","palette","control","native","via","build",{"skillNo":137,"name":138,"nameZh":6,"description":139,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":140,"categories":141,"keywords":147,"publishedAt":38},"01M71DARX9YN0QEXS6EM2C6E4X","best-practices-scillm","Error recovery and anti-patterns for scillm LLM proxy. Load this when scillm calls fail, timeout, or return unexpected results. Covers batch sizing, header requirements, model selection, and debugging workflow.","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills\u002Fbest-practices-scillm\u002FSKILL.md",[142],{"categoryNo":143,"categoryName":144,"taxonomyType":145,"taxonomyTypeDesc":146},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[148,18,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,119],"best practices scillm","scillm","error","processing","model","selection","model selection","error recovery","debugging","when","workflow","batch","anti-patterns","rate","rate limit","patterns","limit","proxy",{"skillNo":167,"name":168,"nameZh":6,"description":169,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":170,"categories":171,"keywords":175,"publishedAt":38},"01M71DAMV3PAAPBADP5EY1N61S","best-practices-rust","Repo-specific Rust best practices for agentic coding: thiserror + anyhow error handling, tokio async runtime, tracing for observability, clap for CLI, axum for HTTP, serde patterns, cargo workspace conventions, ownership and lifetimes, and idiomatic Rust 2024 edition patterns.","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills\u002Fbest-practices-rust\u002FSKILL.md",[172],{"categoryNo":173,"categoryName":174,"taxonomyType":145,"taxonomyTypeDesc":146},"01JBSEEDCAT000000000000007","代码审查",[176,177,18,150,178,179,180,181,182,183,29,184,26,185,186,187,188,189,190,191],"rust","best practices rust","async runtime","tokio","performance","code review","2024","agentic","review","server","cli","axum","rust error handling","polars","rust best practices","tracing",{"skillNo":193,"name":194,"nameZh":6,"description":195,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":196,"categories":197,"keywords":201,"publishedAt":38},"01M71DACXQ43RCTYYCB8WB1KPG","react-native-skills","React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills\u002Fbest-practices-react\u002Fskills\u002Freact-native-skills\u002FSKILL.md",[198],{"categoryNo":199,"categoryName":200,"taxonomyType":145,"taxonomyTypeDesc":146},"01JBSEEDCAT000000000000009","前端开发",[202,203,133,204,205,206,207,119,180,208,209,210,211,212,213,157,18,214,215,216],"react","react native","react native skills","modules","platform","animations","vercel","implementing","triggers","use","react native components","apps","tasks","components","with",{"skillNo":218,"name":219,"nameZh":6,"description":220,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":221,"categories":222,"keywords":224,"publishedAt":38},"01M71DA9YZ05NTRPEK9JAX283K","react-best-practices","React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React\u002FNext.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills\u002Fbest-practices-react\u002Fskills\u002Freact-best-practices\u002FSKILL.md",[223],{"categoryNo":199,"categoryName":200,"taxonomyType":145,"taxonomyTypeDesc":146},[202,225,18,180,226,157,29,214,215,227,228,229,230,231,163,232,208,233,210,234],"react best practices","writing","data fetching","react components","next","refactoring","data","Optimization","guidelines","pages",{"skillNo":236,"name":237,"nameZh":6,"description":238,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":239,"categories":240,"keywords":242,"publishedAt":38},"01M71DA77CYKDT0Q7VAXG4Y9QW","composition-patterns","React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.","https:\u002F\u002Fgithub.com\u002Fcateyephotos\u002Fagent-skills\u002Fblob\u002Fmain\u002Fskills\u002Fbest-practices-react\u002Fskills\u002Fcomposition-patterns\u002FSKILL.md",[241],{"categoryNo":199,"categoryName":200,"taxonomyType":145,"taxonomyTypeDesc":146},[243,244,163,245,246,208,247,210,248,249,211,157,250,214,215,251,252,253,202,216],"Composition","composition patterns","react composition patterns","react 19","render","boolean","component architecture","reusable","architecture","react composition","api"]