{"slug": "trelix-v2-7-to-v2-9-fixing-the-pipeline-debt", "title": "Trelix v2.7 to v2.9: Fixing the Pipeline Debt", "summary": "Trelix v2.7 to v2.9 releases fix pipeline debt including binary naming collisions, concurrency bugs, and silent data corruption. Developer Alex (author) discovered that CI had never built Linux binaries, test suites were not wired into CI allowing a regression where MCP server claimed 6 tools but had 8, and a TOCTOU race condition in the sparse embedder's lazy-load required double-checked locking. The most critical fix addressed silent foreign-key corruption during partial re-indexing that nulled graph edges without error messages.", "body_md": "# Trelix v2.7 to v2.9: Fixing the Pipeline Debt\n\n`release.yml`\n\nwas building macOS and Linux PyInstaller binaries both as `dist/trelix`\n\n, and since `softprops/action-gh-release`\n\nuploads by basename, the files collided. I had no way of knowing which OS actually survived the upload. It wasn't a bug in the retrieval logic—it was a failure in the delivery mechanism that passed CI because the workflow turned green, but the output was broken.## The Infrastructure Debt\n\nFixing the binary collision in v2.7.1 was the start of a two-week audit. I discovered that the PR-time CI workflow had never actually built a Linux binary, despite the release workflow doing so at tag time. I had to manually add the missing matrix entry and a verification step to ensure parity.\n\nThe most frustrating find was in `trelix-mcp`\n\n. The test suite wasn't even wired into CI. This allowed a regression to sit undetected where a test asserted the [MCP](/en/tags/mcp/) server had \"exactly 6 tools,\" while it actually had 8 (due to subscription tools added in v2.5.0). A test that passes for the wrong reason is worse than having no test at all.\n\nTo fix the deployment pipeline and avoid future collisions, I updated the build script to rename binaries uniquely before the upload step:\n\n```\n# Example fix for binary naming collision in GitHub Actions\nfor binary in dist/trelix*; do\n  os_name=$(echo \"$binary\" | cut -d'-' -f1) # Assuming naming convention like macos-trelix\n  mv \"$binary\" \"dist/trelix-${os_name}\"\ndone\n```\n\n## Concurrency and Race Conditions\n\nv2.7.2 was where I stopped pretending `check_same_thread=False`\n\nmade my code thread-safe. While I added Qdrant Cloud readiness and parallel BM25 read pools, the real value was in the five concurrency bugs I unearthed via stress tests.\n\nI found a TOCTOU (Time-of-Check to Time-of-Use) race in the sparse embedder's lazy-load. Two threads could check if a model was loaded, both see \"False,\" and both attempt to initialize the model simultaneously. I solved this using double-checked locking.\n\nI also dealt with an MCP stdout write race where concurrent notification writes interleaved partial JSON-RPC lines, corrupting the client's output. The fix required a strict lock around the write-and-flush sequence:\n\n``` python\nimport threading\n\n# Fix for MCP stdout interleaving\n_write_lock = threading.Lock()\n\ndef safe_write(data):\n    with _write_lock:\n        sys.stdout.write(data)\n        sys.stdout.flush()\n```\n\nOther critical fixes included implementing a max-subscriber cap and a TTL sweep for the subscription registry to prevent unbounded memory growth from misbehaving clients.\n\n## The Silent Data Corruption Bug\n\nThe most unsettling issue was silent foreign-key corruption during partial re-indexing. In the database schema, parent-symbol, call-callee, and type-edge columns were set to null on delete. While this seems safe, deleting a changed symbol's old row silently nulled those links on every row that referenced it—including unchanged rows.\n\nThere was no error message; the graph edges were just quietly disappearing as files changed. To stop this \"silent rot,\" I had to implement a snapshot-and-reverify mechanism to ensure referential integrity during the re-index process.\n\nIf you're building an LLM agent or a complex retrieval system, don't just test the \"happy path\" of your AI workflow. Stress test the concurrency and actually check your release assets. The pipeline is part of the product.\n\n[Next AI Coding and the Expertise Gap: Why Friction Matters →](/en/threads/2905/)", "url": "https://wpnews.pro/news/trelix-v2-7-to-v2-9-fixing-the-pipeline-debt", "canonical_source": "https://promptcube3.com/en/threads/2927/", "published_at": "2026-07-24 22:01:05+00:00", "updated_at": "2026-07-24 22:06:46.818402+00:00", "lang": "en", "topics": ["developer-tools", "ai-agents", "ai-infrastructure"], "entities": ["Trelix", "GitHub Actions", "Qdrant Cloud", "MCP"], "alternates": {"html": "https://wpnews.pro/news/trelix-v2-7-to-v2-9-fixing-the-pipeline-debt", "markdown": "https://wpnews.pro/news/trelix-v2-7-to-v2-9-fixing-the-pipeline-debt.md", "text": "https://wpnews.pro/news/trelix-v2-7-to-v2-9-fixing-the-pipeline-debt.txt", "jsonld": "https://wpnews.pro/news/trelix-v2-7-to-v2-9-fixing-the-pipeline-debt.jsonld"}}