shockharness 0.1.0__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.
- shockharness-0.1.0/.gitignore +20 -0
- shockharness-0.1.0/.shockharness.toml +179 -0
- shockharness-0.1.0/CHANGELOG.md +292 -0
- shockharness-0.1.0/LICENSE +21 -0
- shockharness-0.1.0/PKG-INFO +447 -0
- shockharness-0.1.0/README.md +413 -0
- shockharness-0.1.0/STATUS.md +1581 -0
- shockharness-0.1.0/docs/architecture.md +165 -0
- shockharness-0.1.0/docs/assets/hero.png +0 -0
- shockharness-0.1.0/docs/assets/hero.webp +0 -0
- shockharness-0.1.0/docs/assets/logo.png +0 -0
- shockharness-0.1.0/docs/assets/logo.webp +0 -0
- shockharness-0.1.0/docs/complexity.md +324 -0
- shockharness-0.1.0/docs/configuration.md +412 -0
- shockharness-0.1.0/docs/integration.md +218 -0
- shockharness-0.1.0/docs/limitations.md +252 -0
- shockharness-0.1.0/docs/releasing.md +86 -0
- shockharness-0.1.0/docs/rules.md +352 -0
- shockharness-0.1.0/docs/shockharness_advanced_guardrails_spec.md +687 -0
- shockharness-0.1.0/docs/shockharness_distribution_integration.md +168 -0
- shockharness-0.1.0/docs/skills.md +131 -0
- shockharness-0.1.0/evals/README.md +82 -0
- shockharness-0.1.0/evals/run_evals.py +324 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/reference/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/reference/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/reference/src/config.js +33 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/start/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/start/src/config.js +8 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/task.md +17 -0
- shockharness-0.1.0/evals/tasks/js-01-config-env/tests/config.test.js +22 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/reference/.shockharness.toml +10 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/reference/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/reference/src/body.js +27 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/start/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/start/src/body.js +17 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/task.md +21 -0
- shockharness-0.1.0/evals/tasks/js-02-catch-fallback/tests/body.test.js +23 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/reference/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/reference/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/reference/src/pages.js +26 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/start/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/start/src/pages.js +17 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/task.md +17 -0
- shockharness-0.1.0/evals/tasks/js-03-pagination-defaults/tests/pages.test.js +20 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/reference/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/reference/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/reference/src/status.js +18 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/start/package.json +5 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/start/src/status.js +13 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/task.md +17 -0
- shockharness-0.1.0/evals/tasks/js-04-status-codes/tests/status.test.js +20 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/reference/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/reference/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/reference/src/app/settings.py +29 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/start/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/start/src/app/settings.py +20 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/task.md +19 -0
- shockharness-0.1.0/evals/tasks/py-01-http-settings/tests/test_settings.py +30 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/reference/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/reference/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/reference/src/app/profile.py +17 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/start/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/start/src/app/profile.py +13 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/task.md +16 -0
- shockharness-0.1.0/evals/tasks/py-02-mapping-fallbacks/tests/test_profile.py +27 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/reference/.shockharness.toml +10 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/reference/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/reference/src/app/store.py +21 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/start/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/start/src/app/store.py +22 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/task.md +20 -0
- shockharness-0.1.0/evals/tasks/py-03-json-boundary/tests/test_store.py +24 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/expected.json +6 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/reference/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/reference/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/reference/src/app/pages.py +38 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/start/.shockharness.toml +4 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/start/src/app/__init__.py +0 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/start/src/app/pages.py +16 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/task.md +17 -0
- shockharness-0.1.0/evals/tasks/py-04-pagination-numbers/tests/test_pages.py +30 -0
- shockharness-0.1.0/examples/js/.shockharness.toml +12 -0
- shockharness-0.1.0/examples/js/README.md +28 -0
- shockharness-0.1.0/examples/python/.shockharness.toml +12 -0
- shockharness-0.1.0/examples/python/README.md +22 -0
- shockharness-0.1.0/examples/python/tests/__init__.py +0 -0
- shockharness-0.1.0/examples/python/tests/test_example_service.py +24 -0
- shockharness-0.1.0/pyproject.toml +85 -0
- shockharness-0.1.0/schemas/baseline-v1.schema.json +64 -0
- shockharness-0.1.0/schemas/diff-v1.schema.json +656 -0
- shockharness-0.1.0/schemas/report-v1.schema.json +948 -0
- shockharness-0.1.0/src/shockharness/__init__.py +13 -0
- shockharness-0.1.0/src/shockharness/__main__.py +5 -0
- shockharness-0.1.0/src/shockharness/analysis.py +447 -0
- shockharness-0.1.0/src/shockharness/api.py +114 -0
- shockharness-0.1.0/src/shockharness/cli.py +596 -0
- shockharness-0.1.0/src/shockharness/complexity/__init__.py +8 -0
- shockharness-0.1.0/src/shockharness/complexity/assemble.py +119 -0
- shockharness-0.1.0/src/shockharness/complexity/callgraph.py +754 -0
- shockharness-0.1.0/src/shockharness/complexity/cognitive.py +96 -0
- shockharness-0.1.0/src/shockharness/complexity/coupling.py +350 -0
- shockharness-0.1.0/src/shockharness/complexity/cyclomatic.py +45 -0
- shockharness-0.1.0/src/shockharness/complexity/metrics.py +148 -0
- shockharness-0.1.0/src/shockharness/complexity/nesting.py +50 -0
- shockharness-0.1.0/src/shockharness/complexity/npath.py +110 -0
- shockharness-0.1.0/src/shockharness/complexity/regressions.py +176 -0
- shockharness-0.1.0/src/shockharness/complexity/size.py +28 -0
- shockharness-0.1.0/src/shockharness/config.py +502 -0
- shockharness-0.1.0/src/shockharness/constants.py +77 -0
- shockharness-0.1.0/src/shockharness/defaults.toml +102 -0
- shockharness-0.1.0/src/shockharness/discovery.py +143 -0
- shockharness-0.1.0/src/shockharness/errors.py +42 -0
- shockharness-0.1.0/src/shockharness/findings.py +87 -0
- shockharness-0.1.0/src/shockharness/gate.py +232 -0
- shockharness-0.1.0/src/shockharness/gating/__init__.py +1 -0
- shockharness-0.1.0/src/shockharness/gating/baseline.py +349 -0
- shockharness-0.1.0/src/shockharness/gating/diff.py +260 -0
- shockharness-0.1.0/src/shockharness/globs.py +142 -0
- shockharness-0.1.0/src/shockharness/history/__init__.py +1 -0
- shockharness-0.1.0/src/shockharness/history/cochange.py +73 -0
- shockharness-0.1.0/src/shockharness/history/git.py +182 -0
- shockharness-0.1.0/src/shockharness/history/hotspots.py +163 -0
- shockharness-0.1.0/src/shockharness/ir/__init__.py +1 -0
- shockharness-0.1.0/src/shockharness/ir/javascript.py +181 -0
- shockharness-0.1.0/src/shockharness/ir/javascript_catches.py +570 -0
- shockharness-0.1.0/src/shockharness/ir/javascript_defaults.py +874 -0
- shockharness-0.1.0/src/shockharness/ir/javascript_functions.py +1135 -0
- shockharness-0.1.0/src/shockharness/ir/lines.py +54 -0
- shockharness-0.1.0/src/shockharness/ir/model.py +411 -0
- shockharness-0.1.0/src/shockharness/ir/python.py +185 -0
- shockharness-0.1.0/src/shockharness/ir/python_catches.py +441 -0
- shockharness-0.1.0/src/shockharness/ir/python_defaults.py +917 -0
- shockharness-0.1.0/src/shockharness/ir/python_functions.py +865 -0
- shockharness-0.1.0/src/shockharness/output/__init__.py +1 -0
- shockharness-0.1.0/src/shockharness/output/json.py +15 -0
- shockharness-0.1.0/src/shockharness/output/sarif.py +203 -0
- shockharness-0.1.0/src/shockharness/output/text.py +248 -0
- shockharness-0.1.0/src/shockharness/profiles/__init__.py +1 -0
- shockharness-0.1.0/src/shockharness/profiles/minimal.toml +20 -0
- shockharness-0.1.0/src/shockharness/profiles/recommended.toml +7 -0
- shockharness-0.1.0/src/shockharness/profiles/strict.toml +5 -0
- shockharness-0.1.0/src/shockharness/report.py +188 -0
- shockharness-0.1.0/src/shockharness/rules/__init__.py +1 -0
- shockharness-0.1.0/src/shockharness/rules/complexity.py +207 -0
- shockharness-0.1.0/src/shockharness/rules/defaults.py +285 -0
- shockharness-0.1.0/src/shockharness/rules/exceptions.py +318 -0
- shockharness-0.1.0/src/shockharness/rules/magic_numbers.py +59 -0
- shockharness-0.1.0/src/shockharness/rules/registry.py +917 -0
- shockharness-0.1.0/src/shockharness/rules/severity.py +23 -0
- shockharness-0.1.0/src/shockharness/schema.py +271 -0
- shockharness-0.1.0/src/shockharness/skills/__init__.py +91 -0
- shockharness-0.1.0/src/shockharness/skills/adapters.py +106 -0
- shockharness-0.1.0/src/shockharness/skills/fix-violations/SKILL.md +393 -0
- shockharness-0.1.0/src/shockharness/skills/install.py +336 -0
- shockharness-0.1.0/src/shockharness/skills/pre-commit-review/SKILL.md +304 -0
- shockharness-0.1.0/src/shockharness/skills/refactor-complexity/SKILL.md +413 -0
- shockharness-0.1.0/src/shockharness/skills/write-compliant-code/SKILL.md +400 -0
- shockharness-0.1.0/src/shockharness/source.py +52 -0
- shockharness-0.1.0/src/shockharness/suppressions.py +195 -0
- shockharness-0.1.0/src/shockharness/version.py +3 -0
- shockharness-0.1.0/tests/__init__.py +0 -0
- shockharness-0.1.0/tests/cli/__init__.py +0 -0
- shockharness-0.1.0/tests/cli/test_cli.py +962 -0
- shockharness-0.1.0/tests/conftest.py +139 -0
- shockharness-0.1.0/tests/fixtures/expected.json +3074 -0
- shockharness-0.1.0/tests/fixtures/javascript/clean.js +3 -0
- shockharness-0.1.0/tests/fixtures/javascript/common.cjs +1 -0
- shockharness-0.1.0/tests/fixtures/javascript/def_destructuring.js +21 -0
- shockharness-0.1.0/tests/fixtures/javascript/def_exception_default.js +74 -0
- shockharness-0.1.0/tests/fixtures/javascript/def_logical_or.js +43 -0
- shockharness-0.1.0/tests/fixtures/javascript/def_nullish.js +40 -0
- shockharness-0.1.0/tests/fixtures/javascript/def_parameters.js +38 -0
- shockharness-0.1.0/tests/fixtures/javascript/def_process_env.js +15 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_async_await.js +35 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_boundary_catch.js +87 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_nested_catches.js +28 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_promise_catch.js +32 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_rethrow.js +40 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_return_fallback.js +58 -0
- shockharness-0.1.0/tests/fixtures/javascript/exc_untyped_catch.js +32 -0
- shockharness-0.1.0/tests/fixtures/javascript/jsx.js +2 -0
- shockharness-0.1.0/tests/fixtures/javascript/magic_basic.js +7 -0
- shockharness-0.1.0/tests/fixtures/javascript/magic_encodings.js +3 -0
- shockharness-0.1.0/tests/fixtures/javascript/magic_negatives.js +10 -0
- shockharness-0.1.0/tests/fixtures/javascript/module.mjs +2 -0
- shockharness-0.1.0/tests/fixtures/javascript/scopes.js +9 -0
- shockharness-0.1.0/tests/fixtures/javascript/strings_regex.js +6 -0
- shockharness-0.1.0/tests/fixtures/javascript/suppressions.js +4 -0
- shockharness-0.1.0/tests/fixtures/javascript/syntax_error.js +3 -0
- shockharness-0.1.0/tests/fixtures/javascript/unicode.js +2 -0
- shockharness-0.1.0/tests/fixtures/python/clean.py +15 -0
- shockharness-0.1.0/tests/fixtures/python/def_dataclasses.py +30 -0
- shockharness-0.1.0/tests/fixtures/python/def_environment.py +23 -0
- shockharness-0.1.0/tests/fixtures/python/def_exception_default.py +78 -0
- shockharness-0.1.0/tests/fixtures/python/def_mapping_get.py +55 -0
- shockharness-0.1.0/tests/fixtures/python/def_nullish.py +28 -0
- shockharness-0.1.0/tests/fixtures/python/def_parameters.py +59 -0
- shockharness-0.1.0/tests/fixtures/python/def_truthiness.py +42 -0
- shockharness-0.1.0/tests/fixtures/python/exc_bare.py +12 -0
- shockharness-0.1.0/tests/fixtures/python/exc_broad.py +26 -0
- shockharness-0.1.0/tests/fixtures/python/exc_except_star.py +10 -0
- shockharness-0.1.0/tests/fixtures/python/exc_logging_only.py +49 -0
- shockharness-0.1.0/tests/fixtures/python/exc_multiple_operations.py +60 -0
- shockharness-0.1.0/tests/fixtures/python/exc_nested.py +29 -0
- shockharness-0.1.0/tests/fixtures/python/exc_rethrow.py +44 -0
- shockharness-0.1.0/tests/fixtures/python/exc_rethrow_transparent.py +30 -0
- shockharness-0.1.0/tests/fixtures/python/exc_return_fallback.py +62 -0
- shockharness-0.1.0/tests/fixtures/python/exc_scoped_timeout.py +55 -0
- shockharness-0.1.0/tests/fixtures/python/exc_tuple.py +22 -0
- shockharness-0.1.0/tests/fixtures/python/exc_unresolved.py +91 -0
- shockharness-0.1.0/tests/fixtures/python/magic_annotations.py +8 -0
- shockharness-0.1.0/tests/fixtures/python/magic_basic.py +13 -0
- shockharness-0.1.0/tests/fixtures/python/magic_booleans.py +5 -0
- shockharness-0.1.0/tests/fixtures/python/magic_encodings.py +3 -0
- shockharness-0.1.0/tests/fixtures/python/magic_negatives.py +9 -0
- shockharness-0.1.0/tests/fixtures/python/magic_scopes.py +32 -0
- shockharness-0.1.0/tests/fixtures/python/magic_strings_not_numbers.py +5 -0
- shockharness-0.1.0/tests/fixtures/python/suppressions.py +9 -0
- shockharness-0.1.0/tests/fixtures/python/syntax_error.py +2 -0
- shockharness-0.1.0/tests/fixtures/python/unicode.py +3 -0
- shockharness-0.1.0/tests/property/__init__.py +0 -0
- shockharness-0.1.0/tests/property/test_complexity_fuzz.py +175 -0
- shockharness-0.1.0/tests/property/test_properties.py +159 -0
- shockharness-0.1.0/tests/test_self_compliance.py +31 -0
- shockharness-0.1.0/tests/unit/__init__.py +0 -0
- shockharness-0.1.0/tests/unit/test_analysis.py +132 -0
- shockharness-0.1.0/tests/unit/test_baseline.py +274 -0
- shockharness-0.1.0/tests/unit/test_callgraph.py +357 -0
- shockharness-0.1.0/tests/unit/test_complexity_diff.py +182 -0
- shockharness-0.1.0/tests/unit/test_complexity_metrics.py +589 -0
- shockharness-0.1.0/tests/unit/test_config.py +250 -0
- shockharness-0.1.0/tests/unit/test_consumer_script.py +84 -0
- shockharness-0.1.0/tests/unit/test_defaults.py +628 -0
- shockharness-0.1.0/tests/unit/test_discovery.py +110 -0
- shockharness-0.1.0/tests/unit/test_evals.py +136 -0
- shockharness-0.1.0/tests/unit/test_exceptions.py +478 -0
- shockharness-0.1.0/tests/unit/test_gate_and_report.py +247 -0
- shockharness-0.1.0/tests/unit/test_globs.py +76 -0
- shockharness-0.1.0/tests/unit/test_golden_fixtures.py +49 -0
- shockharness-0.1.0/tests/unit/test_history.py +274 -0
- shockharness-0.1.0/tests/unit/test_javascript_adapter.py +311 -0
- shockharness-0.1.0/tests/unit/test_magic001.py +109 -0
- shockharness-0.1.0/tests/unit/test_profiles.py +249 -0
- shockharness-0.1.0/tests/unit/test_python_adapter.py +280 -0
- shockharness-0.1.0/tests/unit/test_registry.py +83 -0
- shockharness-0.1.0/tests/unit/test_sarif.py +231 -0
- shockharness-0.1.0/tests/unit/test_schema.py +82 -0
- shockharness-0.1.0/tests/unit/test_skills.py +560 -0
- shockharness-0.1.0/tests/unit/test_source.py +38 -0
- shockharness-0.1.0/tests/unit/test_suppressions.py +99 -0
- shockharness-0.1.0/tests/unit/test_workflows.py +111 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# ShockHarness analyzing itself. Every exemption below is deliberate and reported.
|
|
2
|
+
# See agent_code_integrity_project_spec.md section 15.5 for the bootstrapping policy.
|
|
3
|
+
|
|
4
|
+
[tool.shockharness]
|
|
5
|
+
profile = "strict"
|
|
6
|
+
include = ["src/**/*.py", "tests/**/*.py", "scripts/**/*.py", "evals/*.py"]
|
|
7
|
+
exclude = ["**/.venv/**", "**/node_modules/**", "**/dist/**", "**/build/**", "tests/fixtures/**", "evals/tasks/**"]
|
|
8
|
+
parse_errors = "fail"
|
|
9
|
+
|
|
10
|
+
[tool.shockharness.rules.magic_numbers]
|
|
11
|
+
enabled = true
|
|
12
|
+
allowed_values = [0, 1]
|
|
13
|
+
allow_unary_negative_one = false
|
|
14
|
+
exclude_configuration_files = false
|
|
15
|
+
|
|
16
|
+
[tool.shockharness.rules.exceptions]
|
|
17
|
+
enabled = true
|
|
18
|
+
mode = "boundary_whitelist"
|
|
19
|
+
|
|
20
|
+
# Declared boundaries where the tool meets the outside world. Each converts the
|
|
21
|
+
# named failure into a typed analyzer diagnostic; none may swallow it.
|
|
22
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
23
|
+
path = "src/shockharness/config.py"
|
|
24
|
+
language = "python"
|
|
25
|
+
exception = "OSError"
|
|
26
|
+
reason = "Reading a configuration file is I/O; failure becomes a ConfigError (exit 2)"
|
|
27
|
+
|
|
28
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
29
|
+
path = "src/shockharness/config.py"
|
|
30
|
+
language = "python"
|
|
31
|
+
exception = "tomllib.TOMLDecodeError"
|
|
32
|
+
reason = "Config file content is external input; decode failure becomes a ConfigError (exit 2)"
|
|
33
|
+
|
|
34
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
35
|
+
path = "src/shockharness/config.py"
|
|
36
|
+
language = "python"
|
|
37
|
+
exception = "UnicodeDecodeError"
|
|
38
|
+
reason = "Config file bytes are external input; non-UTF-8 becomes a ConfigError (exit 2)"
|
|
39
|
+
|
|
40
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
41
|
+
path = "src/shockharness/analysis.py"
|
|
42
|
+
language = "python"
|
|
43
|
+
exception = "OSError"
|
|
44
|
+
reason = "Reading analyzed files is I/O; failure becomes a per-file read_error status, never a pass"
|
|
45
|
+
|
|
46
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
47
|
+
path = "src/shockharness/analysis.py"
|
|
48
|
+
language = "python"
|
|
49
|
+
exception = "UnicodeDecodeError"
|
|
50
|
+
reason = "Analyzed source is external bytes; undecodable input becomes a decode_error file status"
|
|
51
|
+
|
|
52
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
53
|
+
path = "src/shockharness/ir/python.py"
|
|
54
|
+
language = "python"
|
|
55
|
+
exception = "SyntaxError"
|
|
56
|
+
reason = "ast.parse and tokenize on external source; failure becomes a ParseFailure fact feeding the parse_errors gate"
|
|
57
|
+
|
|
58
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
59
|
+
path = "src/shockharness/ir/python.py"
|
|
60
|
+
language = "python"
|
|
61
|
+
exception = "ValueError"
|
|
62
|
+
reason = "ast.parse raises ValueError for NUL bytes on 3.11 and tokenize raises UnicodeDecodeError (a ValueError) on some CR sequences; both become a ParseFailure"
|
|
63
|
+
|
|
64
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
65
|
+
path = "src/shockharness/ir/python.py"
|
|
66
|
+
language = "python"
|
|
67
|
+
exception = "tokenize.TokenError"
|
|
68
|
+
reason = "Comment extraction tokenizes external source; failure becomes a ParseFailure fact"
|
|
69
|
+
|
|
70
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
71
|
+
path = "src/shockharness/cli.py"
|
|
72
|
+
language = "python"
|
|
73
|
+
exception = "OSError"
|
|
74
|
+
reason = "Writing the report, baseline, or init file to a user path is I/O; failure becomes a UsageError (exit 2)"
|
|
75
|
+
|
|
76
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
77
|
+
path = "src/shockharness/gating/baseline.py"
|
|
78
|
+
language = "python"
|
|
79
|
+
exception = "OSError"
|
|
80
|
+
reason = "Reading a user-supplied baseline file is I/O; failure becomes a UsageError (exit 2)"
|
|
81
|
+
|
|
82
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
83
|
+
path = "src/shockharness/gating/baseline.py"
|
|
84
|
+
language = "python"
|
|
85
|
+
exception = "UnicodeDecodeError"
|
|
86
|
+
reason = "Baseline file bytes are external input; non-UTF-8 becomes a UsageError (exit 2)"
|
|
87
|
+
|
|
88
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
89
|
+
path = "src/shockharness/gating/baseline.py"
|
|
90
|
+
language = "python"
|
|
91
|
+
exception = "json.JSONDecodeError"
|
|
92
|
+
reason = "Baseline file content is external input; malformed JSON becomes a UsageError (exit 2)"
|
|
93
|
+
|
|
94
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
95
|
+
path = "src/shockharness/history/git.py"
|
|
96
|
+
language = "python"
|
|
97
|
+
exception = "subprocess.CalledProcessError"
|
|
98
|
+
reason = "The git subprocess is the outside world; a non-zero exit (not a repository, unknown revision) becomes a GitResult error: diff/gate turn it into a UsageError (exit 2), history metrics into status = unavailable"
|
|
99
|
+
|
|
100
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
101
|
+
path = "src/shockharness/history/git.py"
|
|
102
|
+
language = "python"
|
|
103
|
+
exception = "OSError"
|
|
104
|
+
reason = "Launching git can fail when the binary is absent (FileNotFoundError) or cannot be executed; the failure becomes a GitResult error, never a silent empty history"
|
|
105
|
+
|
|
106
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
107
|
+
path = "src/shockharness/history/git.py"
|
|
108
|
+
language = "python"
|
|
109
|
+
exception = "UnicodeDecodeError"
|
|
110
|
+
reason = "git output is external bytes; undecodable output becomes a GitResult error"
|
|
111
|
+
|
|
112
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
113
|
+
path = "src/shockharness/skills/install.py"
|
|
114
|
+
language = "python"
|
|
115
|
+
exception = "OSError"
|
|
116
|
+
reason = "Reading and writing skill files and the manifest in the user's chosen destination is I/O; failure becomes a UsageError (exit 2)"
|
|
117
|
+
|
|
118
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
119
|
+
path = "src/shockharness/skills/install.py"
|
|
120
|
+
language = "python"
|
|
121
|
+
exception = "UnicodeDecodeError"
|
|
122
|
+
reason = "Skills manifest bytes are external input; non-UTF-8 becomes a UsageError (exit 2)"
|
|
123
|
+
|
|
124
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
125
|
+
path = "src/shockharness/skills/install.py"
|
|
126
|
+
language = "python"
|
|
127
|
+
exception = "json.JSONDecodeError"
|
|
128
|
+
reason = "Skills manifest content is external input; malformed JSON becomes a UsageError (exit 2)"
|
|
129
|
+
|
|
130
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
131
|
+
path = "src/shockharness/cli.py"
|
|
132
|
+
language = "python"
|
|
133
|
+
exception = "shockharness.errors.ShockHarnessError"
|
|
134
|
+
reason = "Process boundary: typed configuration and usage errors print one line and exit 2"
|
|
135
|
+
|
|
136
|
+
[[tool.shockharness.rules.exceptions.boundaries]]
|
|
137
|
+
path = "src/shockharness/cli.py"
|
|
138
|
+
language = "python"
|
|
139
|
+
exception = "Exception"
|
|
140
|
+
reason = "Process boundary: an unexpected internal failure prints its full traceback and exits 3 instead of Python's default 1; nothing is hidden"
|
|
141
|
+
|
|
142
|
+
[tool.shockharness.rules.defaults]
|
|
143
|
+
enabled = true
|
|
144
|
+
function_parameters = "forbid"
|
|
145
|
+
dataclass_fields = "forbid"
|
|
146
|
+
missing_value_fallbacks = "forbid"
|
|
147
|
+
truthiness_fallbacks = "review"
|
|
148
|
+
nullish_fallbacks = "forbid"
|
|
149
|
+
configuration_fallbacks = "forbid"
|
|
150
|
+
|
|
151
|
+
[tool.shockharness.rules.magic_strings]
|
|
152
|
+
enabled = true
|
|
153
|
+
mode = "review"
|
|
154
|
+
|
|
155
|
+
[tool.shockharness.complexity]
|
|
156
|
+
enabled = true
|
|
157
|
+
cognitive_warn = 15
|
|
158
|
+
cyclomatic_warn = 15
|
|
159
|
+
max_nesting_warn = 4
|
|
160
|
+
|
|
161
|
+
[tool.shockharness.gates]
|
|
162
|
+
new_hard_violations = "fail"
|
|
163
|
+
legacy = "baseline"
|
|
164
|
+
complexity_regressions = "warn"
|
|
165
|
+
|
|
166
|
+
[[tool.shockharness.overrides]]
|
|
167
|
+
paths = ["src/shockharness/constants.py"]
|
|
168
|
+
disable = ["MAGIC001"]
|
|
169
|
+
reason = "Single designated module holding every numeric constant the tool needs (exit codes, schema version, caps)"
|
|
170
|
+
|
|
171
|
+
[[tool.shockharness.overrides]]
|
|
172
|
+
paths = ["tests/**"]
|
|
173
|
+
disable = ["MAGIC001", "DEF001"]
|
|
174
|
+
reason = "Test code encodes literal expected outputs and pytest fixture signatures"
|
|
175
|
+
|
|
176
|
+
[[tool.shockharness.overrides]]
|
|
177
|
+
paths = ["evals/run_evals.py"]
|
|
178
|
+
disable = ["MAGIC001"]
|
|
179
|
+
reason = "The eval runner is stdlib-only by design (it scores the analyzer from outside), so it cannot import shockharness.constants and holds its one JSON indent constant itself"
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ShockHarness. Format follows Keep a Changelog; versions
|
|
4
|
+
follow Semantic Versioning. The report `schema_version` is tracked separately.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-10-14
|
|
9
|
+
|
|
10
|
+
First stable release. Every milestone of the specification (A through G) is
|
|
11
|
+
implemented: the three default-on hard policies (`MAGIC001`, `EXC001`-`EXC003`,
|
|
12
|
+
`DEF001`-`DEF006`), inline suppressions, baselines, revision diff and gate,
|
|
13
|
+
SARIF, git history metrics, advisory complexity metrics and call graph
|
|
14
|
+
(`CPLX001`-`CPLX005`), three profiles, the `check` command, `--exit-zero`,
|
|
15
|
+
tag-triggered publishing, a reusable GitHub Actions workflow, consumer
|
|
16
|
+
examples, four agent skills with `skills install`/`update`, and the eval
|
|
17
|
+
corpus. From this release the CLI surface, exit codes, rule IDs and report
|
|
18
|
+
`schema_version` 1 are stable; changes to them are documented here first.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Skills: `SKILL.md` content is normalized to LF when read from the package, so
|
|
22
|
+
frontmatter checks, installs and manifest hashes are identical on checkouts
|
|
23
|
+
that use CRLF (Windows runners with `core.autocrlf`). A `.gitattributes` now
|
|
24
|
+
pins LF on checkout for every text file in the repository.
|
|
25
|
+
- History metrics: commit dates are now normalized to one ISO 8601 spelling
|
|
26
|
+
(`+00:00`) regardless of the host's git version. Git 2.45+ prints UTC as `Z`,
|
|
27
|
+
which made `metrics.history` differ between machines and broke two tests on
|
|
28
|
+
CI runners with newer git than the development machine.
|
|
29
|
+
- Dependabot no longer proposes minor or major `tree-sitter` bumps; the 0.25
|
|
30
|
+
pin is deliberate (0.26.0 segfaults on CPython 3.14).
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- Milestone E: complexity metrics, call graph, hotspots. `CPLX001`-`CPLX005`
|
|
34
|
+
are implemented as advisory warnings that never fail a plain `check`. Every
|
|
35
|
+
module body, class body, function, method, lambda and arrow is a *unit*
|
|
36
|
+
measured on its own (nested units are excluded from their parent) with
|
|
37
|
+
versioned conventions stated in every message and in
|
|
38
|
+
`metrics.complexity.conventions`: `cyclomatic/1` (boolean operators counted
|
|
39
|
+
under `complexity.cyclomatic_counts_boolean_operators`, optional chaining
|
|
40
|
+
never), `cognitive/1` (published-style nesting and interruption increments,
|
|
41
|
+
explicitly not a SonarSource score, Python/JavaScript parity enforced by a
|
|
42
|
+
table and a Hypothesis property), `nesting/1`, `npath/1` (bounded by
|
|
43
|
+
`complexity.npath_cap`, reported as `>= cap` when saturated, never
|
|
44
|
+
enumerated) and `size/1`. New keys `npath_cap`, `npath_warn`, `fan_out_warn`,
|
|
45
|
+
`cyclomatic_counts_boolean_operators`, `abstraction_displacement`;
|
|
46
|
+
`cyclomatic_counts_boolean_operators = false` is a reported deviation.
|
|
47
|
+
`docs/complexity.md` defines everything.
|
|
48
|
+
- `metrics.graph`: a call graph whose edges are `resolved` (direct call of a
|
|
49
|
+
module-level, nested or imported definition), `probable` (module alias
|
|
50
|
+
attribute, `self`/`this` method the class defines, static class call,
|
|
51
|
+
function passed as an argument) or `unresolved` (computed callee, unknown
|
|
52
|
+
receiver, unbound name, undefined `self` attribute); counts per confidence,
|
|
53
|
+
fan-in/fan-out per unit (builtins excluded, unresolved never counted),
|
|
54
|
+
module import edges for Python (absolute and relative, canonical
|
|
55
|
+
package-rooted names, never suffix matching) and JavaScript (`import`,
|
|
56
|
+
`export ... from`, `export *`, `require`), strongly connected components
|
|
57
|
+
as `cycles`, and `shared_mutable_state` notes. `CPLX005` fires per cycle
|
|
58
|
+
member and on fan-out above `fan_out_warn`.
|
|
59
|
+
- `metrics.history.hotspots`: `log1p(churn) * normalized_complexity` per file
|
|
60
|
+
(summed cognitive complexity over the repository maximum), with
|
|
61
|
+
`hotspots_formula`; `null` with a reason when history or complexity is
|
|
62
|
+
unavailable.
|
|
63
|
+
- Revision diffs list `metrics.complexity.regressions` (head units above a
|
|
64
|
+
threshold whose value grew since base) and honour
|
|
65
|
+
`gates.complexity_regressions` (`warn` default, `fail`, `ignore`);
|
|
66
|
+
`gate.complexity_regressions` and `summary.complexity_regressions` are new
|
|
67
|
+
fields. `complexity.abstraction_displacement = true` adds the experimental
|
|
68
|
+
`metrics.graph.abstraction_displacement` (every entry approximate).
|
|
69
|
+
- The analysis pipeline now has a cross-file phase between the per-file rules
|
|
70
|
+
and suppressions, so graph findings are suppressible and fingerprinted;
|
|
71
|
+
`build_report` takes the metrics, `collect_history` takes the complexity
|
|
72
|
+
section, `gate.evaluate` takes the regression count. Report and diff schemas
|
|
73
|
+
gain the new sections (additive); SARIF carries CPLX results.
|
|
74
|
+
- The `refactor-complexity` skill now quotes the real rules, thresholds,
|
|
75
|
+
report fields and commands.
|
|
76
|
+
- Milestone G: four vendor-neutral agent skills shipped as package data
|
|
77
|
+
(`shockharness/skills/<name>/SKILL.md`, open Agent Skills format with
|
|
78
|
+
`name`/`description` frontmatter): `write-compliant-code`,
|
|
79
|
+
`fix-violations`, `refactor-complexity`, `pre-commit-review`. Each has
|
|
80
|
+
compliant and non-compliant Python and JavaScript examples, exact CLI
|
|
81
|
+
invocations, a corrective workflow, a checklist, and an "Adopting
|
|
82
|
+
ShockHarness" section identical to the README's (enforced by a test).
|
|
83
|
+
`refactor-complexity` states that `CPLX001`-`CPLX005` are not implemented.
|
|
84
|
+
- `shockharness skills list|show|install|update`. `install --agent
|
|
85
|
+
claude|codex|generic [--project PATH] [--user] [--force]` writes the skills
|
|
86
|
+
(claude: `.claude/skills`, codex: `.agents/skills`, generic: `--project DIR`
|
|
87
|
+
itself plus a printed `AGENTS.md` pointer line) and a manifest
|
|
88
|
+
`.shockharness-skills.json` (schema version 1: agent, package version,
|
|
89
|
+
SHA-256 per file); it refuses to overwrite without `--force` (exit 2) and
|
|
90
|
+
touches nothing outside the destination. `update` rewrites files whose hash
|
|
91
|
+
still equals the manifest, restores missing ones, adds new skills, reports
|
|
92
|
+
removed ones, leaves edited files alone and reports each as a conflict with
|
|
93
|
+
the remediation (exit 1 while a conflict remains); `update --check` writes
|
|
94
|
+
nothing and exits 1 on any drift. Three new declared exception boundaries in
|
|
95
|
+
`skills/install.py` (`OSError`, `UnicodeDecodeError`,
|
|
96
|
+
`json.JSONDecodeError`).
|
|
97
|
+
- Skill evaluation corpus under `evals/`: eight paired tasks (four Python,
|
|
98
|
+
four JavaScript) with prompt, starting files, behavioral tests, expected
|
|
99
|
+
outcome and a clean reference solution; `evals/run_evals.py` (standard
|
|
100
|
+
library only, one declared `MAGIC001` override) scores candidate directories
|
|
101
|
+
by unsuppressed findings per rule, test exit code and suppressions added,
|
|
102
|
+
and writes a JSON scorecard. CI scores the reference solutions and
|
|
103
|
+
round-trips `skills install` / `update --check` for all three agents on
|
|
104
|
+
ubuntu. The sdist carries `evals/`.
|
|
105
|
+
- README "Adopting ShockHarness": a self-contained guide for an agent reading
|
|
106
|
+
the README over HTTP (pinned `uv tool install` / `uvx` / `pip` forms,
|
|
107
|
+
`init`, the four onboarding paths, direct and reusable CI steps with the
|
|
108
|
+
Dependabot snippet, `skills install`, and a verification table), with
|
|
109
|
+
absolute links only. `docs/skills.md` is new; `docs/integration.md` gains a
|
|
110
|
+
skills section.
|
|
111
|
+
- `--exit-zero` on `check` (and the `scan` alias) and `gate`: an advisory
|
|
112
|
+
run. The report, severities, gate verdict text and every count are
|
|
113
|
+
unchanged; only the process exit code is 0 where the verdict would have
|
|
114
|
+
returned 1. Exit 2 (configuration, usage) and exit 3 (tool failure, strict
|
|
115
|
+
parse failure) are never masked. The run is visible as `gate.advisory`
|
|
116
|
+
(JSON and diff reports, both schemas, additive), a `shockharness: advisory
|
|
117
|
+
run (--exit-zero): gate verdict FAIL reported as exit 0` line in text, and
|
|
118
|
+
the `advisory` SARIF run property; the SARIF invocation `exitCode` is the
|
|
119
|
+
process exit code while `properties.gate.exit_code` keeps the verdict's.
|
|
120
|
+
Deliberately a CLI-only flag, not a configuration key: the configuration
|
|
121
|
+
form is `gates.new_hard_violations = "warn"`. `docs/configuration.md` gains
|
|
122
|
+
"Adopting ShockHarness on an existing codebase" comparing the four onboarding
|
|
123
|
+
paths (baseline, `gates.new_hard_violations = "warn"`, `profile =
|
|
124
|
+
"minimal"`, `--exit-zero`).
|
|
125
|
+
- Milestone F (distribution half). `shockharness check` is the canonical
|
|
126
|
+
analysis command (identical arguments and behaviour to `scan`, which stays as
|
|
127
|
+
a hidden alias); `shockharness version` prints the same string as
|
|
128
|
+
`--version`.
|
|
129
|
+
- Built-in profiles shipped as package data (`shockharness/profiles/*.toml`),
|
|
130
|
+
selected with `profile = "strict" | "recommended" | "minimal"`: `strict` is
|
|
131
|
+
the bundled defaults; `recommended` reports `DEF003`/`DEF004` at review;
|
|
132
|
+
`minimal` caps `MAGIC001`, `EXC001`-`EXC003` and `DEF001`-`DEF006` at
|
|
133
|
+
review, sets `parse_errors = "warn"` and disables complexity. Precedence:
|
|
134
|
+
bundled defaults < profile < repository configuration < command-line
|
|
135
|
+
overrides. Every profile below `strict` is a policy deviation in every
|
|
136
|
+
report and in `config validate`; `config effective` records profile keys
|
|
137
|
+
with origin `profile:<name>`.
|
|
138
|
+
- `rules.magic_numbers.severity`, `rules.exceptions.severity`,
|
|
139
|
+
`rules.defaults.severity` (`"error"` | `"review"`): a per-section cap that
|
|
140
|
+
turns error findings into review findings; each cap is a reported deviation.
|
|
141
|
+
- The only two command-line overrides, on every configuration-loading command:
|
|
142
|
+
`--profile NAME` (replaces the profile layer; the repository's other keys
|
|
143
|
+
still apply) and `--parse-errors fail|warn`. Both are recorded with origin
|
|
144
|
+
`cli_override`, noted in the report, and part of the configuration digest.
|
|
145
|
+
- `shockharness.toml` is accepted alongside `.shockharness.toml` at the
|
|
146
|
+
dedicated tier; both present in one root is a configuration error naming
|
|
147
|
+
both files.
|
|
148
|
+
- `init --profile recommended|minimal` writes a short file selecting the profile.
|
|
149
|
+
- Release automation: `.github/workflows/publish.yml` (tag `v*` -> full CI
|
|
150
|
+
workflow -> build wheel and sdist -> tag equals `version.py` -> PyPI trusted
|
|
151
|
+
publishing with `id-token: write`, environment `pypi`, no stored token);
|
|
152
|
+
`docs/releasing.md` with the one-time PyPI steps and the per-release steps.
|
|
153
|
+
- Reusable consumer workflow `.github/workflows/check.yml` (`workflow_call`;
|
|
154
|
+
inputs `shockharness-version` (required), `config`, `base`,
|
|
155
|
+
`python-version`; SARIF upload to code scanning; permissions
|
|
156
|
+
`contents: read`, `security-events: write` only); `.github/dependabot.yml`
|
|
157
|
+
for this repository; `docs/integration.md` (pinning to a tag and to a SHA,
|
|
158
|
+
Dependabot snippet, where the pin may live).
|
|
159
|
+
- Consumer examples `examples/python` (dev dependency pinned to `0.1.0a1`)
|
|
160
|
+
and `examples/js` (analyzer in an isolated tool environment, never an npm
|
|
161
|
+
dependency), each with a policy file, a workflow and one deliberate
|
|
162
|
+
suppressed finding; `scripts/consumer_upgrade_check.py` (stdlib only)
|
|
163
|
+
copies each into a throwaway git repository, installs the wheel built from
|
|
164
|
+
the checkout, and asserts `check` exits 0, then 1 after one injected
|
|
165
|
+
violation, and `gate --base HEAD~1 --head HEAD` exits 1; run by CI on ubuntu.
|
|
166
|
+
- `effective_profile` is also in SARIF run properties, next to
|
|
167
|
+
`config_deviations`.
|
|
168
|
+
- Milestone F (analysis half): stable finding fingerprints (`findings[].fingerprint`;
|
|
169
|
+
rule ID, path, enclosing scope, whitespace-free evidence, occurrence index)
|
|
170
|
+
that survive line moves and reformatting; `shockharness baseline create PATH...
|
|
171
|
+
--output FILE` writing a reviewed, byte-stable baseline
|
|
172
|
+
(`schemas/baseline-v1.schema.json`) that is never updated automatically;
|
|
173
|
+
`--baseline FILE` on `scan` and `gate` with `gates.legacy` semantics
|
|
174
|
+
(`baseline`: matched findings listed and counted but not gating; `ignore`:
|
|
175
|
+
listed, not counted; `fail`: baseline recorded but not applied); stale
|
|
176
|
+
baseline entries reported under `gate.baseline.stale`; `evaluate_gate` and
|
|
177
|
+
`apply_baseline` in the library API.
|
|
178
|
+
- `shockharness diff --base REF --head REF` (reporting only) and
|
|
179
|
+
`shockharness gate --base REF --head REF` (exit 1 only on new hard
|
|
180
|
+
violations): both revisions resolved and exported with local git only, full
|
|
181
|
+
trees analyzed, findings classified new / fixed / unchanged by fingerprint,
|
|
182
|
+
provenance (both commits, config digest, tool version) in the report
|
|
183
|
+
(`schemas/diff-v1.schema.json`).
|
|
184
|
+
- SARIF 2.1.0 output (`--format sarif`) for `scan`, `diff`, and `gate`: one
|
|
185
|
+
run, every registered rule with full descriptions, results with level mapping
|
|
186
|
+
(error, warning, review/note -> note), root-relative URIs, exclusive end
|
|
187
|
+
columns, `partialFingerprints` carrying the baseline fingerprint, in-source
|
|
188
|
+
suppressions, and `baselineState` when a baseline or base revision applies.
|
|
189
|
+
- `metrics.history` in every CLI report: per-file commit count, author count,
|
|
190
|
+
first and last change, and co-change pairs with support and directional
|
|
191
|
+
confidence, read from local `git log` over `history.horizon_days` anchored
|
|
192
|
+
on the head commit date; `history.enabled`, `history.horizon_days`,
|
|
193
|
+
`history.min_support` configuration keys; explicit `unavailable`,
|
|
194
|
+
`disabled`, and `not_collected` statuses with reasons; `hotspots` is `null`
|
|
195
|
+
with a reason until Milestone E provides complexity.
|
|
196
|
+
- Six declared exception boundaries: reading a baseline file (`OSError`,
|
|
197
|
+
`UnicodeDecodeError`, `json.JSONDecodeError`) and the git subprocess
|
|
198
|
+
(`subprocess.CalledProcessError`, `OSError`, `UnicodeDecodeError`).
|
|
199
|
+
|
|
200
|
+
### Changed
|
|
201
|
+
- **`policy_profile` is renamed to `effective_profile`** in the JSON report,
|
|
202
|
+
the diff report, both JSON schemas and SARIF run properties (specification
|
|
203
|
+
section 20, decision 13). `schema_version` stays `"1"`: no tagged release
|
|
204
|
+
has shipped the old field (0.1.0a1 was published only to claim the name and
|
|
205
|
+
predates the schema's first tagged release), so the rename is made before
|
|
206
|
+
schema v1 is published rather than versioned around.
|
|
207
|
+
- The text report's `config:` line names the effective profile; `config
|
|
208
|
+
validate` prints it.
|
|
209
|
+
- CI, the pre-commit hook, the README and the docs invoke `shockharness
|
|
210
|
+
check`; the self-gate and Ruff also cover `scripts/`.
|
|
211
|
+
- Report schema (still `schema_version` 1, additive): `findings[].fingerprint`,
|
|
212
|
+
`findings[].baseline`, `gate.baselined`, `gate.new_hard_violations`,
|
|
213
|
+
`gate.baseline`, `execution_summary.baselined`, `metrics.history`. The gate
|
|
214
|
+
verdict is driven by `new_hard_violations`; without a baseline it equals
|
|
215
|
+
`hard_violations` as before.
|
|
216
|
+
- `--format sarif` no longer exits 2; `evaluate_gate` no longer raises
|
|
217
|
+
`UnsupportedFeatureError` for a baseline (it rejects anything that is not a
|
|
218
|
+
`Baseline` with a `UsageError`).
|
|
219
|
+
|
|
220
|
+
## [0.1.0a1] - 2026-10-14
|
|
221
|
+
|
|
222
|
+
First public alpha, published to PyPI to claim the `shockharness` name.
|
|
223
|
+
Contains Milestones A through D: MAGIC001, EXC001-EXC003, DEF001-DEF006,
|
|
224
|
+
SUPP001/SUPP002, configuration validation, text and JSON reports, and the
|
|
225
|
+
`scan`, `config`, `rules`, and `init` commands. No baselines, diff, SARIF,
|
|
226
|
+
complexity metrics, or skills yet; see `coverage.rules_not_implemented` in
|
|
227
|
+
every report.
|
|
228
|
+
|
|
229
|
+
Everything listed under "Added" below shipped in this version.
|
|
230
|
+
|
|
231
|
+
### Added
|
|
232
|
+
- Milestone A: package skeleton, MIT license, configuration schema with full
|
|
233
|
+
shape and semantic validation, precedence (`--config` > `.shockharness.toml` >
|
|
234
|
+
`pyproject.toml` > bundled defaults), rule registry with every specified rule
|
|
235
|
+
ID, diagnostic and report models, `scan`, `config validate`, `config effective`,
|
|
236
|
+
`rules list`, `rules explain`, `init`, exit-code contract, GitHub Actions matrix.
|
|
237
|
+
- Milestone B: Python (`ast`) and JavaScript (tree-sitter) adapters with
|
|
238
|
+
byte-to-character column mapping, explicit parse-failure propagation,
|
|
239
|
+
`MAGIC001` (value-based 0/1, folded unary sign, annotations flagged),
|
|
240
|
+
`SUPP001`/`SUPP002` inline suppression checks, per-path overrides,
|
|
241
|
+
configuration-file exemption, JSON report (`schema_version` 1) and text output.
|
|
242
|
+
- Milestone C: catch facts in the IR (exception identities with
|
|
243
|
+
exact/approximate/unresolved confidence, handler body classification,
|
|
244
|
+
try-body operation attribution) for Python `except`/`except*` and JavaScript
|
|
245
|
+
`catch`, `.catch(handler)`, `.then(ok, handler)`; `EXC001` forbidden catch
|
|
246
|
+
(boundary whitelist, fail-closed on bare/unresolved identities, JavaScript
|
|
247
|
+
instanceof-guard convention), `EXC002` suppressed or converted exception,
|
|
248
|
+
`EXC003` broad try scope; `rules.exceptions.transparent_rethrow` (default
|
|
249
|
+
false); boundary `exception` names validated as dotted class names; every
|
|
250
|
+
boundary and `transparent_rethrow` reported under `config.deviations`.
|
|
251
|
+
- Milestone D: default facts in the IR (`DefaultFact`: family, kind, subject,
|
|
252
|
+
fallback, constant-shapedness, syntactic context, confidence) extracted by
|
|
253
|
+
`ir/python_defaults.py` and `ir/javascript_defaults.py`; `DEF001` parameter
|
|
254
|
+
and dataclass-field defaults, `DEF002` mapping/attribute/property fallbacks
|
|
255
|
+
(plus the implicit `None` of `.get(key)` as a review finding), `DEF003`
|
|
256
|
+
truthiness fallbacks (review by default; Boolean logic excluded), `DEF004`
|
|
257
|
+
nullish fallbacks, `DEF005` exception-to-default (literal returns and
|
|
258
|
+
one-step static traces to module constants and one-return helpers), `DEF006`
|
|
259
|
+
configuration fallbacks (`os.getenv`/`os.environ`/`process.env` in every
|
|
260
|
+
spelling); new `rules.defaults` keys `dataclass_fields`, `nullish_fallbacks`,
|
|
261
|
+
`configuration_fallbacks` (all `forbid`); loosened modes reported under
|
|
262
|
+
`config.deviations`; 13 golden fixtures and 25 unit tests; `safe_autofix` is
|
|
263
|
+
false for every finding.
|
|
264
|
+
|
|
265
|
+
### Changed
|
|
266
|
+
- `EXC002` no longer reports the `return_fallback` classification while `DEF005`
|
|
267
|
+
is active: the handler is reported once, as `DEF005`, with the EXC002
|
|
268
|
+
classification named in the message. Disabling `DEF005` restores the EXC002
|
|
269
|
+
finding. Golden expectations for the eleven affected handlers changed rule ID
|
|
270
|
+
only.
|
|
271
|
+
- The tool's own code was brought under `DEF002`/`DEF004`: three `setdefault`
|
|
272
|
+
accumulators became explicit membership checks, the report's
|
|
273
|
+
`discovery`/`explicit_targets_excluded` fields are built with an explicit
|
|
274
|
+
branch, and nineteen `x if x is not None else v` expressions on parser fields
|
|
275
|
+
became either explicit branches (genuinely optional values: import aliases,
|
|
276
|
+
`from . import`, source segments) or assertions of grammar invariants (fields
|
|
277
|
+
tree-sitter and `ast` always populate). A Python `SyntaxError` position is now
|
|
278
|
+
recorded only when the interpreter reports both a line and a column; before,
|
|
279
|
+
a missing column was silently replaced by column 1.
|
|
280
|
+
- The arities that distinguish `getattr(o, a, v)` and `m.get(k, v)` from plain
|
|
281
|
+
lookups are named in `constants.py`.
|
|
282
|
+
- The tool's own boundaries were narrowed to satisfy `EXC003`: config decoding
|
|
283
|
+
and TOML parsing are two `try` blocks, analyzed-file decoding is bound before
|
|
284
|
+
the result is returned, and comment tokenization materializes the token list
|
|
285
|
+
in the one protected call. Behaviour is unchanged.
|
|
286
|
+
|
|
287
|
+
### Dependencies
|
|
288
|
+
- `tree-sitter` 0.25.x (MIT) and `tree-sitter-javascript` 0.25.x (MIT). Pinned
|
|
289
|
+
as a tested pair; language ABI 15. `tree-sitter` 0.26.0 is excluded: with
|
|
290
|
+
CPython 3.14 it segfaults when walking trees nested deeper than about 200
|
|
291
|
+
levels (CPython 3.13 with 0.26.0 and CPython 3.14 with 0.25.2 are both fine).
|
|
292
|
+
The 300-deep nesting test in `tests/unit/test_javascript_adapter.py` guards this.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thomas Lahore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|