boost-skill-cli 1.0.321__tar.gz → 1.0.323__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (575) hide show
  1. {boost_skill_cli-1.0.321/boost_skill_cli.egg-info → boost_skill_cli-1.0.323}/PKG-INFO +1 -1
  2. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/_version.py +2 -2
  3. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/rag.py +109 -12
  4. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323/boost_skill_cli.egg-info}/PKG-INFO +1 -1
  5. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/SOURCES.txt +1 -0
  6. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/near-duplicate-items-eat-the-result-slots.md +69 -1
  7. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap.html +69 -1
  8. boost_skill_cli-1.0.323/tests/unit/test_content_dedupe.py +144 -0
  9. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_rag.py +4 -2
  10. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.gitattributes +0 -0
  11. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.gitignore +0 -0
  12. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.gitleaks.toml +0 -0
  13. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.htmlvalidate.json +0 -0
  14. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.lighthouserc.json +0 -0
  15. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.lycheeignore +0 -0
  16. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.markdownlint-cli2.jsonc +0 -0
  17. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.pre-commit-config.yaml +0 -0
  18. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.stylelintrc.json +0 -0
  19. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.vale/styles/boost/Terminology.yml +0 -0
  20. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.vale/styles/config/vocabularies/boost/accept.txt +0 -0
  21. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.vale.ini +0 -0
  22. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/CLAUDE.md +0 -0
  23. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/CODE_OF_CONDUCT.md +0 -0
  24. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/CONTRIBUTING.md +0 -0
  25. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/LICENSE +0 -0
  26. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/MANIFEST.in +0 -0
  27. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/Makefile +0 -0
  28. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/README.md +0 -0
  29. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/SECURITY.md +0 -0
  30. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost +0 -0
  31. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/__init__.py +0 -0
  32. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/__main__.py +0 -0
  33. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/cli.py +0 -0
  34. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/cliparse.py +0 -0
  35. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/__init__.py +0 -0
  36. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/_common.py +0 -0
  37. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/bmad.py +0 -0
  38. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/configuration.py +0 -0
  39. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/discovery.py +0 -0
  40. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/hooks.py +0 -0
  41. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/info.py +0 -0
  42. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/intelligence.py +0 -0
  43. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/pkg.py +0 -0
  44. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/quality.py +0 -0
  45. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/run.py +0 -0
  46. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/safety.py +0 -0
  47. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/taps.py +0 -0
  48. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/team.py +0 -0
  49. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/__init__.py +0 -0
  50. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/adapters.py +0 -0
  51. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/agents.py +0 -0
  52. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/ai.py +0 -0
  53. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/capabilities.py +0 -0
  54. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/catalog.py +0 -0
  55. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/claude_settings.py +0 -0
  56. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/config.py +0 -0
  57. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/dense.py +0 -0
  58. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/ed25519.py +0 -0
  59. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/embed.py +0 -0
  60. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/faithfulness.py +0 -0
  61. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/frontmatter.py +0 -0
  62. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/gitutil.py +0 -0
  63. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/imperative.py +0 -0
  64. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/injectscan.py +0 -0
  65. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/installscan.py +0 -0
  66. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/integrity.py +0 -0
  67. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/journal.py +0 -0
  68. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/localembed.py +0 -0
  69. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/lockfile.py +0 -0
  70. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/logs.py +0 -0
  71. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/mcp.py +0 -0
  72. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/mcpdecl.py +0 -0
  73. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/mcphost.py +0 -0
  74. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/minisign.py +0 -0
  75. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/nethttp.py +0 -0
  76. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/output.py +0 -0
  77. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/paths.py +0 -0
  78. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/policy.py +0 -0
  79. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/projectlock.py +0 -0
  80. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/provenance.py +0 -0
  81. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/registry.py +0 -0
  82. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/resolve.py +0 -0
  83. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/rules.py +0 -0
  84. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/scopes.py +0 -0
  85. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/secretscan.py +0 -0
  86. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/selfupdate.py +0 -0
  87. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/serve.py +0 -0
  88. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/stackprobe.py +0 -0
  89. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/staleness.py +0 -0
  90. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/store.py +0 -0
  91. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/trustaudit.py +0 -0
  92. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/typosquat.py +0 -0
  93. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/updatediff.py +0 -0
  94. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/util.py +0 -0
  95. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/workflows.py +0 -0
  96. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/data/registries.json +0 -0
  97. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/errors.py +0 -0
  98. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/spin.py +0 -0
  99. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
  100. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/entry_points.txt +0 -0
  101. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/requires.txt +0 -0
  102. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/top_level.txt +0 -0
  103. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/codecov.yml +0 -0
  104. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/.claude/settings.local.json +0 -0
  105. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/DEBUGGING.md +0 -0
  106. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/adapters.html +0 -0
  107. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/README.md +0 -0
  108. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-components-core.md +0 -0
  109. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-containers.md +0 -0
  110. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-context.md +0 -0
  111. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-dynamic-install.md +0 -0
  112. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/distill.gif +0 -0
  113. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/doctor.gif +0 -0
  114. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/explain.gif +0 -0
  115. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/hooks.gif +0 -0
  116. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/install.gif +0 -0
  117. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/pulse.gif +0 -0
  118. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/search.gif +0 -0
  119. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/tap.gif +0 -0
  120. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/distill.tape +0 -0
  121. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/doctor.tape +0 -0
  122. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/explain.tape +0 -0
  123. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/hooks.tape +0 -0
  124. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/install.tape +0 -0
  125. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/pulse.tape +0 -0
  126. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/search.tape +0 -0
  127. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/tap.tape +0 -0
  128. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel.html +0 -0
  129. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/commands.html +0 -0
  130. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/demo.gif +0 -0
  131. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/demo.tape +0 -0
  132. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/design-roadmap.html +0 -0
  133. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/eval.html +0 -0
  134. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/index.html +0 -0
  135. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/mcp-hub.html +0 -0
  136. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/rag-architecture.md +0 -0
  137. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D01.md +0 -0
  138. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D02.md +0 -0
  139. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D03.md +0 -0
  140. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D04.md +0 -0
  141. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D05.md +0 -0
  142. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D06.md +0 -0
  143. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D07.md +0 -0
  144. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D08.md +0 -0
  145. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D09.md +0 -0
  146. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D10.md +0 -0
  147. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D11.md +0 -0
  148. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D12.md +0 -0
  149. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D13.md +0 -0
  150. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D14.md +0 -0
  151. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D15.md +0 -0
  152. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D16.md +0 -0
  153. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D17.md +0 -0
  154. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D18.md +0 -0
  155. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D19.md +0 -0
  156. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D20.md +0 -0
  157. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D21.md +0 -0
  158. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D22.md +0 -0
  159. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D23.md +0 -0
  160. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D24.md +0 -0
  161. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D25.md +0 -0
  162. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D26.md +0 -0
  163. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
  164. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/actionlint-skips-shellcheck-silently.md +0 -0
  165. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/adapter-conformance-langgraph-leg-never-ran.md +0 -0
  166. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ai-bridge-silent-failure-logging.md +0 -0
  167. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ambiguous-tap-short-name-resolution.md +0 -0
  168. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
  169. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
  170. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
  171. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/bm25-index-is-one-json-blob.md +0 -0
  172. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/boost-run-live-agents.md +0 -0
  173. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
  174. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
  175. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/browse-crashes-selecting-a-rule-or-workflow.md +0 -0
  176. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
  177. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
  178. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
  179. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ci-job-timeout-minutes.md +0 -0
  180. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ci-summary-step-fails-itself.md +0 -0
  181. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ci-time-is-now-runner-queueing.md +0 -0
  182. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
  183. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/code-scanning-rule-can-be-restored.md +0 -0
  184. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/codeql-job-rename-stranded-merge-protection.md +0 -0
  185. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/command-carousel-page.md +0 -0
  186. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/command-reference-docs-site.md +0 -0
  187. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
  188. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -0
  189. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
  190. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
  191. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/crash-listing-branch-untested.md +0 -0
  192. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
  193. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/demo-cannot-open-its-own-pr.md +0 -0
  194. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/demo-gif-workflow-has-never-succeeded.md +0 -0
  195. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dense-search-fallback-and-stale-tap-pruning.md +0 -0
  196. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependabot-missing-pip-ecosystem.md +0 -0
  197. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependabot-regeneration-drops-platform-pins.md +0 -0
  198. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependabot-root-pip-entry-duplicates-requirements.md +0 -0
  199. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
  200. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
  201. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
  202. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
  203. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
  204. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/eval-corpus-cannot-see-real-retrieval.md +0 -0
  205. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
  206. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
  207. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
  208. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
  209. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
  210. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
  211. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
  212. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
  213. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
  214. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/gemini-cli-agent-target.md +0 -0
  215. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/github-community-health-files.md +0 -0
  216. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
  217. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
  218. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
  219. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/html-validation-html-validate.md +0 -0
  220. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-docs-never-mentioned-upgrading.md +0 -0
  221. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-from-path-bypasses-policy-and-pin-checks.md +0 -0
  222. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
  223. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
  224. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/installed-skill-trust-audit.md +0 -0
  225. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
  226. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/internal-architecture-diagrams.md +0 -0
  227. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
  228. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/journal-rotation-race-and-handle-leak.md +0 -0
  229. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/keyless-dense-tier-local-static-embeddings.md +0 -0
  230. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/keyless-semantic-search-for-everyone.md +0 -0
  231. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
  232. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/license-compliance-scanning.md +0 -0
  233. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
  234. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lint-tap-misscopes-rule-workflow-entries.md +0 -0
  235. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
  236. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
  237. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lock-invariant-cannot-parse-extras-pins.md +0 -0
  238. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
  239. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
  240. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
  241. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/main-has-no-branch-protection.md +0 -0
  242. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/main-ruleset-ref-pattern-has-literal-quotes.md +0 -0
  243. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/make-lint-masks-actionlint-failures.md +0 -0
  244. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
  245. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
  246. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/markdownlint-lints-the-fuzz-corpus.md +0 -0
  247. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-aware-skills.md +0 -0
  248. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-check-skills-before-starting-a-task.md +0 -0
  249. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
  250. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-install-skips-the-injection-scan.md +0 -0
  251. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
  252. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-one-benefit-nameable-task.md +0 -0
  253. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
  254. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
  255. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-servers-ignore-install-scope.md +0 -0
  256. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
  257. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
  258. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/merge-queue-would-deadlock-most-required-checks.md +0 -0
  259. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/merged-loop-branches-are-never-pruned.md +0 -0
  260. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
  261. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-gate-was-the-whole-critical-path.md +0 -0
  262. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
  263. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
  264. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
  265. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-shard-floor-is-one-file.md +0 -0
  266. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mypy-strict-mode-for-commands.md +0 -0
  267. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/negative-limit-inverts-log-pulse-output.md +0 -0
  268. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/onboard-overwrites-generated-files-without-confirm.md +0 -0
  269. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
  270. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/one-commit-can-cut-two-releases.md +0 -0
  271. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
  272. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
  273. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
  274. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/path-traversal-unsanitized-rule-workflow-name.md +0 -0
  275. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
  276. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
  277. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/post-deploy-always-defeated-by-skipped-setup.md +0 -0
  278. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
  279. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
  280. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prerequisites-and-semantic-search-setup.md +0 -0
  281. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
  282. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
  283. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
  284. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
  285. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
  286. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
  287. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
  288. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/publish-gate-ignores-pip-audit-and-metadata.md +0 -0
  289. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/publish-trigger-was-reachable-from-a-fork.md +0 -0
  290. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/pytest-tmpdir-cve-blocked-by-py39-floor.md +0 -0
  291. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
  292. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
  293. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
  294. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
  295. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/rel-time-tests-race-the-wall-clock.md +0 -0
  296. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/release-verifies-the-wrong-commit.md +0 -0
  297. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/required-checks-can-declare-a-check-that-deadlocks-prs.md +0 -0
  298. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/required-checks-config-drift.md +0 -0
  299. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
  300. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
  301. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/roadmap-html-goes-stale-on-every-rebase.md +0 -0
  302. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
  303. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ruff-016-widens-the-default-rule-set.md +0 -0
  304. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
  305. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
  306. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
  307. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
  308. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-declares-the-wrong-version.md +0 -0
  309. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
  310. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-release-event-never-fires.md +0 -0
  311. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
  312. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/scheduled-toolchain-lock-regeneration.md +0 -0
  313. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/scorecard-findings-triage.md +0 -0
  314. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
  315. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
  316. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
  317. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
  318. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/self-update-broken-for-pip-pipx-installs.md +0 -0
  319. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/serve-py-traversal-guards-untested.md +0 -0
  320. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
  321. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
  322. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
  323. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
  324. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
  325. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
  326. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/structured-json-log-output-mode.md +0 -0
  327. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
  328. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/surface-every-docs-page-from-the-guide.md +0 -0
  329. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sync-deletes-unowned-broken-symlinks.md +0 -0
  330. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sync-relinks-into-every-agent-ignoring-scope.md +0 -0
  331. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
  332. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
  333. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/typo-detection-codespell.md +0 -0
  334. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
  335. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
  336. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/uninstall-has-no-confirmation-prompt.md +0 -0
  337. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
  338. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/update-diff-before-apply.md +0 -0
  339. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
  340. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/update-reinstall-widens-agent-scope.md +0 -0
  341. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
  342. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
  343. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
  344. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/wire-bdd-suite-into-ci.md +0 -0
  345. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
  346. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
  347. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
  348. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workspace-scope-install.md +0 -0
  349. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/zizmor-pin-is-a-yanked-release.md +0 -0
  350. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/eslint.config.mjs +0 -0
  351. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/README.md +0 -0
  352. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/__init__.py +0 -0
  353. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/baseline.json +0 -0
  354. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/faithfulness.py +0 -0
  355. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/golden_set.json +0 -0
  356. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/make_corpus.py +0 -0
  357. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/make_golden.py +0 -0
  358. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/metrics.py +0 -0
  359. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/run_evals.py +0 -0
  360. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/examples/README.md +0 -0
  361. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/examples/adapt-demo.sh +0 -0
  362. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/examples/boost-run-prototype.sh +0 -0
  363. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/index.html +0 -0
  364. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/noxfile.py +0 -0
  365. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/pyproject.toml +0 -0
  366. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/coverage-tools.in +0 -0
  367. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/coverage-tools.txt +0 -0
  368. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/lint-tools.in +0 -0
  369. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/lint-tools.txt +0 -0
  370. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/mutation-tools.in +0 -0
  371. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/mutation-tools.txt +0 -0
  372. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/platform-pins.lock +0 -0
  373. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/release-tools.in +0 -0
  374. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/release-tools.txt +0 -0
  375. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/test-tools.in +0 -0
  376. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/test-tools.txt +0 -0
  377. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/a11y_check.py +0 -0
  378. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/bench_cli.sh +0 -0
  379. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/build_command_reference.py +0 -0
  380. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/build_registries.py +0 -0
  381. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/build_roadmap.py +0 -0
  382. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/check_anchors.py +0 -0
  383. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/check_licenses.py +0 -0
  384. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/check_required_checks.py +0 -0
  385. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/ensure_eval_corpus.sh +0 -0
  386. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_explain.py +0 -0
  387. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_gate.py +0 -0
  388. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_recommend.py +0 -0
  389. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_retrieval.py +0 -0
  390. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_stats_summary.py +0 -0
  391. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/import_budget.py +0 -0
  392. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/lock_toolchain.py +0 -0
  393. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/mutation_gate.py +0 -0
  394. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/mutation_shards.py +0 -0
  395. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/mutation_weights.json +0 -0
  396. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/perf_gate.py +0 -0
  397. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/post_deploy_smoke.py +0 -0
  398. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/release_guard.py +0 -0
  399. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/release_preflight.py +0 -0
  400. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/setup.cfg +0 -0
  401. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/sonar-project.properties +0 -0
  402. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/README.md +0 -0
  403. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/boost.css +0 -0
  404. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/boost.js +0 -0
  405. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/demo.html +0 -0
  406. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/adapt.feature +0 -0
  407. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/browse.feature +0 -0
  408. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/count.feature +0 -0
  409. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/discover.feature +0 -0
  410. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/doctor.feature +0 -0
  411. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/environment.py +0 -0
  412. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/explain.feature +0 -0
  413. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/index.feature +0 -0
  414. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/install.feature +0 -0
  415. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/list.feature +0 -0
  416. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/mcp.feature +0 -0
  417. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/search.feature +0 -0
  418. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/cli_steps.py +0 -0
  419. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/discovery_steps.py +0 -0
  420. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/mcp_steps.py +0 -0
  421. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/quality_steps.py +0 -0
  422. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/conftest.py +0 -0
  423. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/baseline.json +0 -0
  424. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/explain.jsonl +0 -0
  425. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/golden-natural.jsonl +0 -0
  426. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/golden.jsonl +0 -0
  427. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/recommend.jsonl +0 -0
  428. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/taps.txt +0 -0
  429. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_capabilities_policy.py +0 -0
  430. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_adapt.py +0 -0
  431. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_bmad.py +0 -0
  432. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_configuration.py +0 -0
  433. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_discovery.py +0 -0
  434. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_hooks.py +0 -0
  435. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_info.py +0 -0
  436. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_intelligence.py +0 -0
  437. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_pkg.py +0 -0
  438. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_quality.py +0 -0
  439. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_taps.py +0 -0
  440. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_team.py +0 -0
  441. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_everyday_loop.py +0 -0
  442. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_integrity_enforce.py +0 -0
  443. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_property_parsers.py +0 -0
  444. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_run.py +0 -0
  445. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_tap_signing.py +0 -0
  446. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_workspace_scope.py +0 -0
  447. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/README.md +0 -0
  448. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/01-typical.md +0 -0
  449. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/02-lossy-version.md +0 -0
  450. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/03-lossy-numbers.md +0 -0
  451. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/04-lists.md +0 -0
  452. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/05-nested.md +0 -0
  453. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/06-empty-block.md +0 -0
  454. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/07-unterminated.md +0 -0
  455. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/08-none.md +0 -0
  456. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/09-unbalanced-quote.md +0 -0
  457. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/10-keywords.md +0 -0
  458. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/11-crlf.md +0 -0
  459. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/12-bom.md +0 -0
  460. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/01-shorthand.txt +0 -0
  461. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/02-https.txt +0 -0
  462. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/03-ssh.txt +0 -0
  463. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/04-traversal.txt +0 -0
  464. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/05-empty.txt +0 -0
  465. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/06-space.txt +0 -0
  466. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/07-slash.txt +0 -0
  467. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/08-bare-host.txt +0 -0
  468. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/fuzz_frontmatter.py +0 -0
  469. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/fuzz_registry.py +0 -0
  470. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/make_fixture.py +0 -0
  471. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/perf/test_benchmarks.py +0 -0
  472. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/smoke.sh +0 -0
  473. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_a11y_check.py +0 -0
  474. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_adapters.py +0 -0
  475. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_adapters_multi.py +0 -0
  476. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_adapters_runner.py +0 -0
  477. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_agents.py +0 -0
  478. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_ai.py +0 -0
  479. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_capabilities.py +0 -0
  480. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_catalog.py +0 -0
  481. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_check_anchors.py +0 -0
  482. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_check_licenses.py +0 -0
  483. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_claude_settings.py +0 -0
  484. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_cliparse.py +0 -0
  485. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_codeql_analysis_key.py +0 -0
  486. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_command_reference_fresh.py +0 -0
  487. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_community_health_files.py +0 -0
  488. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_config.py +0 -0
  489. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense.py +0 -0
  490. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense_fallback.py +0 -0
  491. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense_fix_hint.py +0 -0
  492. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense_status.py +0 -0
  493. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dependabot_config.py +0 -0
  494. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_docs_pages_linked.py +0 -0
  495. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_docsite_chrome.py +0 -0
  496. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_ed25519.py +0 -0
  497. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_embed.py +0 -0
  498. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_embed_local.py +0 -0
  499. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_entry_key.py +0 -0
  500. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_errors_and_cli_table.py +0 -0
  501. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_baseline.py +0 -0
  502. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_faithfulness.py +0 -0
  503. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_gate.py +0 -0
  504. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_metrics.py +0 -0
  505. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_stats_summary.py +0 -0
  506. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_faithfulness.py +0 -0
  507. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_frontmatter.py +0 -0
  508. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_fuzz_targets.py +0 -0
  509. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_gitleaks_config.py +0 -0
  510. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_gitutil.py +0 -0
  511. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_imperative.py +0 -0
  512. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_import_budget.py +0 -0
  513. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_injectscan.py +0 -0
  514. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_installscan.py +0 -0
  515. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_integrity.py +0 -0
  516. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_journal.py +0 -0
  517. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_localembed.py +0 -0
  518. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_lockfile.py +0 -0
  519. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_logs.py +0 -0
  520. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_marketing_counts.py +0 -0
  521. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcp.py +0 -0
  522. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcp_install_scan.py +0 -0
  523. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcpdecl.py +0 -0
  524. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcphost.py +0 -0
  525. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_minisign.py +0 -0
  526. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutation_hardening.py +0 -0
  527. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutation_shards.py +0 -0
  528. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutation_subfile_shards.py +0 -0
  529. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutmut_source_paths.py +0 -0
  530. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_nethttp.py +0 -0
  531. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_output.py +0 -0
  532. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_paths.py +0 -0
  533. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_perf_gate.py +0 -0
  534. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_pkg_confusions.py +0 -0
  535. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_pkg_injection.py +0 -0
  536. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_pkg_secrets.py +0 -0
  537. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_platform_pins.py +0 -0
  538. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_policy.py +0 -0
  539. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_post_deploy_smoke.py +0 -0
  540. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_project_mcp_sidecar.py +0 -0
  541. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_projectlock.py +0 -0
  542. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_provenance.py +0 -0
  543. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_rag_fusion.py +0 -0
  544. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_registries_fresh.py +0 -0
  545. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_registry.py +0 -0
  546. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_registry_categories.py +0 -0
  547. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_release_guard.py +0 -0
  548. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_release_preflight.py +0 -0
  549. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_required_checks.py +0 -0
  550. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_resolve.py +0 -0
  551. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_roadmap_fresh.py +0 -0
  552. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_rules.py +0 -0
  553. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_scopes.py +0 -0
  554. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_secretscan.py +0 -0
  555. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_selfupdate.py +0 -0
  556. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_serve.py +0 -0
  557. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_spin.py +0 -0
  558. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_stackprobe.py +0 -0
  559. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_staleness.py +0 -0
  560. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_store.py +0 -0
  561. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_token_parity.py +0 -0
  562. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_toolchain_lock.py +0 -0
  563. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_trustaudit.py +0 -0
  564. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_typosquat.py +0 -0
  565. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_updatediff.py +0 -0
  566. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_util.py +0 -0
  567. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_version.py +0 -0
  568. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_workflow_concurrency.py +0 -0
  569. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_workflow_timeouts.py +0 -0
  570. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_workflows.py +0 -0
  571. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/a11y_check.mjs +0 -0
  572. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/console_check.mjs +0 -0
  573. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/package-lock.json +0 -0
  574. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/package.json +0 -0
  575. {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/visual_check.mjs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.321
3
+ Version: 1.0.323
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.0.321'
22
- __version_tuple__ = version_tuple = (1, 0, 321)
21
+ __version__ = version = '1.0.323'
22
+ __version_tuple__ = version_tuple = (1, 0, 323)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -15,13 +15,14 @@ no AI -> the BM25 order is returned as-is.
15
15
  """
16
16
  from __future__ import annotations
17
17
 
18
+ import hashlib
18
19
  import json
19
20
  import math
20
21
  import re
21
22
  from collections import defaultdict
22
23
  from collections.abc import Sequence
23
24
  from pathlib import Path
24
- from typing import Dict, List, Optional, Tuple
25
+ from typing import Dict, List, Optional, Tuple, cast
25
26
 
26
27
  try: # TypedDict lives in typing on 3.9+, kept optional for safety
27
28
  from typing import TypedDict
@@ -33,7 +34,7 @@ from . import ai, catalog, frontmatter, gitutil, paths, registry, util
33
34
  # v2: `snip` stores a larger head of the matched chunk (not just SNIP_WIDTH
34
35
  # chars) so `retrieve` can window it onto the query terms; bump forces a
35
36
  # one-time reindex.
36
- INDEX_VERSION = 4
37
+ INDEX_VERSION = 5
37
38
  ENGINE = "bm25"
38
39
 
39
40
  # Chunking defaults (documented in docs/rag-architecture.md §4).
@@ -62,10 +63,13 @@ _STOPWORDS = frozenset({
62
63
  })
63
64
 
64
65
 
65
- class Hit(TypedDict): # type: ignore[misc]
66
+ class Hit(TypedDict, total=False): # type: ignore[misc]
66
67
  entry: dict # the live catalog entry (name, tap, kind, skill_md, ...)
67
68
  score: float # BM25 relevance (higher = better)
68
69
  snippet: str # best-matching passage, for display + rerank context
70
+ content: str # digest of the body, for collapsing byte-identical copies.
71
+ # Optional: dense hits arrive without one and have it
72
+ # attached from the BM25 index (see `_with_content`).
69
73
 
70
74
 
71
75
  # --------------------------------------------------------------- tokenizing
@@ -193,13 +197,16 @@ def _make_docs(entries: List[dict], tap_paths: Dict[str, Path]) -> List[dict]:
193
197
  docs: List[dict] = []
194
198
  for e in entries:
195
199
  body = read_body(e, tap_paths)
200
+ # Hashed here because the body is already in hand; doing it at query
201
+ # time would mean re-reading ~30k files to answer one search.
202
+ digest = hashlib.sha256(body.strip().encode("utf-8", "replace")).hexdigest()[:16]
196
203
  tf: Dict[str, int] = defaultdict(int)
197
204
  for tok in tokenize(surface(e) + "\n" + body):
198
205
  tf[tok] += 1
199
206
  if not tf:
200
207
  continue
201
208
  docs.append({
202
- "n": e["name"], "t": e["tap"], "f": e["skill_md"],
209
+ "n": e["name"], "t": e["tap"], "f": e["skill_md"], "h": digest,
203
210
  "k": e.get("kind", "skill"),
204
211
  "c": 0, "l": sum(tf.values()),
205
212
  "snip": body[:SNIP_STORE].strip(), # windowed at retrieve time
@@ -268,6 +275,7 @@ def _save(docs: List[dict], commits: Dict[str, str]) -> None:
268
275
  postings[term].append([doc_id, tf])
269
276
  total_len += d["l"]
270
277
  meta_docs.append({"n": d["n"], "t": d["t"], "f": d["f"], "k": d["k"],
278
+ "h": d.get("h", ""),
271
279
  "c": d["c"], "l": d["l"], "snip": d["snip"]})
272
280
  payload = {
273
281
  "version": INDEX_VERSION,
@@ -418,6 +426,54 @@ def entry_key(entry: dict) -> Tuple[str, str]:
418
426
  return (entry["tap"], entry["skill_md"])
419
427
 
420
428
 
429
+ def dedupe_by_content(hits: List[Hit], limit: int) -> List[Hit]:
430
+ """Collapse byte-identical copies, then take ``limit``.
431
+
432
+ Measured over 77 tapped registries: 78.3% of 29,938 entries are
433
+ byte-identical duplicates (largest cluster 40 copies), and the
434
+ natural-language query set averaged 4.94 of 10 result slots consumed by a
435
+ repeat — worst case 9 of 10. Registries mirror each other, so N copies of one
436
+ rule across N taps ate N of the ten slots a user sees.
437
+
438
+ Clustering is on the **body**, never the name. 82.7% of entries share a name
439
+ but only 78.3% share a body, and that ~4.4% gap is real distinct content —
440
+ the ``admin-interface-rule`` pair fixed in #366 is two different rules under
441
+ one name. Clustering on content cannot make that mistake: of 14,153 distinct
442
+ bodies, the number of clusters spanning more than one name is zero.
443
+
444
+ Within a cluster every copy is byte-identical, so *which* one survives is
445
+ purely about where the user would rather install from: a ``curated`` tap wins
446
+ over a better-ranked uncurated one. The cluster keeps its best score either
447
+ way, so promoting the trusted copy never demotes the result.
448
+
449
+ A hit with no recorded content hash is always kept — two unknowns must not
450
+ collapse into each other, which would hide real results.
451
+ """
452
+ best: Dict[str, int] = {} # content hash -> index into `out`
453
+ out: List[Hit] = []
454
+ for hit in hits:
455
+ digest = hit.get("content")
456
+ if not digest:
457
+ out.append(hit) # unknown content is never a match
458
+ continue
459
+ seen = best.get(digest)
460
+ if seen is None:
461
+ best[digest] = len(out)
462
+ out.append(hit)
463
+ continue
464
+ # Same body already shown. Keep its rank, but prefer a curated source.
465
+ kept = out[seen]
466
+ if hit["entry"].get("curated") and not kept["entry"].get("curated"):
467
+ # cast, not a literal annotation: `Hit` is total=False for the
468
+ # optional `content` key, and NotRequired needs 3.11 while boost
469
+ # targets 3.9.
470
+ out[seen] = cast(Hit, {"entry": hit["entry"],
471
+ "score": kept["score"],
472
+ "snippet": kept["snippet"],
473
+ "content": digest})
474
+ return out[:limit]
475
+
476
+
421
477
  def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
422
478
  entries: Optional[List[dict]] = None) -> List[Hit]:
423
479
  """Top-k full-content hits for ``query``. ``[]`` if the index is empty."""
@@ -430,7 +486,7 @@ def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
430
486
  entries = catalog.all_entries() if entries is None else entries
431
487
  live = {entry_key(e): e for e in entries}
432
488
  docs = raw["docs"]
433
- best: Dict[Tuple[str, str], Tuple[float, str]] = {}
489
+ best: Dict[Tuple[str, str], Tuple[float, str, str]] = {}
434
490
  for doc_id, score in _bm25(terms, raw).items():
435
491
  d = docs[doc_id]
436
492
  if kind is not None and d["k"] != kind:
@@ -440,17 +496,20 @@ def retrieve(query: str, k: int = 60, kind: Optional[str] = None,
440
496
  continue
441
497
  prev = best.get(key)
442
498
  if prev is None or score > prev[0]:
443
- best[key] = (score, d["snip"])
499
+ best[key] = (score, d["snip"], d.get("h", ""))
444
500
  # Tie-break on the displayed name, not the identity key: the key is now
445
501
  # (tap, path), and ordering by it would silently reshuffle equal-scoring
446
502
  # results relative to every previous release.
447
503
  ranked = sorted(best.items(),
448
504
  key=lambda kv: (-kv[1][0], live[kv[0]]["name"], kv[0]))
505
+ # Over-fetch, then collapse byte-identical copies before taking k — the
506
+ # duplicates have to be removed from the pool, not from the visible page,
507
+ # or they still consume the slots they were removed from.
449
508
  hits: List[Hit] = [
450
- {"entry": live[key], "score": score,
509
+ {"entry": live[key], "score": score, "content": digest,
451
510
  "snippet": _passage(snip, terms)} # type: ignore[typeddict-item]
452
- for key, (score, snip) in ranked[:k]]
453
- return hits
511
+ for key, (score, snip, digest) in ranked]
512
+ return dedupe_by_content(hits, k)
454
513
 
455
514
 
456
515
  def rerank(query: str, hits: List[Hit], limit: int = 10) -> Tuple[List[Hit], str]:
@@ -516,6 +575,39 @@ def rrf_fuse(rankings: List[List[Hit]], limit: int = 60) -> List[Hit]:
516
575
  return fused
517
576
 
518
577
 
578
+ def content_hashes() -> Dict[Tuple[str, str], str]:
579
+ """``entry_key`` -> content hash, read from the BM25 index.
580
+
581
+ The dense store does not carry the hash and does not need to: BM25 indexes
582
+ the same entries and auto-builds on first search, so one map serves every
583
+ engine. Keeping it in one place also means the two engines can never
584
+ disagree about which copies are identical.
585
+ """
586
+ raw = _load_raw()
587
+ if not raw:
588
+ return {}
589
+ return {(d["t"], d["f"]): d["h"]
590
+ for d in raw.get("docs", []) if d.get("h")}
591
+
592
+
593
+ def _with_content(hits: List[Hit]) -> List[Hit]:
594
+ """Attach content hashes to hits that lack them (dense, and fused output)."""
595
+ if all(h.get("content") for h in hits):
596
+ return hits
597
+ hashes = content_hashes()
598
+ if not hashes:
599
+ return hits
600
+ filled: List[Hit] = []
601
+ for h in hits:
602
+ if h.get("content"):
603
+ filled.append(h)
604
+ continue
605
+ filled.append(cast(Hit, {
606
+ "entry": h["entry"], "score": h["score"], "snippet": h["snippet"],
607
+ "content": hashes.get(entry_key(h["entry"]), "")}))
608
+ return filled
609
+
610
+
519
611
  def retrieve_any(query: str, k: int = 60, kind: Optional[str] = None,
520
612
  entries: Optional[List[dict]] = None
521
613
  ) -> Tuple[Optional[List[Hit]], str]:
@@ -551,13 +643,18 @@ def retrieve_any(query: str, k: int = 60, kind: Optional[str] = None,
551
643
  # counts as a ranking to fuse.
552
644
  bm25_hits = retrieve(query, k=pool, kind=kind,
553
645
  entries=entries) if ready() else None
646
+ # Dedupe at this seam as well as inside `retrieve`, because dense hits carry
647
+ # no hash of their own and fusion can reintroduce a duplicate that BM25
648
+ # already dropped — the copies are distinct `(tap, path)` keys, so RRF has
649
+ # no reason to treat them as one.
554
650
  if dense_hits and bm25_hits:
555
- return (rrf_fuse([bm25_hits, dense_hits], limit=k),
651
+ fused = rrf_fuse([bm25_hits, dense_hits], limit=max(k, RRF_K))
652
+ return (dedupe_by_content(_with_content(fused), k),
556
653
  "hybrid RRF (BM25 + dense)")
557
654
  if dense_hits:
558
- return dense_hits[:k], "dense vectors"
655
+ return dedupe_by_content(_with_content(dense_hits), k), "dense vectors"
559
656
  if bm25_hits is not None:
560
- return bm25_hits[:k], "BM25 full-content"
657
+ return bm25_hits[:k], "BM25 full-content" # already deduped
561
658
  return None, ""
562
659
 
563
660
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boost-skill-cli
3
- Version: 1.0.321
3
+ Version: 1.0.323
4
4
  Summary: boost — Homebrew for AI coding skills
5
5
  Author: Jonathan Reyes
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -483,6 +483,7 @@ tests/unit/test_codeql_analysis_key.py
483
483
  tests/unit/test_command_reference_fresh.py
484
484
  tests/unit/test_community_health_files.py
485
485
  tests/unit/test_config.py
486
+ tests/unit/test_content_dedupe.py
486
487
  tests/unit/test_dense.py
487
488
  tests/unit/test_dense_fallback.py
488
489
  tests/unit/test_dense_fix_hint.py
@@ -9,7 +9,7 @@ impact: High
9
9
  wow: 4
10
10
  note: 56.3% of entries share a name
11
11
  order: 71
12
- owner: loop/entry-key-collision
12
+ owner: loop/content-hash-dedup
13
13
  pr:
14
14
  title: Near-duplicate items consume the top-10, and it gets worse with every tap
15
15
  ---
@@ -91,3 +91,71 @@ at one path — and were corrected.
91
91
  prior, and the 83-tap re-measurement of the fusion bound are all untouched. Note the two are
92
92
  independent: dedup <em>merges</em> copies that are genuinely the same, whereas this fix stops copies
93
93
  that were never the same from being merged by accident.
94
+
95
+ <b>The curve this card asked for.</b> The note above measured 0 duplicate slots over the pinned
96
+ 6-tap corpus and said, correctly, that absence at 6 taps says nothing about 83. Re-measured over 77
97
+ tapped registries sampled across the shipped list, with the same 50 natural-language queries,
98
+ counting how many of the ten slots a user sees are second-or-later copies of a name already in that
99
+ same result list:
100
+
101
+ <code>6 taps</code>: 1,122 entries, 0.0% sharing a name, <b>0.00</b> duplicate slots/query &middot;
102
+ <code>12</code>: 14,194 entries, 77.8%, <b>4.28</b> &middot;
103
+ <code>25</code>: 16,633, 77.9%, 4.12 &middot;
104
+ <code>40</code>: 17,470, 74.7%, 3.88 &middot;
105
+ <code>60</code>: 29,137, 83.9%, 5.08 &middot;
106
+ <code>77</code>: 29,938 entries, 82.7% sharing a name, <b>4.94</b> duplicate slots/query.
107
+
108
+ So at realistic corpus size <b>roughly half the ten slots a user sees are repeat copies of a name
109
+ already in the list</b>, worst observed 9 of 10 (&ldquo;our full-text queries have got slow as the
110
+ index grew&rdquo;). The card's thesis holds, and more sharply than its own 56.3% figure suggested.
111
+
112
+ <b>But the driver is not tap count.</b> Entries jump 1,122 &rarr; 14,194 between 6 and 12 taps, so
113
+ the sample is not uniform: a single awesome-list-style rule registry contributes more entries than
114
+ the entire 6-tap eval corpus. Duplicate pressure is a step function of <em>which</em> registries are
115
+ tapped, not a smooth function of how many &mdash; it appears the moment one duplicate-heavy registry
116
+ is added and then stays flat at ~4-5 slots per query from 12 taps to 77. Any dedup benefit should be
117
+ reported against a stated tap set for that reason, and a user who taps only curated skill registries
118
+ may never see the problem at all.
119
+
120
+ <b>What this does not settle.</b> The measurement counts repeated <em>names</em>, which is the
121
+ symptom the card names, not content-identical bodies &mdash; two rules that share a name and
122
+ genuinely differ (the <code>admin-interface-rule</code> pair fixed in <code>#366</code>) are counted
123
+ here as duplicate slots but must <em>not</em> be merged. That is precisely why the card asks for
124
+ clustering on content hash rather than on name, and this curve is an upper bound on what dedup could
125
+ reclaim, not a target.
126
+
127
+ <b>Shipped: content-hash dedup, measured 4.94 &rarr; 0.60 duplicate slots per query</b> over the
128
+ 77-tap corpus (worst case 9 of 10 &rarr; 4). The residual 0.60 is correct rather than leftover: those
129
+ are entries sharing a <em>name</em> whose bodies genuinely differ, which must stay separate.
130
+
131
+ Two measurements settled the design the card left open (&ldquo;content hash <em>or</em> canonical
132
+ name&rdquo;). Of 29,938 entries, <b>78.3% are byte-identical duplicates</b> &mdash; 14,153 distinct
133
+ bodies, largest cluster <b>40 copies</b> &mdash; while 82.7% share a name. That ~4.4% gap is real
134
+ distinct content, so name clustering would merge exactly the pairs <code>#366</code> proved must not
135
+ be merged. And clustering on content cannot make the opposite mistake here: the number of content
136
+ clusters spanning more than one name is <b>0</b>, so collapsing by body never merges two
137
+ differently-named items. Content hashing is strictly the safer of the two options the card offered,
138
+ which was not obvious before measuring.
139
+
140
+ The quality prior is <code>curated</code>: inside a cluster every copy is byte-identical, so the one
141
+ worth surfacing is the one from a tap the user marked trusted. The cluster keeps its best score when
142
+ that swap happens, so promoting the trusted copy never demotes the result.
143
+
144
+ Mechanically, the body hash is computed in <code>_make_docs</code> where the body is already read
145
+ &mdash; doing it at query time would mean re-reading ~30k files to answer one search &mdash; and
146
+ persisted per document (<code>INDEX_VERSION</code> 4 &rarr; 5). Dense hits carry no hash of their own
147
+ and do not need a second schema: <code>content_hashes()</code> serves one map to every engine from
148
+ the BM25 index, so the engines can never disagree about which copies are identical. Dedup runs inside
149
+ <code>retrieve</code> <em>and</em> at the <code>retrieve_any</code> seam, because fusion can
150
+ reintroduce a copy BM25 already dropped &mdash; the copies are distinct <code>(tap, path)</code>
151
+ keys, so RRF has no reason to treat them as one. Collapsing happens before <code>k</code> is applied,
152
+ or the duplicates would still consume the slots they were removed from.
153
+
154
+ Cost on the pinned eval corpus: <b>none</b> &mdash; 0.978 / 0.791 / 0.854 / 0.882, identical to
155
+ before, all four floors passing. That corpus has little content duplication, which is exactly why the
156
+ 83-tap measurement had to exist.
157
+
158
+ <b>Still open:</b> a richer quality prior than <code>curated</code> (the card names stars, recency,
159
+ maintenance; only <code>curated</code> is available on an entry today), and
160
+ <code>core/typosquat.py</code>'s confusion machinery for near-identical rather than byte-identical
161
+ bodies. Both are refinements &mdash; the byte-identical case is 78.3% of the problem.
@@ -3440,7 +3440,75 @@ at one path &mdash; and were corrected.
3440
3440
  <b>Still open:</b> the ranking half. Clustering on content hash, picking a winner with a quality
3441
3441
  prior, and the 83-tap re-measurement of the fusion bound are all untouched. Note the two are
3442
3442
  independent: dedup <em>merges</em> copies that are genuinely the same, whereas this fix stops copies
3443
- that were never the same from being merged by accident.</p>
3443
+ that were never the same from being merged by accident.
3444
+
3445
+ <b>The curve this card asked for.</b> The note above measured 0 duplicate slots over the pinned
3446
+ 6-tap corpus and said, correctly, that absence at 6 taps says nothing about 83. Re-measured over 77
3447
+ tapped registries sampled across the shipped list, with the same 50 natural-language queries,
3448
+ counting how many of the ten slots a user sees are second-or-later copies of a name already in that
3449
+ same result list:
3450
+
3451
+ <code>6 taps</code>: 1,122 entries, 0.0% sharing a name, <b>0.00</b> duplicate slots/query &middot;
3452
+ <code>12</code>: 14,194 entries, 77.8%, <b>4.28</b> &middot;
3453
+ <code>25</code>: 16,633, 77.9%, 4.12 &middot;
3454
+ <code>40</code>: 17,470, 74.7%, 3.88 &middot;
3455
+ <code>60</code>: 29,137, 83.9%, 5.08 &middot;
3456
+ <code>77</code>: 29,938 entries, 82.7% sharing a name, <b>4.94</b> duplicate slots/query.
3457
+
3458
+ So at realistic corpus size <b>roughly half the ten slots a user sees are repeat copies of a name
3459
+ already in the list</b>, worst observed 9 of 10 (&ldquo;our full-text queries have got slow as the
3460
+ index grew&rdquo;). The card's thesis holds, and more sharply than its own 56.3% figure suggested.
3461
+
3462
+ <b>But the driver is not tap count.</b> Entries jump 1,122 &rarr; 14,194 between 6 and 12 taps, so
3463
+ the sample is not uniform: a single awesome-list-style rule registry contributes more entries than
3464
+ the entire 6-tap eval corpus. Duplicate pressure is a step function of <em>which</em> registries are
3465
+ tapped, not a smooth function of how many &mdash; it appears the moment one duplicate-heavy registry
3466
+ is added and then stays flat at ~4-5 slots per query from 12 taps to 77. Any dedup benefit should be
3467
+ reported against a stated tap set for that reason, and a user who taps only curated skill registries
3468
+ may never see the problem at all.
3469
+
3470
+ <b>What this does not settle.</b> The measurement counts repeated <em>names</em>, which is the
3471
+ symptom the card names, not content-identical bodies &mdash; two rules that share a name and
3472
+ genuinely differ (the <code>admin-interface-rule</code> pair fixed in <code>#366</code>) are counted
3473
+ here as duplicate slots but must <em>not</em> be merged. That is precisely why the card asks for
3474
+ clustering on content hash rather than on name, and this curve is an upper bound on what dedup could
3475
+ reclaim, not a target.
3476
+
3477
+ <b>Shipped: content-hash dedup, measured 4.94 &rarr; 0.60 duplicate slots per query</b> over the
3478
+ 77-tap corpus (worst case 9 of 10 &rarr; 4). The residual 0.60 is correct rather than leftover: those
3479
+ are entries sharing a <em>name</em> whose bodies genuinely differ, which must stay separate.
3480
+
3481
+ Two measurements settled the design the card left open (&ldquo;content hash <em>or</em> canonical
3482
+ name&rdquo;). Of 29,938 entries, <b>78.3% are byte-identical duplicates</b> &mdash; 14,153 distinct
3483
+ bodies, largest cluster <b>40 copies</b> &mdash; while 82.7% share a name. That ~4.4% gap is real
3484
+ distinct content, so name clustering would merge exactly the pairs <code>#366</code> proved must not
3485
+ be merged. And clustering on content cannot make the opposite mistake here: the number of content
3486
+ clusters spanning more than one name is <b>0</b>, so collapsing by body never merges two
3487
+ differently-named items. Content hashing is strictly the safer of the two options the card offered,
3488
+ which was not obvious before measuring.
3489
+
3490
+ The quality prior is <code>curated</code>: inside a cluster every copy is byte-identical, so the one
3491
+ worth surfacing is the one from a tap the user marked trusted. The cluster keeps its best score when
3492
+ that swap happens, so promoting the trusted copy never demotes the result.
3493
+
3494
+ Mechanically, the body hash is computed in <code>_make_docs</code> where the body is already read
3495
+ &mdash; doing it at query time would mean re-reading ~30k files to answer one search &mdash; and
3496
+ persisted per document (<code>INDEX_VERSION</code> 4 &rarr; 5). Dense hits carry no hash of their own
3497
+ and do not need a second schema: <code>content_hashes()</code> serves one map to every engine from
3498
+ the BM25 index, so the engines can never disagree about which copies are identical. Dedup runs inside
3499
+ <code>retrieve</code> <em>and</em> at the <code>retrieve_any</code> seam, because fusion can
3500
+ reintroduce a copy BM25 already dropped &mdash; the copies are distinct <code>(tap, path)</code>
3501
+ keys, so RRF has no reason to treat them as one. Collapsing happens before <code>k</code> is applied,
3502
+ or the duplicates would still consume the slots they were removed from.
3503
+
3504
+ Cost on the pinned eval corpus: <b>none</b> &mdash; 0.978 / 0.791 / 0.854 / 0.882, identical to
3505
+ before, all four floors passing. That corpus has little content duplication, which is exactly why the
3506
+ 83-tap measurement had to exist.
3507
+
3508
+ <b>Still open:</b> a richer quality prior than <code>curated</code> (the card names stars, recency,
3509
+ maintenance; only <code>curated</code> is available on an entry today), and
3510
+ <code>core/typosquat.py</code>'s confusion machinery for near-identical rather than byte-identical
3511
+ bodies. Both are refinements &mdash; the byte-identical case is 78.3% of the problem.</p>
3444
3512
  <div class="meta">
3445
3513
  <span class="m">Complexity <b>M</b></span>
3446
3514
  <span class="m hi">Impact <b>High</b></span>
@@ -0,0 +1,144 @@
1
+ """Collapsing byte-identical copies before they reach the ten slots a user sees.
2
+
3
+ Measured over 77 tapped registries (29,938 entries): **78.3% are byte-identical
4
+ duplicates**, largest cluster 40 copies, and the natural-language query set
5
+ averages **4.94 of 10 result slots** consumed by a repeat of a name already in
6
+ the list — worst case 9 of 10.
7
+
8
+ Two measurements decide the design:
9
+
10
+ * **Cluster on content, never on name.** 82.7% of entries share a name but only
11
+ 78.3% share a body. That ~4.4% gap is real distinct content — the
12
+ `admin-interface-rule` pair fixed in #366 is two different rules under one
13
+ name — and name-based dedup would merge exactly those.
14
+ * **Content clustering cannot over-merge here.** Of 14,153 distinct bodies, the
15
+ number of clusters spanning more than one name is **0**. So collapsing by body
16
+ never merges two differently-named items; it is strictly safer than the
17
+ name-based alternative the card also offered.
18
+
19
+ The quality prior is `curated`: within a cluster every copy is byte-identical,
20
+ so the one worth surfacing is the one from a tap the user marked trusted.
21
+ """
22
+ from __future__ import annotations
23
+
24
+ from boost_cli.core import rag
25
+
26
+
27
+ def _hit(name, tap, score, content, curated=False, skill_md=None):
28
+ return {"entry": {"name": name, "tap": tap, "curated": curated,
29
+ "skill_md": skill_md or ("%s/SKILL.md" % name),
30
+ "kind": "skill", "description": ""},
31
+ "score": score, "snippet": "", "content": content}
32
+
33
+
34
+ class TestCollapsesIdenticalBodies:
35
+ def test_two_byte_identical_copies_become_one(self):
36
+ hits = [_hit("rule", "a/x", 3.0, "same"), _hit("rule", "b/y", 2.0, "same")]
37
+ out = rag.dedupe_by_content(hits, limit=10)
38
+ assert len(out) == 1
39
+
40
+ def test_the_best_ranked_copy_is_the_one_kept(self):
41
+ hits = [_hit("rule", "a/x", 3.0, "same"), _hit("rule", "b/y", 2.0, "same")]
42
+ assert rag.dedupe_by_content(hits, limit=10)[0]["entry"]["tap"] == "a/x"
43
+
44
+ def test_a_cluster_of_forty_becomes_one(self):
45
+ # The largest real cluster measured over 77 taps.
46
+ hits = [_hit("rule", "t%d/r" % i, 40.0 - i, "same") for i in range(40)]
47
+ assert len(rag.dedupe_by_content(hits, limit=10)) == 1
48
+
49
+ def test_order_of_surviving_hits_is_preserved(self):
50
+ hits = [_hit("a", "t/1", 5.0, "A"), _hit("b", "t/2", 4.0, "B"),
51
+ _hit("a2", "t/3", 3.0, "A")] # dupe of the first
52
+ out = rag.dedupe_by_content(hits, limit=10)
53
+ assert [h["entry"]["name"] for h in out] == ["a", "b"]
54
+
55
+
56
+ class TestNeverMergesDistinctContent:
57
+ """The failure that would matter: two different rules collapsed into one."""
58
+
59
+ def test_same_name_different_bodies_stay_two_results(self):
60
+ # The #366 pair: one tap, one name, two genuinely different rules.
61
+ hits = [_hit("admin-interface-rule", "z/rules", 3.0, "express mongodb",
62
+ skill_md="a/express-mongodb/admin-interface-rule.mdc"),
63
+ _hit("admin-interface-rule", "z/rules", 2.0, "mern fullstack",
64
+ skill_md="a/fullstack-mern/admin-interface-rule.mdc")]
65
+ out = rag.dedupe_by_content(hits, limit=10)
66
+ assert len(out) == 2, "distinct bodies were merged on a shared name"
67
+
68
+ def test_missing_content_is_never_treated_as_a_match(self):
69
+ # Two hits with no recorded hash must not collapse into each other just
70
+ # because both are unknown — that would silently hide real results.
71
+ hits = [_hit("a", "t/1", 3.0, None), _hit("b", "t/2", 2.0, None)]
72
+ assert len(rag.dedupe_by_content(hits, limit=10)) == 2
73
+
74
+
75
+ class TestQualityPrior:
76
+ """Every copy in a cluster is byte-identical, so surface the trusted one."""
77
+
78
+ def test_a_curated_copy_wins_over_a_better_ranked_uncurated_one(self):
79
+ hits = [_hit("rule", "rando/x", 3.0, "same"),
80
+ _hit("rule", "trusted/y", 2.0, "same", curated=True)]
81
+ kept = rag.dedupe_by_content(hits, limit=10)[0]["entry"]
82
+ assert kept["tap"] == "trusted/y"
83
+
84
+ def test_the_kept_hit_keeps_the_best_score_in_its_cluster(self):
85
+ # Swapping which copy is shown must not demote the cluster's ranking.
86
+ hits = [_hit("rule", "rando/x", 3.0, "same"),
87
+ _hit("rule", "trusted/y", 2.0, "same", curated=True)]
88
+ assert rag.dedupe_by_content(hits, limit=10)[0]["score"] == 3.0
89
+
90
+ def test_two_curated_copies_fall_back_to_rank(self):
91
+ hits = [_hit("rule", "a/x", 3.0, "same", curated=True),
92
+ _hit("rule", "b/y", 2.0, "same", curated=True)]
93
+ assert rag.dedupe_by_content(hits, limit=10)[0]["entry"]["tap"] == "a/x"
94
+
95
+
96
+ class TestLimit:
97
+ def test_limit_is_applied_after_collapsing_not_before(self):
98
+ # The whole point: duplicates must not consume the slots. Ten copies of
99
+ # one body followed by three distinct items should yield four results,
100
+ # not one.
101
+ hits = ([_hit("dup", "t%d/r" % i, 20.0 - i, "same") for i in range(10)]
102
+ + [_hit("x", "t/a", 5.0, "X"), _hit("y", "t/b", 4.0, "Y"),
103
+ _hit("z", "t/c", 3.0, "Z")])
104
+ out = rag.dedupe_by_content(hits, limit=4)
105
+ assert [h["entry"]["name"] for h in out] == ["dup", "x", "y", "z"]
106
+
107
+ def test_empty_input_is_empty_output(self):
108
+ assert rag.dedupe_by_content([], limit=10) == []
109
+
110
+
111
+ class TestHashesReachEveryEngine:
112
+ """Dense hits carry no hash of their own; one map serves all engines."""
113
+
114
+ def test_hashes_are_read_from_the_bm25_index(self, sandbox):
115
+ rag._save([{"n": "a", "t": "x/y", "f": "a/SKILL.md", "h": "deadbeef",
116
+ "k": "skill", "c": 0, "l": 1, "snip": "s",
117
+ "tf": {"z": 1}}], {})
118
+ rag._CACHE.clear()
119
+ assert rag.content_hashes() == {("x/y", "a/SKILL.md"): "deadbeef"}
120
+
121
+ def test_a_dense_hit_gets_its_hash_attached(self, sandbox):
122
+ rag._save([{"n": "a", "t": "x/y", "f": "a/SKILL.md", "h": "deadbeef",
123
+ "k": "skill", "c": 0, "l": 1, "snip": "s",
124
+ "tf": {"z": 1}}], {})
125
+ rag._CACHE.clear()
126
+ bare = [{"entry": {"name": "a", "tap": "x/y",
127
+ "skill_md": "a/SKILL.md"},
128
+ "score": 1.0, "snippet": ""}]
129
+ assert rag._with_content(bare)[0]["content"] == "deadbeef"
130
+
131
+ def test_an_existing_hash_is_not_overwritten(self, sandbox):
132
+ hits = [{"entry": {"name": "a", "tap": "x/y", "skill_md": "a/SKILL.md"},
133
+ "score": 1.0, "snippet": "", "content": "mine"}]
134
+ assert rag._with_content(hits)[0]["content"] == "mine"
135
+
136
+ def test_no_index_degrades_to_leaving_hits_alone(self, sandbox):
137
+ # No BM25 index yet: dedupe must no-op rather than collapse everything
138
+ # into one result on a shared empty hash.
139
+ bare = [{"entry": {"name": "a", "tap": "x/y", "skill_md": "a.md"},
140
+ "score": 2.0, "snippet": ""},
141
+ {"entry": {"name": "b", "tap": "x/y", "skill_md": "b.md"},
142
+ "score": 1.0, "snippet": ""}]
143
+ out = rag.dedupe_by_content(rag._with_content(bare), limit=10)
144
+ assert len(out) == 2
@@ -741,9 +741,11 @@ class TestSavePayload:
741
741
  assert sorted(raw["postings"]["foo"]) == [[0, 1], [1, 2]]
742
742
  assert raw["postings"]["bar"] == [[0, 1]]
743
743
  assert raw["docs"][0] == {"n": "a", "t": "x/y", "f": "a/SKILL.md",
744
- "k": "skill", "c": 0, "l": 2, "snip": "sa"}
744
+ "h": "", "k": "skill", "c": 0, "l": 2,
745
+ "snip": "sa"}
745
746
  assert raw["docs"][1] == {"n": "b", "t": "x/y", "f": "b.mdc",
746
- "k": "rule", "c": 3, "l": 4, "snip": "sb"}
747
+ "h": "", "k": "rule", "c": 3, "l": 4,
748
+ "snip": "sb"}
747
749
 
748
750
  def test_empty_docs_avg_len_zero(self, sandbox):
749
751
  rag._save([], {})