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.
- {boost_skill_cli-1.0.321/boost_skill_cli.egg-info → boost_skill_cli-1.0.323}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/rag.py +109 -12
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323/boost_skill_cli.egg-info}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/SOURCES.txt +1 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap.html +69 -1
- boost_skill_cli-1.0.323/tests/unit/test_content_dedupe.py +144 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_rag.py +4 -2
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.gitattributes +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.gitignore +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.gitleaks.toml +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.htmlvalidate.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.lighthouserc.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.lycheeignore +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.markdownlint-cli2.jsonc +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.pre-commit-config.yaml +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.stylelintrc.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.vale/styles/boost/Terminology.yml +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.vale/styles/config/vocabularies/boost/accept.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/.vale.ini +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/CODE_OF_CONDUCT.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/LICENSE +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/Makefile +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/README.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/_common.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/pkg.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/quality.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/run.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/safety.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/taps.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/adapters.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/capabilities.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/ed25519.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/faithfulness.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/imperative.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/injectscan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/installscan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/integrity.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/localembed.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/mcpdecl.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/mcphost.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/minisign.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/nethttp.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/projectlock.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/provenance.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/resolve.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/rules.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/scopes.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/secretscan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/selfupdate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/serve.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/stackprobe.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/staleness.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/trustaudit.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/typosquat.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/updatediff.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/core/workflows.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/codecov.yml +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/adapters.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/README.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-components-core.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-containers.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-context.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/architecture/c4-dynamic-install.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/distill.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/doctor.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/explain.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/hooks.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/install.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/pulse.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/search.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/gifs/tap.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/distill.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/doctor.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/explain.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/hooks.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/install.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/pulse.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/search.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel/tapes/tap.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/carousel.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/commands.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/eval.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/index.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/actionlint-skips-shellcheck-silently.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/adapter-conformance-langgraph-leg-never-ran.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ai-bridge-silent-failure-logging.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ambiguous-tap-short-name-resolution.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/bm25-index-is-one-json-blob.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/boost-run-live-agents.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ci-job-timeout-minutes.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ci-summary-step-fails-itself.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ci-time-is-now-runner-queueing.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/code-scanning-rule-can-be-restored.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/codeql-job-rename-stranded-merge-protection.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/command-carousel-page.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {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
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/crash-listing-branch-untested.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/demo-cannot-open-its-own-pr.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/demo-gif-workflow-has-never-succeeded.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependabot-missing-pip-ecosystem.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependabot-regeneration-drops-platform-pins.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependabot-root-pip-entry-duplicates-requirements.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/docsite-chrome-and-content-audit.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/doctor-checks-rules-and-workflows.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/eval-corpus-cannot-see-real-retrieval.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/framework-adapter-langgraph.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/framework-adapter-multi-agent.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/framework-adapters-boost-adapt.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/gemini-cli-agent-target.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/github-community-health-files.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/golden-set-statistical-power.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-docs-never-mentioned-upgrading.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-resolves-skill-dependencies.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/install-scope-user-or-project.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/installed-skill-trust-audit.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/internal-architecture-diagrams.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/journal-rotation-race-and-handle-leak.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/keyless-dense-tier-local-static-embeddings.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/keyless-semantic-search-for-everyone.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/license-compliance-scanning.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lint-tap-misscopes-rule-workflow-entries.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/list-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lock-invariant-cannot-parse-extras-pins.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/log-timestamps-mislabeled-utc.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/main-has-no-branch-protection.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/make-lint-masks-actionlint-failures.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/markdownlint-lints-the-fuzz-corpus.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-aware-skills.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-hub-diagram-node-text-overflow.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-install-skips-the-injection-scan.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-launch-objc-fork-safety.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-one-benefit-nameable-task.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-register-names-server-before-env.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-search-before-reinventing.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-servers-ignore-install-scope.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mcp-startup-self-harden-fork-safety.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/merged-loop-branches-are-never-pruned.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mutation-shard-floor-is-one-file.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/mypy-strict-mode-for-commands.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/negative-limit-inverts-log-pulse-output.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/onboard-overwrites-generated-files-without-confirm.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/one-commit-can-cut-two-releases.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/path-traversal-unsanitized-rule-workflow-name.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/pin-the-lint-toolchain.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prerequisites-and-semantic-search-setup.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/project-scope-across-every-command.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/project-scope-symlink-escape-on-write.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {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
- {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
- {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
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/ranx-significance-monitor.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/release-verifies-the-wrong-commit.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/required-checks-config-drift.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/retrieval-quality-eval-harness.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/rule-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/runtime-explain-faithfulness-guardrail.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-declares-the-wrong-version.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sbom-release-event-never-fires.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/scan-and-sync-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/scheduled-toolchain-lock-regeneration.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/scorecard-findings-triage.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {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
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/serve-py-traversal-guards-untested.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {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
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/structured-json-log-output-mode.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/sync-deletes-unowned-broken-symlinks.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/uninstall-has-no-confirmation-prompt.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/update-refreshes-rules-and-workflows.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/update-reinstall-widens-agent-scope.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {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
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/wire-bdd-suite-into-ci.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workflow-install-native-materialization.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/workspace-scope-install.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/docs/roadmap/items/zizmor-pin-is-a-yanked-release.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/eslint.config.mjs +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/README.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/__init__.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/baseline.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/faithfulness.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/golden_set.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/make_corpus.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/make_golden.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/metrics.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/evals/run_evals.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/examples/README.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/examples/adapt-demo.sh +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/examples/boost-run-prototype.sh +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/index.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/noxfile.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/coverage-tools.in +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/coverage-tools.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/lint-tools.in +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/lint-tools.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/mutation-tools.in +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/mutation-tools.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/platform-pins.lock +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/release-tools.in +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/release-tools.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/test-tools.in +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/requirements/test-tools.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/a11y_check.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/bench_cli.sh +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/build_command_reference.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/check_anchors.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/check_licenses.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/check_required_checks.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/ensure_eval_corpus.sh +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_explain.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_gate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_recommend.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_retrieval.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/import_budget.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/lock_toolchain.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/mutation_shards.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/mutation_weights.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/perf_gate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/post_deploy_smoke.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/release_guard.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/scripts/release_preflight.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/setup.cfg +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/sonar-project.properties +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/README.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/boost.css +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/boost.js +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/style/demo.html +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/adapt.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/browse.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/count.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/discover.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/doctor.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/environment.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/explain.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/index.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/install.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/list.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/mcp.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/search.feature +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/cli_steps.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/discovery_steps.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/mcp_steps.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/bdd/features/steps/quality_steps.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/baseline.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/explain.jsonl +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/golden-natural.jsonl +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/golden.jsonl +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/recommend.jsonl +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/eval/taps.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_capabilities_policy.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_adapt.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_integrity_enforce.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_property_parsers.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_run.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_tap_signing.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/functional/test_workspace_scope.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/README.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/01-typical.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/02-lossy-version.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/03-lossy-numbers.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/04-lists.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/05-nested.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/06-empty-block.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/07-unterminated.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/08-none.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/09-unbalanced-quote.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/10-keywords.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/11-crlf.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/frontmatter/12-bom.md +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/01-shorthand.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/02-https.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/03-ssh.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/04-traversal.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/05-empty.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/06-space.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/07-slash.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/corpus/registry/08-bare-host.txt +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/fuzz_frontmatter.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/fuzz/fuzz_registry.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/perf/test_benchmarks.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_a11y_check.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_adapters.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_adapters_multi.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_adapters_runner.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_capabilities.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_check_anchors.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_check_licenses.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_codeql_analysis_key.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_command_reference_fresh.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_community_health_files.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense_fix_hint.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dense_status.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_dependabot_config.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_docs_pages_linked.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_docsite_chrome.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_ed25519.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_embed_local.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_entry_key.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_baseline.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_faithfulness.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_gate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_metrics.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_eval_stats_summary.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_faithfulness.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_fuzz_targets.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_gitleaks_config.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_imperative.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_import_budget.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_injectscan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_installscan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_integrity.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_localembed.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_marketing_counts.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcp_install_scan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcpdecl.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mcphost.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_minisign.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutation_shards.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutation_subfile_shards.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_mutmut_source_paths.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_nethttp.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_perf_gate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_pkg_confusions.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_pkg_injection.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_pkg_secrets.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_platform_pins.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_post_deploy_smoke.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_project_mcp_sidecar.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_projectlock.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_provenance.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_rag_fusion.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_registry_categories.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_release_guard.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_release_preflight.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_required_checks.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_resolve.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_rules.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_scopes.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_secretscan.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_selfupdate.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_serve.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_stackprobe.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_staleness.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_toolchain_lock.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_trustaudit.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_typosquat.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_updatediff.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_version.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_workflow_concurrency.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_workflow_timeouts.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/unit/test_workflows.py +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/a11y_check.mjs +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/console_check.mjs +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/package-lock.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/package.json +0 -0
- {boost_skill_cli-1.0.321 → boost_skill_cli-1.0.323}/tests/visual/visual_check.mjs +0 -0
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
|
@@ -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/
|
|
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 ·
|
|
102
|
+
<code>12</code>: 14,194 entries, 77.8%, <b>4.28</b> ·
|
|
103
|
+
<code>25</code>: 16,633, 77.9%, 4.12 ·
|
|
104
|
+
<code>40</code>: 17,470, 74.7%, 3.88 ·
|
|
105
|
+
<code>60</code>: 29,137, 83.9%, 5.08 ·
|
|
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 (“our full-text queries have got slow as the
|
|
110
|
+
index grew”). 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 → 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 — 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 — 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 → 0.60 duplicate slots per query</b> over the
|
|
128
|
+
77-tap corpus (worst case 9 of 10 → 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 (“content hash <em>or</em> canonical
|
|
132
|
+
name”). Of 29,938 entries, <b>78.3% are byte-identical duplicates</b> — 14,153 distinct
|
|
133
|
+
bodies, largest cluster <b>40 copies</b> — 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
|
+
— doing it at query time would mean re-reading ~30k files to answer one search — and
|
|
146
|
+
persisted per document (<code>INDEX_VERSION</code> 4 → 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 — 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> — 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 — the byte-identical case is 78.3% of the problem.
|
|
@@ -3440,7 +3440,75 @@ at one path — 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
|
|
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 ·
|
|
3452
|
+
<code>12</code>: 14,194 entries, 77.8%, <b>4.28</b> ·
|
|
3453
|
+
<code>25</code>: 16,633, 77.9%, 4.12 ·
|
|
3454
|
+
<code>40</code>: 17,470, 74.7%, 3.88 ·
|
|
3455
|
+
<code>60</code>: 29,137, 83.9%, 5.08 ·
|
|
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 (“our full-text queries have got slow as the
|
|
3460
|
+
index grew”). 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 → 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 — 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 — 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 → 0.60 duplicate slots per query</b> over the
|
|
3478
|
+
77-tap corpus (worst case 9 of 10 → 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 (“content hash <em>or</em> canonical
|
|
3482
|
+
name”). Of 29,938 entries, <b>78.3% are byte-identical duplicates</b> — 14,153 distinct
|
|
3483
|
+
bodies, largest cluster <b>40 copies</b> — 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
|
+
— doing it at query time would mean re-reading ~30k files to answer one search — and
|
|
3496
|
+
persisted per document (<code>INDEX_VERSION</code> 4 → 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 — 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> — 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 — 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,
|
|
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,
|
|
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([], {})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|