boost-skill-cli 1.0.82__tar.gz → 1.0.84__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.82 → boost_skill_cli-1.0.84}/PKG-INFO +1 -1
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/_version.py +2 -2
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/pkg.py +6 -6
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/quality.py +15 -13
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/taps.py +15 -10
- boost_skill_cli-1.0.84/boost_cli/core/staleness.py +78 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_skill_cli.egg-info/PKG-INFO +1 -1
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_skill_cli.egg-info/SOURCES.txt +4 -0
- boost_skill_cli-1.0.84/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +16 -0
- boost_skill_cli-1.0.84/docs/roadmap/items/log-timestamps-mislabeled-utc.md +25 -0
- boost_skill_cli-1.0.84/docs/roadmap/items/mcp-launch-objc-fork-safety.md +26 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap.html +57 -13
- boost_skill_cli-1.0.84/tests/unit/test_staleness.py +102 -0
- boost_skill_cli-1.0.82/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md +0 -16
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/.gitattributes +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/.gitignore +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/CLAUDE.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/CONTRIBUTING.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/LICENSE +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/MANIFEST.in +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/Makefile +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/README.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/SECURITY.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/__init__.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/__main__.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/cli.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/cliparse.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/__init__.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/bmad.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/configuration.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/discovery.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/hooks.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/info.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/intelligence.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/commands/team.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/__init__.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/agents.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/ai.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/catalog.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/claude_settings.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/config.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/dense.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/embed.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/frontmatter.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/gitutil.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/journal.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/lockfile.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/logs.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/mcp.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/output.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/paths.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/policy.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/rag.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/registry.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/store.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/core/util.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/data/registries.json +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/errors.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_cli/spin.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_skill_cli.egg-info/dependency_links.txt +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_skill_cli.egg-info/entry_points.txt +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_skill_cli.egg-info/requires.txt +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/boost_skill_cli.egg-info/top_level.txt +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/.claude/settings.local.json +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/DEBUGGING.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/demo.gif +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/demo.tape +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/design-roadmap.html +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/index.html +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/mcp-hub.html +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/rag-architecture.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D01.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D02.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D03.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D04.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D05.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D06.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D07.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D08.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D09.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D10.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D11.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D12.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D13.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D14.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D15.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D16.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D17.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D18.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D19.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D20.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D21.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D22.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D23.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D24.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D25.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/BOOST-D26.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/accessibility-audit-pa11y-ci-axe-core.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/atomic-corruption-safe-lock-file-writes.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/atomic-skill-install-temp-dir-swap.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/autonomous-ship-workflow-and-isolated-worktree.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/bring-commands-under-mutation-testing.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/broken-link-and-anchor-checking-lychee.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/build-provenance-slsa-attestations.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/cache-the-catalog-entry-set-across-rag-queries.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/capability-manifest-and-least-privilege-policy.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/clean-env-install-smoke-pip-and-pipx.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/command-reference-docs-site.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/complexity-and-dead-code-radar-xenon-vulture.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/coverage-dashboard-codecov-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/coverage-guided-fuzzing-atheris-oss-fuzz.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/crash-recorder-error-paths-core-logs-py.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/dependency-cve-gate-pip-audit.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/docstring-coverage-interrogate.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/end-of-options-guard-on-git-commands.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/extension-free-tests-core-dense-py.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/extract-mcp-http-servers-out-of-configuration-py.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/finish-mutation-hardening-across-core.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/fix-self-update-version-detection-dead-branch.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/fork-safe-network-proxy-handler.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/frontmatter-scalar-over-coercion.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/github-pages-deploy-is-broken-every-push.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/hash-pinned-reproducible-toolchain-uv-lock.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/html-validation-html-validate.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/integrity-verification-boost-verify.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/invocation-pid-logging-for-crash-correlation.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/layering-guard-import-linter.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/lighthouse-ci-on-the-pages-site.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/localize-the-stored-bm25-snippet.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/make-the-roadmaps-discoverable.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/markdown-consistency-markdownlint-cli2.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/memoize-config-load-in-process.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/modernization-smells-refurb-pyupgrade.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/mutation-hardening-core-frontmatter-py.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/mutation-hardening-core-gitutil-py.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/mutation-hardening-core-store-py.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/one-command-every-env-nox.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/one-shared-atomic-write-helper.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/package-metadata-validation-twine-check-friends.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/patch-coverage-gate-diff-cover.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/performance-regression-gate-pytest-benchmark.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/post-deploy-smoke-headless-load-check.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/pre-release-python-canary-3-14t-free-threaded.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/promote-nav-footer-into-the-shared-style-system.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/prompt-injection-scanning-of-skill-markdown.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/property-based-tests-hypothesis-on-the-parsers.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/prose-and-terminology-linting-vale.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/prune-ignored-dirs-during-scan-dir-walk.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/quality-dashboard-sonarcloud-free-for-oss.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/reconcile-the-theme-drift.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/refresh-the-marketing-surface.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/reuse-helpers-kill-minor-dead-work.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/robust-tag-argument-parsing.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/runner-egress-monitoring-stepsecurity-harden-run.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/sbom-aware-scanning-osv-scanner.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/sbom-on-every-release-cyclonedx-syft.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/second-type-checker-pyright.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/secret-and-pii-scanning-of-installed-skills.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/secret-scanning-gitleaks-push-protection.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/security-linting-bandit-via-ruff-s-rules.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/shift-left-gate-pre-commit-pre-commit-ci.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/single-imperative-rule-extractor.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/single-tech-stack-prober.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/split-oversized-command-modules.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/startup-and-import-time-budget-x-importtime.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/stop-re-serializing-entry-meta-on-every-search.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/supply-chain-posture-openssf-scorecard.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/tap-signing-and-provenance-sigstore-minisign.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/theme-asset-linting-stylelint-eslint.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/typo-detection-codespell.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/typosquat-and-name-confusion-detection.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/unify-tilde-two-copies-have-a-boundary-bug.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/untrack-generated-build-noise.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/update-diff-before-apply.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/visual-regression-pass-on-the-guide.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/widen-the-ruff-rule-surface-b-sim-c4-perf-ruf.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/windows-in-the-ci-matrix.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/workflow-linting-actionlint.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/docs/roadmap/items/workflow-sast-zizmor.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/index.html +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/pyproject.toml +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/scripts/build_registries.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/scripts/build_roadmap.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/scripts/mutation_gate.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/setup.cfg +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/style/README.md +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/style/boost.css +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/style/boost.js +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/style/demo.html +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/conftest.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_bmad.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_configuration.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_discovery.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_hooks.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_info.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_intelligence.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_pkg.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_quality.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_taps.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_cli_team.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/functional/test_everyday_loop.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/make_fixture.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/smoke.sh +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_agents.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_ai.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_catalog.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_claude_settings.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_cliparse.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_config.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_dense.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_dense_fallback.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_embed.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_errors_and_cli_table.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_frontmatter.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_gitutil.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_journal.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_lockfile.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_logs.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_mcp.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_mutation_hardening.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_output.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_paths.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_policy.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_rag.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_registries_fresh.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_registry.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_roadmap_fresh.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_spin.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_store.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_token_parity.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_util.py +0 -0
- {boost_skill_cli-1.0.82 → boost_skill_cli-1.0.84}/tests/unit/test_version.py +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.84'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 84)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -16,7 +16,7 @@ from typing import Dict, List, Optional
|
|
|
16
16
|
|
|
17
17
|
from .. import cliparse
|
|
18
18
|
from ..core import (agents, catalog, gitutil, journal, lockfile, paths,
|
|
19
|
-
registry, store, util)
|
|
19
|
+
registry, staleness, store, util)
|
|
20
20
|
from ..core import output as out
|
|
21
21
|
from ..errors import BoostError
|
|
22
22
|
|
|
@@ -287,16 +287,16 @@ def cmd_update(argv: List[str]) -> int:
|
|
|
287
287
|
entry = matches[0]
|
|
288
288
|
old_v = str(lk.get("version", "0.0.0"))
|
|
289
289
|
new_v = str(entry.get("version", "0.0.0"))
|
|
290
|
-
|
|
291
|
-
if
|
|
290
|
+
head, src_sha = "", None
|
|
291
|
+
if not util.semver_gt(new_v, old_v):
|
|
292
292
|
head = gitutil.head_commit(registry.get(tapname).path)
|
|
293
293
|
if head and head != lk.get("commit"):
|
|
294
294
|
try:
|
|
295
|
-
|
|
295
|
+
src_sha = util.sha256_dir(store.source_dir_for(entry))
|
|
296
296
|
except BoostError:
|
|
297
297
|
continue
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
reason = staleness.upstream_reason(old_v, new_v, lk.get("commit", ""),
|
|
299
|
+
head, lk.get("sha256", ""), src_sha)
|
|
300
300
|
if reason is None:
|
|
301
301
|
continue
|
|
302
302
|
try:
|
|
@@ -18,7 +18,7 @@ from typing import List, Optional, Tuple
|
|
|
18
18
|
from .. import cliparse
|
|
19
19
|
from ..core import (agents, ai, catalog, frontmatter, gitutil, journal,
|
|
20
20
|
lockfile, logs, output as out, paths, policy, registry,
|
|
21
|
-
store, util)
|
|
21
|
+
staleness, store, util)
|
|
22
22
|
from ..errors import BoostError
|
|
23
23
|
|
|
24
24
|
# --- audit: dangerous-content patterns ------------------------------------
|
|
@@ -119,18 +119,20 @@ def _drift_status(name: str, entry: dict) -> str:
|
|
|
119
119
|
"""'in-sync' | 'local-edits' | 'upstream-moved' | 'source-missing'
|
|
120
120
|
| 'store-missing' | 'n/a' (local imports with no tap source)."""
|
|
121
121
|
sdir = store.skill_store_dir(name)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
122
|
+
store_sha = util.sha256_dir(sdir) if sdir.is_dir() else None
|
|
123
|
+
is_local = entry.get("tap") == "local"
|
|
124
|
+
lock_sha = entry.get("sha256", "")
|
|
125
|
+
source_sha = None
|
|
126
|
+
if store_sha is not None and store_sha == lock_sha and not is_local:
|
|
127
|
+
try:
|
|
128
|
+
src = store.source_dir_for(
|
|
129
|
+
{"name": name, "tap": entry.get("tap", ""),
|
|
130
|
+
"rel_dir": entry.get("source_dir", ".")})
|
|
131
|
+
except BoostError:
|
|
132
|
+
source_sha = None
|
|
133
|
+
else:
|
|
134
|
+
source_sha = util.sha256_dir(src)
|
|
135
|
+
return staleness.drift_state(store_sha, lock_sha, is_local, source_sha)
|
|
134
136
|
|
|
135
137
|
|
|
136
138
|
_DRIFT_STYLE = {"in-sync": out.GREEN, "local-edits": out.YELLOW,
|
|
@@ -5,7 +5,7 @@ import argparse
|
|
|
5
5
|
import json
|
|
6
6
|
|
|
7
7
|
from .. import cliparse, spin
|
|
8
|
-
from ..core import catalog, config, gitutil, journal, lockfile, paths, registry, store, util
|
|
8
|
+
from ..core import catalog, config, gitutil, journal, lockfile, paths, registry, staleness, store, util
|
|
9
9
|
from ..core import output as out
|
|
10
10
|
from ..errors import BoostError
|
|
11
11
|
|
|
@@ -218,9 +218,8 @@ def cmd_outdated(argv) -> int:
|
|
|
218
218
|
latest = str(entry.get("version") or "0.0.0")
|
|
219
219
|
installed_v = str(lk.get("version") or "0.0.0")
|
|
220
220
|
stale, latest_disp = False, latest
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
else:
|
|
221
|
+
head, src_sha, src_missing = "", None, False
|
|
222
|
+
if not util.semver_gt(latest, installed_v):
|
|
224
223
|
if tap_name not in heads:
|
|
225
224
|
try:
|
|
226
225
|
tap = registry.get(tap_name)
|
|
@@ -231,13 +230,19 @@ def cmd_outdated(argv) -> int:
|
|
|
231
230
|
head = heads[tap_name]
|
|
232
231
|
if head and head != lk.get("commit"):
|
|
233
232
|
try:
|
|
234
|
-
|
|
233
|
+
src_sha = util.sha256_dir(store.source_dir_for(entry))
|
|
235
234
|
except BoostError:
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
235
|
+
src_missing = True
|
|
236
|
+
if src_missing:
|
|
237
|
+
stale, latest_disp = True, "source missing"
|
|
238
|
+
else:
|
|
239
|
+
reason = staleness.upstream_reason(
|
|
240
|
+
installed_v, latest, lk.get("commit", ""), head,
|
|
241
|
+
lk.get("sha256", ""), src_sha)
|
|
242
|
+
if reason == staleness.VERSION:
|
|
243
|
+
stale = True
|
|
244
|
+
elif reason == staleness.CONTENT:
|
|
245
|
+
stale, latest_disp = True, "%s (%s)" % (latest, head[:7])
|
|
241
246
|
if stale:
|
|
242
247
|
results.append({"name": name, "installed": installed_v,
|
|
243
248
|
"latest": latest_disp, "tap": tap_name,
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Single source of truth for "is an installed skill stale vs its tap?".
|
|
2
|
+
|
|
3
|
+
``cmd_update`` (pkg.py), ``cmd_outdated`` (taps.py) and ``_drift_status``
|
|
4
|
+
(quality.py) each re-derived this from the same three signals — the catalog
|
|
5
|
+
version, the tap's HEAD commit and the source tree's ``sha256`` — so the logic
|
|
6
|
+
lived in three places that would inevitably drift apart. These two pure
|
|
7
|
+
(I/O-free) functions are now the one decision the commands render.
|
|
8
|
+
|
|
9
|
+
Keeping them free of any filesystem/subprocess access is deliberate: the
|
|
10
|
+
commands gather the raw signals (``head_commit``, ``source_dir_for``,
|
|
11
|
+
``sha256_dir``) and pass them in, so the *decision* stays trivially testable.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
from . import util
|
|
18
|
+
|
|
19
|
+
# ── upstream_reason: why a tap's copy is newer than the installed one ──────
|
|
20
|
+
VERSION = "version" # the catalog advertises a higher semver
|
|
21
|
+
CONTENT = "content" # the tap HEAD moved AND the source tree's bytes changed
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def upstream_reason(installed_version: str, latest_version: str,
|
|
25
|
+
installed_commit: str, tap_head: str,
|
|
26
|
+
installed_sha: str, source_sha: Optional[str]
|
|
27
|
+
) -> Optional[str]:
|
|
28
|
+
"""Why the tap's copy is newer than the installed one, else ``None``.
|
|
29
|
+
|
|
30
|
+
Mirrors the historical order exactly: a higher advertised semver wins
|
|
31
|
+
immediately; otherwise a *moved* tap HEAD **plus** a changed source tree
|
|
32
|
+
counts as content drift. ``source_sha`` is ``None`` when the source tree
|
|
33
|
+
could not be hashed (missing checkout) — treated as "no content signal",
|
|
34
|
+
i.e. not stale on that axis.
|
|
35
|
+
"""
|
|
36
|
+
if util.semver_gt(latest_version, installed_version):
|
|
37
|
+
return VERSION
|
|
38
|
+
if tap_head and tap_head != installed_commit:
|
|
39
|
+
if source_sha is not None and source_sha != installed_sha:
|
|
40
|
+
return CONTENT
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ── drift_state: how an installed skill's store copy relates to lock+tap ───
|
|
45
|
+
IN_SYNC = "in-sync"
|
|
46
|
+
LOCAL_EDITS = "local-edits"
|
|
47
|
+
UPSTREAM_MOVED = "upstream-moved"
|
|
48
|
+
SOURCE_MISSING = "source-missing"
|
|
49
|
+
STORE_MISSING = "store-missing"
|
|
50
|
+
NA = "n/a"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def drift_state(store_sha: Optional[str], lock_sha: str,
|
|
54
|
+
is_local: bool, source_sha: Optional[str]) -> str:
|
|
55
|
+
"""Drift of an installed skill's store copy vs its lock entry and tap.
|
|
56
|
+
|
|
57
|
+
Signals (all pre-computed by the caller):
|
|
58
|
+
|
|
59
|
+
* ``store_sha`` — ``sha256`` of the on-disk store dir, or ``None`` if the
|
|
60
|
+
store dir is missing.
|
|
61
|
+
* ``lock_sha`` — the ``sha256`` recorded in the lock entry.
|
|
62
|
+
* ``is_local`` — ``True`` for local imports (no tap source to compare).
|
|
63
|
+
* ``source_sha`` — ``sha256`` of the tap source dir, or ``None`` if it is
|
|
64
|
+
unavailable (only consulted once the store copy matches the lock and the
|
|
65
|
+
skill has a tap).
|
|
66
|
+
|
|
67
|
+
The ordering is significant and matches the historical ``_drift_status``:
|
|
68
|
+
a missing store beats local edits, which beat the local/upstream split.
|
|
69
|
+
"""
|
|
70
|
+
if store_sha is None:
|
|
71
|
+
return STORE_MISSING
|
|
72
|
+
if store_sha != lock_sha:
|
|
73
|
+
return LOCAL_EDITS
|
|
74
|
+
if is_local:
|
|
75
|
+
return NA
|
|
76
|
+
if source_sha is None:
|
|
77
|
+
return SOURCE_MISSING
|
|
78
|
+
return IN_SYNC if source_sha == lock_sha else UPSTREAM_MOVED
|
|
@@ -48,6 +48,7 @@ boost_cli/core/paths.py
|
|
|
48
48
|
boost_cli/core/policy.py
|
|
49
49
|
boost_cli/core/rag.py
|
|
50
50
|
boost_cli/core/registry.py
|
|
51
|
+
boost_cli/core/staleness.py
|
|
51
52
|
boost_cli/core/store.py
|
|
52
53
|
boost_cli/core/util.py
|
|
53
54
|
boost_cli/data/registries.json
|
|
@@ -126,9 +127,11 @@ docs/roadmap/items/layering-guard-import-linter.md
|
|
|
126
127
|
docs/roadmap/items/lighthouse-ci-on-the-pages-site.md
|
|
127
128
|
docs/roadmap/items/localize-the-stored-bm25-snippet.md
|
|
128
129
|
docs/roadmap/items/lockfile-enforcement-and-commit-pinning.md
|
|
130
|
+
docs/roadmap/items/log-timestamps-mislabeled-utc.md
|
|
129
131
|
docs/roadmap/items/lowest-version-resolution-uv-resolution-lowest-d.md
|
|
130
132
|
docs/roadmap/items/make-the-roadmaps-discoverable.md
|
|
131
133
|
docs/roadmap/items/markdown-consistency-markdownlint-cli2.md
|
|
134
|
+
docs/roadmap/items/mcp-launch-objc-fork-safety.md
|
|
132
135
|
docs/roadmap/items/memoize-config-load-in-process.md
|
|
133
136
|
docs/roadmap/items/modernization-smells-refurb-pyupgrade.md
|
|
134
137
|
docs/roadmap/items/mutation-hardening-core-frontmatter-py.md
|
|
@@ -223,6 +226,7 @@ tests/unit/test_registries_fresh.py
|
|
|
223
226
|
tests/unit/test_registry.py
|
|
224
227
|
tests/unit/test_roadmap_fresh.py
|
|
225
228
|
tests/unit/test_spin.py
|
|
229
|
+
tests/unit/test_staleness.py
|
|
226
230
|
tests/unit/test_store.py
|
|
227
231
|
tests/unit/test_token_parity.py
|
|
228
232
|
tests/unit/test_util.py
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: consolidate-skill-staleness-drift-logic-into-cor
|
|
3
|
+
board: code
|
|
4
|
+
section: shipped
|
|
5
|
+
status: shipped
|
|
6
|
+
category: Correctness
|
|
7
|
+
complexity: M
|
|
8
|
+
impact: High
|
|
9
|
+
wow: 3
|
|
10
|
+
note: three copies → one core module
|
|
11
|
+
order: 3
|
|
12
|
+
owner: loop/consolidate-staleness
|
|
13
|
+
pr: 91
|
|
14
|
+
title: Consolidate skill-staleness / drift logic into core
|
|
15
|
+
---
|
|
16
|
+
The "is this skill outdated" decision — <code>semver_gt</code> → commit compare → <code>sha256_dir</code> vs lock — was reimplemented nearly verbatim in <code>cmd_update</code>, <code>cmd_outdated</code> and <code>_drift_status</code> — three copies of core business logic that would drift apart. It now lives in one place: <code>core/staleness.py</code> exposes two <b>pure</b> (I/O-free) decisions — <code>upstream_reason()</code> (the version→commit→content ladder rendered by <code>cmd_update</code>/<code>cmd_outdated</code>) and <code>drift_state()</code> (the store/local-edits/upstream ladder rendered by <code>_drift_status</code>). The commands became thin renderers; behavior is byte-for-byte preserved and the new module is fully unit-tested (every branch pinned, all mutants killed).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: log-timestamps-mislabeled-utc
|
|
3
|
+
board: code
|
|
4
|
+
section: health
|
|
5
|
+
status: planned
|
|
6
|
+
category: Observability · Diagnostics
|
|
7
|
+
complexity: S
|
|
8
|
+
impact: Med
|
|
9
|
+
wow: 2
|
|
10
|
+
note: converter = time.gmtime
|
|
11
|
+
order: 10
|
|
12
|
+
owner:
|
|
13
|
+
pr:
|
|
14
|
+
title: Log timestamps are local time mislabeled <code>Z</code>
|
|
15
|
+
---
|
|
16
|
+
<code>core/logs.py</code> builds the <code>logging.Formatter</code> with
|
|
17
|
+
<code>datefmt="%Y-%m-%dT%H:%M:%SZ"</code> but never sets
|
|
18
|
+
<code>converter = time.gmtime</code>, so <code>%(asctime)s</code> uses
|
|
19
|
+
<em>local</em> time while stamping a literal <code>Z</code> (UTC)
|
|
20
|
+
suffix. Every line in <code>~/.boost/logs/boost.log</code> is therefore
|
|
21
|
+
off by the machine's UTC offset — which nearly caused a 5-hour
|
|
22
|
+
mis-correlation when matching the log against an OS crash report. Set
|
|
23
|
+
the formatter's <code>converter</code> to <code>time.gmtime</code> (or
|
|
24
|
+
drop the <code>Z</code>) so the trail is honestly UTC, making the
|
|
25
|
+
pid/ppid crash-correlation breadcrumbs trustworthy.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: mcp-launch-objc-fork-safety
|
|
3
|
+
board: code
|
|
4
|
+
section: compat
|
|
5
|
+
status: planned
|
|
6
|
+
category: Compatibility · macOS
|
|
7
|
+
complexity: S
|
|
8
|
+
impact: Med
|
|
9
|
+
wow: 3
|
|
10
|
+
note: OBJC_DISABLE_INITIALIZE_FORK_SAFETY
|
|
11
|
+
order: 9
|
|
12
|
+
owner:
|
|
13
|
+
pr:
|
|
14
|
+
title: Harden <code>boost mcp</code> launch against macOS Obj-C fork aborts
|
|
15
|
+
---
|
|
16
|
+
On macOS, a host that spawns <code>boost mcp --stdio</code> via <code>fork()</code>
|
|
17
|
+
can abort on the child side pre-<code>exec</code> when Obj-C is touched
|
|
18
|
+
post-fork (CFPreferences / <code>_scproxy</code> proxy lookup) — the
|
|
19
|
+
classic <em>"crashed on child side of fork pre-exec"</em>
|
|
20
|
+
<code>SIGABRT</code>. Not boost's own code (it aborts before boost
|
|
21
|
+
runs), but boost can make its integration robust: when registering the
|
|
22
|
+
MCP server (<code>boost mcp register</code> / the emitted client
|
|
23
|
+
config), set <code>OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES</code> (and
|
|
24
|
+
<code>no_proxy=*</code>) in the launch environment so the host's fork
|
|
25
|
+
into boost can't trip the Obj-C fork-safety abort. Document it for
|
|
26
|
+
hosts that launch boost directly.
|
|
@@ -115,10 +115,10 @@
|
|
|
115
115
|
<section class="block reveal">
|
|
116
116
|
<div class="stats">
|
|
117
117
|
<!-- ROADMAP:stats -->
|
|
118
|
-
<div class="stat"><b>
|
|
118
|
+
<div class="stat"><b>10</b><span>Shipped</span></div>
|
|
119
119
|
<div class="stat"><b>3</b><span>Next up</span></div>
|
|
120
120
|
<div class="stat"><b>4</b><span>Planned</span></div>
|
|
121
|
-
<div class="stat"><b>
|
|
121
|
+
<div class="stat"><b>87</b><span>Loop finds</span></div>
|
|
122
122
|
<!-- /ROADMAP:stats -->
|
|
123
123
|
</div>
|
|
124
124
|
</section>
|
|
@@ -162,6 +162,18 @@
|
|
|
162
162
|
</div>
|
|
163
163
|
</article>
|
|
164
164
|
|
|
165
|
+
<article class="cap rcard">
|
|
166
|
+
<div class="head"><span class="pill shipped">Shipped</span><span class="cat">Correctness</span></div>
|
|
167
|
+
<h3>Consolidate skill-staleness / drift logic into core</h3>
|
|
168
|
+
<p>The "is this skill outdated" decision — <code>semver_gt</code> → commit compare → <code>sha256_dir</code> vs lock — was reimplemented nearly verbatim in <code>cmd_update</code>, <code>cmd_outdated</code> and <code>_drift_status</code> — three copies of core business logic that would drift apart. It now lives in one place: <code>core/staleness.py</code> exposes two <b>pure</b> (I/O-free) decisions — <code>upstream_reason()</code> (the version→commit→content ladder rendered by <code>cmd_update</code>/<code>cmd_outdated</code>) and <code>drift_state()</code> (the store/local-edits/upstream ladder rendered by <code>_drift_status</code>). The commands became thin renderers; behavior is byte-for-byte preserved and the new module is fully unit-tested (every branch pinned, all mutants killed).</p>
|
|
169
|
+
<div class="meta">
|
|
170
|
+
<span class="m">Complexity <b>M</b></span>
|
|
171
|
+
<span class="m hi">Impact <b>High</b></span>
|
|
172
|
+
<span class="m">Wow <b class="wow">★★★</b></span>
|
|
173
|
+
<span class="m">three copies → one core module</span>
|
|
174
|
+
</div>
|
|
175
|
+
</article>
|
|
176
|
+
|
|
165
177
|
<article class="cap rcard">
|
|
166
178
|
<div class="head"><span class="pill shipped">Shipped</span><span class="cat">Testing</span></div>
|
|
167
179
|
<h3>Mutation hardening — <code>core/frontmatter.py</code></h3>
|
|
@@ -439,17 +451,6 @@
|
|
|
439
451
|
</div>
|
|
440
452
|
</article>
|
|
441
453
|
|
|
442
|
-
<article class="cap rcard">
|
|
443
|
-
<div class="head"><span class="pill next">Next</span><span class="cat">Correctness</span></div>
|
|
444
|
-
<h3>Consolidate skill-staleness / drift logic into core</h3>
|
|
445
|
-
<p>The "is this skill outdated" decision — <code>semver_gt</code> → commit compare → <code>sha256_dir</code> vs lock — is reimplemented nearly verbatim in <code>cmd_update</code>, <code>cmd_outdated</code> and <code>_drift_status</code> (<code>pkg.py:277 · taps.py:225 · quality.py:120</code>). Three copies of core business logic that <b>will drift apart</b>. Make it one <code>core</code> function the commands render.</p>
|
|
446
|
-
<div class="meta">
|
|
447
|
-
<span class="m">Complexity <b>M</b></span>
|
|
448
|
-
<span class="m hi">Impact <b>High</b></span>
|
|
449
|
-
<span class="m">Wow <b class="wow">★★★</b></span>
|
|
450
|
-
</div>
|
|
451
|
-
</article>
|
|
452
|
-
|
|
453
454
|
<article class="cap rcard">
|
|
454
455
|
<div class="head"><span class="pill shipped">Shipped</span><span class="cat">Performance</span></div>
|
|
455
456
|
<h3>Memoize <code>config.load()</code> in-process</h3>
|
|
@@ -783,6 +784,27 @@
|
|
|
783
784
|
<span class="m">avoid macOS _scproxy Obj-C path</span>
|
|
784
785
|
</div>
|
|
785
786
|
</article>
|
|
787
|
+
|
|
788
|
+
<article class="cap rcard">
|
|
789
|
+
<div class="head"><span class="pill planned">Planned</span><span class="cat">Observability · Diagnostics</span></div>
|
|
790
|
+
<h3>Log timestamps are local time mislabeled <code>Z</code></h3>
|
|
791
|
+
<p><code>core/logs.py</code> builds the <code>logging.Formatter</code> with
|
|
792
|
+
<code>datefmt="%Y-%m-%dT%H:%M:%SZ"</code> but never sets
|
|
793
|
+
<code>converter = time.gmtime</code>, so <code>%(asctime)s</code> uses
|
|
794
|
+
<em>local</em> time while stamping a literal <code>Z</code> (UTC)
|
|
795
|
+
suffix. Every line in <code>~/.boost/logs/boost.log</code> is therefore
|
|
796
|
+
off by the machine's UTC offset — which nearly caused a 5-hour
|
|
797
|
+
mis-correlation when matching the log against an OS crash report. Set
|
|
798
|
+
the formatter's <code>converter</code> to <code>time.gmtime</code> (or
|
|
799
|
+
drop the <code>Z</code>) so the trail is honestly UTC, making the
|
|
800
|
+
pid/ppid crash-correlation breadcrumbs trustworthy.</p>
|
|
801
|
+
<div class="meta">
|
|
802
|
+
<span class="m">Complexity <b>S</b></span>
|
|
803
|
+
<span class="m">Impact <b>Med</b></span>
|
|
804
|
+
<span class="m">Wow <b class="wow">★★</b></span>
|
|
805
|
+
<span class="m">converter = time.gmtime</span>
|
|
806
|
+
</div>
|
|
807
|
+
</article>
|
|
786
808
|
<!-- /ROADMAP:cards -->
|
|
787
809
|
|
|
788
810
|
</div>
|
|
@@ -1398,6 +1420,28 @@
|
|
|
1398
1420
|
<span class="m">local == CI</span>
|
|
1399
1421
|
</div>
|
|
1400
1422
|
</article>
|
|
1423
|
+
|
|
1424
|
+
<article class="cap rcard">
|
|
1425
|
+
<div class="head"><span class="pill planned">Planned</span><span class="cat">Compatibility · macOS</span></div>
|
|
1426
|
+
<h3>Harden <code>boost mcp</code> launch against macOS Obj-C fork aborts</h3>
|
|
1427
|
+
<p>On macOS, a host that spawns <code>boost mcp --stdio</code> via <code>fork()</code>
|
|
1428
|
+
can abort on the child side pre-<code>exec</code> when Obj-C is touched
|
|
1429
|
+
post-fork (CFPreferences / <code>_scproxy</code> proxy lookup) — the
|
|
1430
|
+
classic <em>"crashed on child side of fork pre-exec"</em>
|
|
1431
|
+
<code>SIGABRT</code>. Not boost's own code (it aborts before boost
|
|
1432
|
+
runs), but boost can make its integration robust: when registering the
|
|
1433
|
+
MCP server (<code>boost mcp register</code> / the emitted client
|
|
1434
|
+
config), set <code>OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES</code> (and
|
|
1435
|
+
<code>no_proxy=*</code>) in the launch environment so the host's fork
|
|
1436
|
+
into boost can't trip the Obj-C fork-safety abort. Document it for
|
|
1437
|
+
hosts that launch boost directly.</p>
|
|
1438
|
+
<div class="meta">
|
|
1439
|
+
<span class="m">Complexity <b>S</b></span>
|
|
1440
|
+
<span class="m">Impact <b>Med</b></span>
|
|
1441
|
+
<span class="m">Wow <b class="wow">★★★</b></span>
|
|
1442
|
+
<span class="m">OBJC_DISABLE_INITIALIZE_FORK_SAFETY</span>
|
|
1443
|
+
</div>
|
|
1444
|
+
</article>
|
|
1401
1445
|
<!-- /ROADMAP:cards -->
|
|
1402
1446
|
|
|
1403
1447
|
</div>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Unit tests: boost_cli/core/staleness.py — the single-source staleness/drift
|
|
2
|
+
decision that cmd_update, cmd_outdated and _drift_status now render.
|
|
3
|
+
|
|
4
|
+
Both functions are pure, so every branch (and the ordering between branches) is
|
|
5
|
+
pinned here with assertions specific enough to kill mutants.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from boost_cli.core import staleness
|
|
10
|
+
|
|
11
|
+
SHA_A = "a" * 64
|
|
12
|
+
SHA_B = "b" * 64
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestUpstreamReason:
|
|
16
|
+
def test_higher_semver_is_version(self):
|
|
17
|
+
assert staleness.upstream_reason(
|
|
18
|
+
"1.0.0", "1.1.0", "c1", "c2", SHA_A, SHA_B) == staleness.VERSION
|
|
19
|
+
|
|
20
|
+
def test_version_constant_value(self):
|
|
21
|
+
# pins the literal so a mutated constant is caught
|
|
22
|
+
assert staleness.VERSION == "version"
|
|
23
|
+
|
|
24
|
+
def test_content_constant_value(self):
|
|
25
|
+
assert staleness.CONTENT == "content"
|
|
26
|
+
|
|
27
|
+
def test_version_wins_even_when_commit_and_content_also_differ(self):
|
|
28
|
+
# semver must be checked FIRST — a higher version reports "version"
|
|
29
|
+
# regardless of the (also-changed) commit/content signals.
|
|
30
|
+
assert staleness.upstream_reason(
|
|
31
|
+
"1.0.0", "2.0.0", "old", "new", SHA_A, SHA_B) == staleness.VERSION
|
|
32
|
+
|
|
33
|
+
def test_same_version_no_commit_move_is_none(self):
|
|
34
|
+
assert staleness.upstream_reason(
|
|
35
|
+
"1.0.0", "1.0.0", "c1", "c1", SHA_A, SHA_B) is None
|
|
36
|
+
|
|
37
|
+
def test_empty_tap_head_is_none(self):
|
|
38
|
+
# no HEAD signal (e.g. tap not cloned) → not stale on the content axis
|
|
39
|
+
assert staleness.upstream_reason(
|
|
40
|
+
"1.0.0", "1.0.0", "c1", "", SHA_A, SHA_B) is None
|
|
41
|
+
|
|
42
|
+
def test_moved_commit_but_source_unhashable_is_none(self):
|
|
43
|
+
# source_sha None means "no content signal" — must NOT report content
|
|
44
|
+
assert staleness.upstream_reason(
|
|
45
|
+
"1.0.0", "1.0.0", "old", "new", SHA_A, None) is None
|
|
46
|
+
|
|
47
|
+
def test_moved_commit_same_content_is_none(self):
|
|
48
|
+
assert staleness.upstream_reason(
|
|
49
|
+
"1.0.0", "1.0.0", "old", "new", SHA_A, SHA_A) is None
|
|
50
|
+
|
|
51
|
+
def test_moved_commit_changed_content_is_content(self):
|
|
52
|
+
assert staleness.upstream_reason(
|
|
53
|
+
"1.0.0", "1.0.0", "old", "new", SHA_A, SHA_B) == staleness.CONTENT
|
|
54
|
+
|
|
55
|
+
def test_lower_advertised_version_not_stale_by_version(self):
|
|
56
|
+
# a downgrade in the catalog is not an upgrade — no commit move → None
|
|
57
|
+
assert staleness.upstream_reason(
|
|
58
|
+
"2.0.0", "1.0.0", "c1", "c1", SHA_A, SHA_B) is None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TestDriftState:
|
|
62
|
+
def test_store_missing(self):
|
|
63
|
+
assert staleness.drift_state(
|
|
64
|
+
None, SHA_A, False, SHA_A) == staleness.STORE_MISSING
|
|
65
|
+
|
|
66
|
+
def test_store_missing_takes_precedence_over_local(self):
|
|
67
|
+
# a missing store is reported even for a local import
|
|
68
|
+
assert staleness.drift_state(
|
|
69
|
+
None, SHA_A, True, None) == staleness.STORE_MISSING
|
|
70
|
+
|
|
71
|
+
def test_local_edits(self):
|
|
72
|
+
assert staleness.drift_state(
|
|
73
|
+
SHA_B, SHA_A, False, SHA_A) == staleness.LOCAL_EDITS
|
|
74
|
+
|
|
75
|
+
def test_local_edits_take_precedence_over_na(self):
|
|
76
|
+
# edited store beats the local/upstream split even for local imports
|
|
77
|
+
assert staleness.drift_state(
|
|
78
|
+
SHA_B, SHA_A, True, None) == staleness.LOCAL_EDITS
|
|
79
|
+
|
|
80
|
+
def test_local_import_in_sync_is_na(self):
|
|
81
|
+
assert staleness.drift_state(
|
|
82
|
+
SHA_A, SHA_A, True, None) == staleness.NA
|
|
83
|
+
|
|
84
|
+
def test_source_missing(self):
|
|
85
|
+
assert staleness.drift_state(
|
|
86
|
+
SHA_A, SHA_A, False, None) == staleness.SOURCE_MISSING
|
|
87
|
+
|
|
88
|
+
def test_in_sync(self):
|
|
89
|
+
assert staleness.drift_state(
|
|
90
|
+
SHA_A, SHA_A, False, SHA_A) == staleness.IN_SYNC
|
|
91
|
+
|
|
92
|
+
def test_upstream_moved(self):
|
|
93
|
+
assert staleness.drift_state(
|
|
94
|
+
SHA_A, SHA_A, False, SHA_B) == staleness.UPSTREAM_MOVED
|
|
95
|
+
|
|
96
|
+
def test_status_constant_values(self):
|
|
97
|
+
assert staleness.IN_SYNC == "in-sync"
|
|
98
|
+
assert staleness.LOCAL_EDITS == "local-edits"
|
|
99
|
+
assert staleness.UPSTREAM_MOVED == "upstream-moved"
|
|
100
|
+
assert staleness.SOURCE_MISSING == "source-missing"
|
|
101
|
+
assert staleness.STORE_MISSING == "store-missing"
|
|
102
|
+
assert staleness.NA == "n/a"
|
boost_skill_cli-1.0.82/docs/roadmap/items/consolidate-skill-staleness-drift-logic-into-cor.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
id: consolidate-skill-staleness-drift-logic-into-cor
|
|
3
|
-
board: code
|
|
4
|
-
section: internals
|
|
5
|
-
status: next
|
|
6
|
-
category: Correctness
|
|
7
|
-
complexity: M
|
|
8
|
-
impact: High
|
|
9
|
-
wow: 3
|
|
10
|
-
note:
|
|
11
|
-
order: 3
|
|
12
|
-
owner:
|
|
13
|
-
pr:
|
|
14
|
-
title: Consolidate skill-staleness / drift logic into core
|
|
15
|
-
---
|
|
16
|
-
The "is this skill outdated" decision — <code>semver_gt</code> → commit compare → <code>sha256_dir</code> vs lock — is reimplemented nearly verbatim in <code>cmd_update</code>, <code>cmd_outdated</code> and <code>_drift_status</code> (<code>pkg.py:277 · taps.py:225 · quality.py:120</code>). Three copies of core business logic that <b>will drift apart</b>. Make it one <code>core</code> function the commands render.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|