@ahmed-g-gad/apothem 0.1.1
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.
- package/CHANGELOG.md +60 -0
- package/LICENSE +21 -0
- package/LICENSES/MIT.txt +18 -0
- package/LICENSES/PSF-2.0.txt +47 -0
- package/README.md +549 -0
- package/bin/README.md +37 -0
- package/bin/apothem.mjs +78 -0
- package/package.json +75 -0
- package/pyproject.toml +347 -0
- package/src/apothem/README.md +52 -0
- package/src/apothem/__init__.py +66 -0
- package/src/apothem/__main__.py +28 -0
- package/src/apothem/_vendor/.keep +0 -0
- package/src/apothem/_vendor/__init__.py +25 -0
- package/src/apothem/_vendor/attr/__init__.py +104 -0
- package/src/apothem/_vendor/attr/__init__.pyi +389 -0
- package/src/apothem/_vendor/attr/_cmp.py +160 -0
- package/src/apothem/_vendor/attr/_cmp.pyi +13 -0
- package/src/apothem/_vendor/attr/_compat.py +99 -0
- package/src/apothem/_vendor/attr/_config.py +31 -0
- package/src/apothem/_vendor/attr/_funcs.py +497 -0
- package/src/apothem/_vendor/attr/_make.py +3406 -0
- package/src/apothem/_vendor/attr/_next_gen.py +674 -0
- package/src/apothem/_vendor/attr/_typing_compat.pyi +15 -0
- package/src/apothem/_vendor/attr/_version_info.py +89 -0
- package/src/apothem/_vendor/attr/_version_info.pyi +9 -0
- package/src/apothem/_vendor/attr/converters.py +162 -0
- package/src/apothem/_vendor/attr/converters.pyi +19 -0
- package/src/apothem/_vendor/attr/exceptions.py +95 -0
- package/src/apothem/_vendor/attr/exceptions.pyi +17 -0
- package/src/apothem/_vendor/attr/filters.py +72 -0
- package/src/apothem/_vendor/attr/filters.pyi +6 -0
- package/src/apothem/_vendor/attr/py.typed +0 -0
- package/src/apothem/_vendor/attr/setters.py +79 -0
- package/src/apothem/_vendor/attr/setters.pyi +20 -0
- package/src/apothem/_vendor/attr/validators.py +750 -0
- package/src/apothem/_vendor/attr/validators.pyi +140 -0
- package/src/apothem/_vendor/attr.LICENSE +21 -0
- package/src/apothem/_vendor/attrs/__init__.py +72 -0
- package/src/apothem/_vendor/attrs/__init__.pyi +314 -0
- package/src/apothem/_vendor/attrs/converters.py +3 -0
- package/src/apothem/_vendor/attrs/exceptions.py +3 -0
- package/src/apothem/_vendor/attrs/filters.py +3 -0
- package/src/apothem/_vendor/attrs/py.typed +0 -0
- package/src/apothem/_vendor/attrs/setters.py +3 -0
- package/src/apothem/_vendor/attrs/validators.py +3 -0
- package/src/apothem/_vendor/attrs.LICENSE +21 -0
- package/src/apothem/_vendor/jsonschema/__init__.py +120 -0
- package/src/apothem/_vendor/jsonschema/__main__.py +6 -0
- package/src/apothem/_vendor/jsonschema/_format.py +546 -0
- package/src/apothem/_vendor/jsonschema/_keywords.py +449 -0
- package/src/apothem/_vendor/jsonschema/_legacy_keywords.py +449 -0
- package/src/apothem/_vendor/jsonschema/_types.py +204 -0
- package/src/apothem/_vendor/jsonschema/_typing.py +29 -0
- package/src/apothem/_vendor/jsonschema/_utils.py +355 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/__init__.py +5 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/const_vs_enum.py +30 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/contains.py +28 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/import_benchmark.py +31 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/issue232/issue.json +2653 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/issue232.py +25 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/json_schema_test_suite.py +12 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/nested_schemas.py +56 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/subcomponents.py +42 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/unused_registry.py +35 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/useless_applicator_schemas.py +106 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/useless_keywords.py +32 -0
- package/src/apothem/_vendor/jsonschema/benchmarks/validator_creation.py +14 -0
- package/src/apothem/_vendor/jsonschema/cli.py +292 -0
- package/src/apothem/_vendor/jsonschema/exceptions.py +490 -0
- package/src/apothem/_vendor/jsonschema/protocols.py +230 -0
- package/src/apothem/_vendor/jsonschema/validators.py +1410 -0
- package/src/apothem/_vendor/jsonschema.LICENSE +19 -0
- package/src/apothem/_vendor/jsonschema_specifications/__init__.py +12 -0
- package/src/apothem/_vendor/jsonschema_specifications/_core.py +38 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/metaschema.json +42 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/applicator +56 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/content +17 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/core +57 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/format +14 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data +37 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/validation +98 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/metaschema.json +58 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/applicator +48 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/content +17 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/core +51 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation +14 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion +14 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data +37 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated +15 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/validation +98 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft3/metaschema.json +172 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft4/metaschema.json +149 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft6/metaschema.json +153 -0
- package/src/apothem/_vendor/jsonschema_specifications/schemas/draft7/metaschema.json +166 -0
- package/src/apothem/_vendor/jsonschema_specifications.LICENSE +19 -0
- package/src/apothem/_vendor/referencing/__init__.py +7 -0
- package/src/apothem/_vendor/referencing/_attrs.py +31 -0
- package/src/apothem/_vendor/referencing/_attrs.pyi +21 -0
- package/src/apothem/_vendor/referencing/_core.py +739 -0
- package/src/apothem/_vendor/referencing/exceptions.py +165 -0
- package/src/apothem/_vendor/referencing/jsonschema.py +642 -0
- package/src/apothem/_vendor/referencing/py.typed +0 -0
- package/src/apothem/_vendor/referencing/retrieval.py +94 -0
- package/src/apothem/_vendor/referencing/typing.py +61 -0
- package/src/apothem/_vendor/referencing.LICENSE +19 -0
- package/src/apothem/_vendor/rpds/__init__.py +251 -0
- package/src/apothem/_vendor/typing_extensions.LICENSE +279 -0
- package/src/apothem/_vendor/typing_extensions.py +4317 -0
- package/src/apothem/_vendor/vendor.txt +22 -0
- package/src/apothem/_vendor/yaml/__init__.py +389 -0
- package/src/apothem/_vendor/yaml/composer.py +138 -0
- package/src/apothem/_vendor/yaml/constructor.py +748 -0
- package/src/apothem/_vendor/yaml/cyaml.py +100 -0
- package/src/apothem/_vendor/yaml/dumper.py +61 -0
- package/src/apothem/_vendor/yaml/emitter.py +1137 -0
- package/src/apothem/_vendor/yaml/error.py +74 -0
- package/src/apothem/_vendor/yaml/events.py +85 -0
- package/src/apothem/_vendor/yaml/loader.py +63 -0
- package/src/apothem/_vendor/yaml/nodes.py +48 -0
- package/src/apothem/_vendor/yaml/parser.py +588 -0
- package/src/apothem/_vendor/yaml/reader.py +185 -0
- package/src/apothem/_vendor/yaml/representer.py +388 -0
- package/src/apothem/_vendor/yaml/resolver.py +226 -0
- package/src/apothem/_vendor/yaml/scanner.py +1435 -0
- package/src/apothem/_vendor/yaml/serializer.py +110 -0
- package/src/apothem/_vendor/yaml/tokens.py +103 -0
- package/src/apothem/_vendor/yaml.LICENSE +20 -0
- package/src/apothem/agents/README.md +60 -0
- package/src/apothem/agents/codebase-explorer.md +91 -0
- package/src/apothem/agents/convention-auditor.md +93 -0
- package/src/apothem/agents/dependency-auditor.md +97 -0
- package/src/apothem/agents/fact-checker.md +84 -0
- package/src/apothem/agents/mcp-builder.md +86 -0
- package/src/apothem/agents/memory-auditor.md +93 -0
- package/src/apothem/agents/prompt-evaluator.md +87 -0
- package/src/apothem/agents/quality-gate.md +103 -0
- package/src/apothem/agents/refactor-surgeon.md +74 -0
- package/src/apothem/agents/research-scout.md +73 -0
- package/src/apothem/agents/security-scanner.md +83 -0
- package/src/apothem/agents/test-runner.md +84 -0
- package/src/apothem/audit/README.md +73 -0
- package/src/apothem/audit/_scan_lib.py +182 -0
- package/src/apothem/audit/analyze_graph.py +260 -0
- package/src/apothem/audit/build_capability_graph.py +607 -0
- package/src/apothem/audit/build_inventory.py +657 -0
- package/src/apothem/audit/build_plans_provenance.py +997 -0
- package/src/apothem/audit/check_links.py +389 -0
- package/src/apothem/audit/classify_artifacts.py +381 -0
- package/src/apothem/audit/deprecated-tokens.txt +10 -0
- package/src/apothem/audit/execute_plans_migration.py +491 -0
- package/src/apothem/audit/known-projects.txt +15 -0
- package/src/apothem/audit/render_capability_index.py +467 -0
- package/src/apothem/audit/render_inventory.py +405 -0
- package/src/apothem/audit/scan_ai_surfaces.py +1125 -0
- package/src/apothem/audit/scan_ai_surfaces_coarse.py +261 -0
- package/src/apothem/audit/scan_drift_features.py +143 -0
- package/src/apothem/audit/scan_frontmatter.py +293 -0
- package/src/apothem/audit/scan_header_coverage.py +1134 -0
- package/src/apothem/audit/scan_plan_leakage.py +540 -0
- package/src/apothem/audit/scan_plans_discipline.py +188 -0
- package/src/apothem/audit/scan_secrets_pii.py +245 -0
- package/src/apothem/audit/scan_stale_tokens.py +296 -0
- package/src/apothem/audit/synthesize_drift.py +205 -0
- package/src/apothem/benchmarks/README.md +33 -0
- package/src/apothem/benchmarks/__init__.py +3 -0
- package/src/apothem/benchmarks/bench_agents.py +63 -0
- package/src/apothem/benchmarks/bench_hooks.py +93 -0
- package/src/apothem/benchmarks/bench_install.py +58 -0
- package/src/apothem/benchmarks/bench_tests.py +93 -0
- package/src/apothem/benchmarks/bench_validate_ecosystem.py +84 -0
- package/src/apothem/cli/README.md +33 -0
- package/src/apothem/cli/__init__.py +229 -0
- package/src/apothem/cli/_cmd_completion.py +88 -0
- package/src/apothem/cli/_cmd_diff.py +181 -0
- package/src/apothem/cli/_cmd_doctor.py +143 -0
- package/src/apothem/cli/_cmd_harnesses.py +167 -0
- package/src/apothem/cli/_cmd_install.py +327 -0
- package/src/apothem/cli/_cmd_migrate_workspace.py +143 -0
- package/src/apothem/cli/_cmd_profile.py +341 -0
- package/src/apothem/cli/_cmd_status.py +180 -0
- package/src/apothem/cli/_cmd_uninstall.py +215 -0
- package/src/apothem/cli/_cmd_update.py +397 -0
- package/src/apothem/cli/_cmd_verify.py +194 -0
- package/src/apothem/cli/_common_flags.py +90 -0
- package/src/apothem/cli/_epilogs.py +296 -0
- package/src/apothem/cli/_helpers.py +857 -0
- package/src/apothem/cli/_json_formatter.py +21 -0
- package/src/apothem/cli/_materialize.py +376 -0
- package/src/apothem/cli/completions/apothem.bash +30 -0
- package/src/apothem/cli/completions/apothem.fish +19 -0
- package/src/apothem/cli/completions/apothem.ps1 +27 -0
- package/src/apothem/cli/completions/apothem.zsh +42 -0
- package/src/apothem/cli/reference_export.py +126 -0
- package/src/apothem/commands/README.md +125 -0
- package/src/apothem/commands/a11y-audit.md +203 -0
- package/src/apothem/commands/architecture-review.md +194 -0
- package/src/apothem/commands/audit.md +165 -0
- package/src/apothem/commands/code-audit.md +218 -0
- package/src/apothem/commands/code-review.md +193 -0
- package/src/apothem/commands/dependency-audit.md +209 -0
- package/src/apothem/commands/docs-review.md +199 -0
- package/src/apothem/commands/elevate.md +285 -0
- package/src/apothem/commands/eval.md +149 -0
- package/src/apothem/commands/fortress.md +172 -0
- package/src/apothem/commands/freshify.md +168 -0
- package/src/apothem/commands/github-deploy-fresh.md +178 -0
- package/src/apothem/commands/github-deploy-next.md +167 -0
- package/src/apothem/commands/perf-audit.md +198 -0
- package/src/apothem/commands/plan-amend.md +104 -0
- package/src/apothem/commands/plan-audit.md +127 -0
- package/src/apothem/commands/plan-design.md +257 -0
- package/src/apothem/commands/plan-execute.md +495 -0
- package/src/apothem/commands/plan-generate.md +351 -0
- package/src/apothem/commands/plan-review.md +555 -0
- package/src/apothem/commands/plan-spec.md +359 -0
- package/src/apothem/commands/plan-status.md +222 -0
- package/src/apothem/commands/plan.md +173 -0
- package/src/apothem/commands/projectify.md +142 -0
- package/src/apothem/commands/release-readiness.md +142 -0
- package/src/apothem/commands/research-analysis.md +241 -0
- package/src/apothem/commands/research-design.md +231 -0
- package/src/apothem/commands/research-disseminate.md +225 -0
- package/src/apothem/commands/research-experiment.md +232 -0
- package/src/apothem/commands/research-ideate.md +213 -0
- package/src/apothem/commands/research-paper.md +252 -0
- package/src/apothem/commands/research-proposal.md +220 -0
- package/src/apothem/commands/research-publish.md +255 -0
- package/src/apothem/commands/research-review.md +251 -0
- package/src/apothem/commands/research-sources.md +266 -0
- package/src/apothem/commands/research-spec.md +255 -0
- package/src/apothem/commands/research-synthesis.md +233 -0
- package/src/apothem/commands/research-theory.md +218 -0
- package/src/apothem/commands/research.md +181 -0
- package/src/apothem/commands/security-audit.md +196 -0
- package/src/apothem/commands/supply-chain-audit.md +192 -0
- package/src/apothem/commands/test-suite.md +146 -0
- package/src/apothem/commands/threat-model-audit.md +199 -0
- package/src/apothem/commands/ux-review.md +202 -0
- package/src/apothem/commands/workflow.md +162 -0
- package/src/apothem/conformity/README.md +173 -0
- package/src/apothem/conformity/__init__.py +1 -0
- package/src/apothem/conformity/_grep_base.py +93 -0
- package/src/apothem/conformity/agent_capability_grep.py +306 -0
- package/src/apothem/conformity/agents_md_coverage_grep.py +382 -0
- package/src/apothem/conformity/agnosticism_grep.py +311 -0
- package/src/apothem/conformity/always_on_budget_grep.py +318 -0
- package/src/apothem/conformity/bare_except_grep.py +115 -0
- package/src/apothem/conformity/binding_reciprocity_grep.py +151 -0
- package/src/apothem/conformity/brand_mark_grep.py +272 -0
- package/src/apothem/conformity/commented_out_code_grep.py +176 -0
- package/src/apothem/conformity/completion_claim_grep.py +169 -0
- package/src/apothem/conformity/conventional_commit_grep.py +319 -0
- package/src/apothem/conformity/copilot_instructions_presence_grep.py +324 -0
- package/src/apothem/conformity/cross_platform_matrix_grep.py +297 -0
- package/src/apothem/conformity/determinism_grep.py +306 -0
- package/src/apothem/conformity/diagram_staleness_grep.py +154 -0
- package/src/apothem/conformity/dynamism_grep.py +284 -0
- package/src/apothem/conformity/editorconfig_presence_grep.py +281 -0
- package/src/apothem/conformity/file_header_grep.py +502 -0
- package/src/apothem/conformity/freshness_token_grep.py +233 -0
- package/src/apothem/conformity/frontmatter_grep.py +274 -0
- package/src/apothem/conformity/frontmatter_value_grep.py +386 -0
- package/src/apothem/conformity/gate.py +1386 -0
- package/src/apothem/conformity/gitattributes_presence_grep.py +238 -0
- package/src/apothem/conformity/harden_runner_grep.py +320 -0
- package/src/apothem/conformity/hedging_grep.py +129 -0
- package/src/apothem/conformity/license_author_consistency_grep.py +204 -0
- package/src/apothem/conformity/link_check.py +327 -0
- package/src/apothem/conformity/magic_number_grep.py +182 -0
- package/src/apothem/conformity/multi_surface_coherence_grep.py +620 -0
- package/src/apothem/conformity/naming_grep.py +224 -0
- package/src/apothem/conformity/no_global_plans_grep.py +339 -0
- package/src/apothem/conformity/no_toplevel_docs_grep.py +120 -0
- package/src/apothem/conformity/oidc_trusted_publishing_grep.py +291 -0
- package/src/apothem/conformity/option_annotation_grep.py +352 -0
- package/src/apothem/conformity/orphan_output_grep.py +206 -0
- package/src/apothem/conformity/permissions_minimum_scope_grep.py +299 -0
- package/src/apothem/conformity/plain_language_grep.py +559 -0
- package/src/apothem/conformity/plan_next_step_consistency_grep.py +450 -0
- package/src/apothem/conformity/plan_suite_structure_grep.py +534 -0
- package/src/apothem/conformity/plans_discipline_language_grep.py +245 -0
- package/src/apothem/conformity/production_ready_pr_grep.py +200 -0
- package/src/apothem/conformity/recommend_next_step_grep.py +250 -0
- package/src/apothem/conformity/redundancy_grep.py +401 -0
- package/src/apothem/conformity/reference_token_grep.py +230 -0
- package/src/apothem/conformity/registry_capability_consistency_grep.py +368 -0
- package/src/apothem/conformity/secret_leak_grep.py +193 -0
- package/src/apothem/conformity/semver_stability_grep.py +358 -0
- package/src/apothem/conformity/smoke_install_grep.py +194 -0
- package/src/apothem/conformity/static_version_grep.py +284 -0
- package/src/apothem/conformity/token_efficiency_grep.py +185 -0
- package/src/apothem/conformity/unpinned_action_grep.py +115 -0
- package/src/apothem/conformity/user_confirm_grep.py +74 -0
- package/src/apothem/conformity/workflow_concurrency_grep.py +283 -0
- package/src/apothem/harnesses/README.md +63 -0
- package/src/apothem/harnesses/__init__.py +16 -0
- package/src/apothem/harnesses/_shared/README.md +36 -0
- package/src/apothem/harnesses/_shared/__init__.py +12 -0
- package/src/apothem/harnesses/_shared/install_driver.py +281 -0
- package/src/apothem/harnesses/_shared/install_driver_apply.py +612 -0
- package/src/apothem/harnesses/_shared/install_driver_backup.py +535 -0
- package/src/apothem/harnesses/_shared/install_driver_converters.py +310 -0
- package/src/apothem/harnesses/_shared/install_driver_lifecycle.py +495 -0
- package/src/apothem/harnesses/_shared/install_driver_materialize.py +675 -0
- package/src/apothem/harnesses/_shared/install_driver_merge.py +656 -0
- package/src/apothem/harnesses/_shared/install_driver_pathsafety.py +137 -0
- package/src/apothem/harnesses/_shared/install_driver_planvalidation.py +240 -0
- package/src/apothem/harnesses/_shared/install_driver_removal.py +366 -0
- package/src/apothem/harnesses/_shared/install_driver_treeops.py +248 -0
- package/src/apothem/harnesses/_shared/install_driver_types.py +330 -0
- package/src/apothem/harnesses/_shared/wrapper_factories.py +448 -0
- package/src/apothem/harnesses/antigravity/STANDARD-CONVENTION-PIN.md +91 -0
- package/src/apothem/harnesses/antigravity/__init__.py +70 -0
- package/src/apothem/harnesses/antigravity/capabilities.yml +40 -0
- package/src/apothem/harnesses/antigravity/install.py +63 -0
- package/src/apothem/harnesses/antigravity/templates/GEMINI.md +40 -0
- package/src/apothem/harnesses/antigravity/templates/plugin.json +5 -0
- package/src/apothem/harnesses/antigravity/uninstall.py +22 -0
- package/src/apothem/harnesses/antigravity/update.py +10 -0
- package/src/apothem/harnesses/antigravity/verify.py +11 -0
- package/src/apothem/harnesses/claude_code/STANDARD-CONVENTION-PIN.md +65 -0
- package/src/apothem/harnesses/claude_code/__init__.py +107 -0
- package/src/apothem/harnesses/claude_code/capabilities.yml +42 -0
- package/src/apothem/harnesses/claude_code/install.py +147 -0
- package/src/apothem/harnesses/claude_code/templates/settings.json +351 -0
- package/src/apothem/harnesses/claude_code/uninstall.py +23 -0
- package/src/apothem/harnesses/claude_code/update.py +10 -0
- package/src/apothem/harnesses/claude_code/verify.py +11 -0
- package/src/apothem/harnesses/codebuddy/STANDARD-CONVENTION-PIN.md +74 -0
- package/src/apothem/harnesses/codebuddy/__init__.py +49 -0
- package/src/apothem/harnesses/codebuddy/capabilities.yml +34 -0
- package/src/apothem/harnesses/codebuddy/install.py +40 -0
- package/src/apothem/harnesses/codebuddy/templates/apothem-rules.md +37 -0
- package/src/apothem/harnesses/codebuddy/uninstall.py +25 -0
- package/src/apothem/harnesses/codebuddy/update.py +10 -0
- package/src/apothem/harnesses/codebuddy/verify.py +11 -0
- package/src/apothem/harnesses/codex/STANDARD-CONVENTION-PIN.md +79 -0
- package/src/apothem/harnesses/codex/__init__.py +72 -0
- package/src/apothem/harnesses/codex/capabilities.yml +40 -0
- package/src/apothem/harnesses/codex/install.py +69 -0
- package/src/apothem/harnesses/codex/templates/AGENTS.md +40 -0
- package/src/apothem/harnesses/codex/templates/hooks.json +127 -0
- package/src/apothem/harnesses/codex/uninstall.py +23 -0
- package/src/apothem/harnesses/codex/update.py +10 -0
- package/src/apothem/harnesses/codex/verify.py +11 -0
- package/src/apothem/harnesses/cursor/STANDARD-CONVENTION-PIN.md +79 -0
- package/src/apothem/harnesses/cursor/__init__.py +48 -0
- package/src/apothem/harnesses/cursor/capabilities.yml +42 -0
- package/src/apothem/harnesses/cursor/install.py +38 -0
- package/src/apothem/harnesses/cursor/templates/apothem-rules.mdc +40 -0
- package/src/apothem/harnesses/cursor/uninstall.py +25 -0
- package/src/apothem/harnesses/cursor/update.py +10 -0
- package/src/apothem/harnesses/cursor/verify.py +11 -0
- package/src/apothem/harnesses/gemini_cli/STANDARD-CONVENTION-PIN.md +102 -0
- package/src/apothem/harnesses/gemini_cli/__init__.py +52 -0
- package/src/apothem/harnesses/gemini_cli/capabilities.yml +43 -0
- package/src/apothem/harnesses/gemini_cli/install.py +43 -0
- package/src/apothem/harnesses/gemini_cli/templates/GEMINI.md +38 -0
- package/src/apothem/harnesses/gemini_cli/uninstall.py +25 -0
- package/src/apothem/harnesses/gemini_cli/update.py +10 -0
- package/src/apothem/harnesses/gemini_cli/verify.py +11 -0
- package/src/apothem/harnesses/github_copilot/STANDARD-CONVENTION-PIN.md +84 -0
- package/src/apothem/harnesses/github_copilot/__init__.py +47 -0
- package/src/apothem/harnesses/github_copilot/capabilities.yml +42 -0
- package/src/apothem/harnesses/github_copilot/install.py +40 -0
- package/src/apothem/harnesses/github_copilot/templates/copilot-instructions.md +33 -0
- package/src/apothem/harnesses/github_copilot/uninstall.py +25 -0
- package/src/apothem/harnesses/github_copilot/update.py +10 -0
- package/src/apothem/harnesses/github_copilot/verify.py +11 -0
- package/src/apothem/harnesses/glm/STANDARD-CONVENTION-PIN.md +77 -0
- package/src/apothem/harnesses/glm/__init__.py +56 -0
- package/src/apothem/harnesses/glm/capabilities.yml +33 -0
- package/src/apothem/harnesses/glm/install.py +45 -0
- package/src/apothem/harnesses/glm/templates/glm.toml +58 -0
- package/src/apothem/harnesses/glm/uninstall.py +25 -0
- package/src/apothem/harnesses/glm/update.py +10 -0
- package/src/apothem/harnesses/glm/verify.py +11 -0
- package/src/apothem/harnesses/hermes/STANDARD-CONVENTION-PIN.md +57 -0
- package/src/apothem/harnesses/hermes/__init__.py +33 -0
- package/src/apothem/harnesses/hermes/capabilities.yml +36 -0
- package/src/apothem/harnesses/hermes/install.py +17 -0
- package/src/apothem/harnesses/hermes/materializer.py +35 -0
- package/src/apothem/harnesses/hermes/uninstall.py +33 -0
- package/src/apothem/harnesses/hermes/update.py +10 -0
- package/src/apothem/harnesses/hermes/verify.py +11 -0
- package/src/apothem/harnesses/kimi_code/STANDARD-CONVENTION-PIN.md +128 -0
- package/src/apothem/harnesses/kimi_code/__init__.py +59 -0
- package/src/apothem/harnesses/kimi_code/capabilities.yml +40 -0
- package/src/apothem/harnesses/kimi_code/install.py +42 -0
- package/src/apothem/harnesses/kimi_code/templates/AGENTS.md +43 -0
- package/src/apothem/harnesses/kimi_code/uninstall.py +27 -0
- package/src/apothem/harnesses/kimi_code/update.py +10 -0
- package/src/apothem/harnesses/kimi_code/verify.py +11 -0
- package/src/apothem/harnesses/kiro/STANDARD-CONVENTION-PIN.md +77 -0
- package/src/apothem/harnesses/kiro/__init__.py +49 -0
- package/src/apothem/harnesses/kiro/capabilities.yml +36 -0
- package/src/apothem/harnesses/kiro/install.py +39 -0
- package/src/apothem/harnesses/kiro/templates/apothem-rules.md +36 -0
- package/src/apothem/harnesses/kiro/uninstall.py +25 -0
- package/src/apothem/harnesses/kiro/update.py +10 -0
- package/src/apothem/harnesses/kiro/verify.py +11 -0
- package/src/apothem/harnesses/open_claw/STANDARD-CONVENTION-PIN.md +62 -0
- package/src/apothem/harnesses/open_claw/__init__.py +35 -0
- package/src/apothem/harnesses/open_claw/capabilities.yml +35 -0
- package/src/apothem/harnesses/open_claw/install.py +17 -0
- package/src/apothem/harnesses/open_claw/materializer.py +36 -0
- package/src/apothem/harnesses/open_claw/uninstall.py +32 -0
- package/src/apothem/harnesses/open_claw/update.py +10 -0
- package/src/apothem/harnesses/open_claw/verify.py +11 -0
- package/src/apothem/harnesses/opencode/STANDARD-CONVENTION-PIN.md +76 -0
- package/src/apothem/harnesses/opencode/__init__.py +35 -0
- package/src/apothem/harnesses/opencode/capabilities.yml +43 -0
- package/src/apothem/harnesses/opencode/install.py +17 -0
- package/src/apothem/harnesses/opencode/materializer.py +31 -0
- package/src/apothem/harnesses/opencode/uninstall.py +34 -0
- package/src/apothem/harnesses/opencode/update.py +10 -0
- package/src/apothem/harnesses/opencode/verify.py +11 -0
- package/src/apothem/harnesses/qwen_code/STANDARD-CONVENTION-PIN.md +87 -0
- package/src/apothem/harnesses/qwen_code/__init__.py +37 -0
- package/src/apothem/harnesses/qwen_code/capabilities.yml +43 -0
- package/src/apothem/harnesses/qwen_code/install.py +19 -0
- package/src/apothem/harnesses/qwen_code/materializer.py +174 -0
- package/src/apothem/harnesses/qwen_code/templates/QWEN.md +30 -0
- package/src/apothem/harnesses/qwen_code/uninstall.py +34 -0
- package/src/apothem/harnesses/qwen_code/update.py +10 -0
- package/src/apothem/harnesses/qwen_code/verify.py +11 -0
- package/src/apothem/harnesses/trae/STANDARD-CONVENTION-PIN.md +70 -0
- package/src/apothem/harnesses/trae/__init__.py +49 -0
- package/src/apothem/harnesses/trae/capabilities.yml +34 -0
- package/src/apothem/harnesses/trae/install.py +38 -0
- package/src/apothem/harnesses/trae/templates/apothem-rules.md +37 -0
- package/src/apothem/harnesses/trae/uninstall.py +25 -0
- package/src/apothem/harnesses/trae/update.py +10 -0
- package/src/apothem/harnesses/trae/verify.py +11 -0
- package/src/apothem/harnesses/windsurf/STANDARD-CONVENTION-PIN.md +91 -0
- package/src/apothem/harnesses/windsurf/__init__.py +52 -0
- package/src/apothem/harnesses/windsurf/capabilities.yml +40 -0
- package/src/apothem/harnesses/windsurf/install.py +41 -0
- package/src/apothem/harnesses/windsurf/templates/apothem-rules.md +37 -0
- package/src/apothem/harnesses/windsurf/uninstall.py +25 -0
- package/src/apothem/harnesses/windsurf/update.py +10 -0
- package/src/apothem/harnesses/windsurf/verify.py +11 -0
- package/src/apothem/harnesses/zed/STANDARD-CONVENTION-PIN.md +92 -0
- package/src/apothem/harnesses/zed/__init__.py +57 -0
- package/src/apothem/harnesses/zed/capabilities.yml +38 -0
- package/src/apothem/harnesses/zed/install.py +41 -0
- package/src/apothem/harnesses/zed/templates/apothem-rules.md +32 -0
- package/src/apothem/harnesses/zed/uninstall.py +28 -0
- package/src/apothem/harnesses/zed/update.py +10 -0
- package/src/apothem/harnesses/zed/verify.py +11 -0
- package/src/apothem/hooks/README.md +81 -0
- package/src/apothem/hooks/__init__.py +24 -0
- package/src/apothem/hooks/askuserquestion_validator.py +380 -0
- package/src/apothem/hooks/dispatch.py +296 -0
- package/src/apothem/hooks/emit_hook_context.py +444 -0
- package/src/apothem/hooks/hooks.json +318 -0
- package/src/apothem/hooks/lib/README.md +39 -0
- package/src/apothem/hooks/lib/__init__.py +18 -0
- package/src/apothem/hooks/lib/bootstrap.ps1 +129 -0
- package/src/apothem/hooks/lib/bootstrap.sh +103 -0
- package/src/apothem/hooks/lib/events.py +51 -0
- package/src/apothem/hooks/lib/find-pwsh.ps1 +78 -0
- package/src/apothem/hooks/lib/find-pwsh.sh +76 -0
- package/src/apothem/hooks/lib/find-python.ps1 +63 -0
- package/src/apothem/hooks/lib/find-python.sh +97 -0
- package/src/apothem/hooks/lib/log.py +43 -0
- package/src/apothem/hooks/lib/resolve_root.py +264 -0
- package/src/apothem/hooks/messages/postcompact.md +14 -0
- package/src/apothem/hooks/messages/posttooluse-proactive-compaction.md +46 -0
- package/src/apothem/hooks/messages/precompact.md +14 -0
- package/src/apothem/hooks/messages/pretooluse-askuserquestion-recommended.md +65 -0
- package/src/apothem/hooks/messages/pretooluse-bash-plan-guard.md +97 -0
- package/src/apothem/hooks/messages/pretooluse-bash.md +39 -0
- package/src/apothem/hooks/messages/pretooluse-conformity.md +70 -0
- package/src/apothem/hooks/messages/pretooluse-dependency-guard.md +21 -0
- package/src/apothem/hooks/messages/pretooluse-edit-header-guard.md +61 -0
- package/src/apothem/hooks/messages/pretooluse-edit.md +21 -0
- package/src/apothem/hooks/messages/pretooluse-eval-guard.md +39 -0
- package/src/apothem/hooks/messages/pretooluse-notebookedit.md +11 -0
- package/src/apothem/hooks/messages/pretooluse-write-header-guard.md +45 -0
- package/src/apothem/hooks/messages/pretooluse-write-plan-guard.md +72 -0
- package/src/apothem/hooks/messages/pretooluse-write.md +21 -0
- package/src/apothem/hooks/messages/sessionstart.md +15 -0
- package/src/apothem/hooks/messages/stop.md +27 -0
- package/src/apothem/hooks/proactive_compaction_tracker.py +327 -0
- package/src/apothem/hooks/session_start_bootstrap.py +472 -0
- package/src/apothem/lib/README.md +42 -0
- package/src/apothem/lib/__init__.py +13 -0
- package/src/apothem/lib/atomic_io.py +189 -0
- package/src/apothem/lib/auditor.py +687 -0
- package/src/apothem/lib/clean_slate.py +396 -0
- package/src/apothem/lib/contexts.py +352 -0
- package/src/apothem/lib/data_home.py +255 -0
- package/src/apothem/lib/frontmatter.py +101 -0
- package/src/apothem/lib/harness_materializer.py +213 -0
- package/src/apothem/lib/harness_protocol.py +59 -0
- package/src/apothem/lib/harness_registry.py +282 -0
- package/src/apothem/lib/harness_registry_data.py +843 -0
- package/src/apothem/lib/install_ledger.py +347 -0
- package/src/apothem/lib/learning.py +540 -0
- package/src/apothem/lib/memory.py +347 -0
- package/src/apothem/lib/parallel_sweep.py +234 -0
- package/src/apothem/lib/plan_tiers.py +200 -0
- package/src/apothem/lib/plugin_bootstrap.py +132 -0
- package/src/apothem/lib/plugin_tree.py +599 -0
- package/src/apothem/lib/profile.py +755 -0
- package/src/apothem/lib/profile_projection.py +198 -0
- package/src/apothem/lib/propagation-manifest.yaml +878 -0
- package/src/apothem/lib/propagation.py +220 -0
- package/src/apothem/lib/python_resolver.py +189 -0
- package/src/apothem/lib/reporter.py +62 -0
- package/src/apothem/lib/workspace_migration.py +323 -0
- package/src/apothem/output-styles/README.md +41 -0
- package/src/apothem/output-styles/concise-engineer.md +49 -0
- package/src/apothem/output-styles/default-architect.md +52 -0
- package/src/apothem/output-styles/default.md +113 -0
- package/src/apothem/output-styles/forensic-auditor.md +63 -0
- package/src/apothem/py.typed +0 -0
- package/src/apothem/rules/README.md +121 -0
- package/src/apothem/rules/agent-capability-discipline-matrix.md +89 -0
- package/src/apothem/rules/agent-capability-discipline.md +78 -0
- package/src/apothem/rules/agent-orchestration-patterns.md +144 -0
- package/src/apothem/rules/agent-orchestration.md +65 -0
- package/src/apothem/rules/agents-md-convention.md +86 -0
- package/src/apothem/rules/agile-sprints-elements.md +135 -0
- package/src/apothem/rules/agile-sprints.md +64 -0
- package/src/apothem/rules/agnostic-posture-checklist.md +47 -0
- package/src/apothem/rules/agnostic-posture.md +48 -0
- package/src/apothem/rules/authoritative-referencing-quotation.md +50 -0
- package/src/apothem/rules/authoritative-referencing.md +66 -0
- package/src/apothem/rules/authority-inquiry-categories.md +58 -0
- package/src/apothem/rules/authority-inquiry.md +54 -0
- package/src/apothem/rules/auto-memory-topic-files.md +86 -0
- package/src/apothem/rules/auto-memory.md +67 -0
- package/src/apothem/rules/bidirectional-binding.md +123 -0
- package/src/apothem/rules/canonical-layout-reporting-tiers.md +212 -0
- package/src/apothem/rules/canonical-layout.md +60 -0
- package/src/apothem/rules/clean-architecture-layers.md +186 -0
- package/src/apothem/rules/clean-room-generation-protocols.md +124 -0
- package/src/apothem/rules/clean-room-generation.md +59 -0
- package/src/apothem/rules/code-craft-conventions.md +101 -0
- package/src/apothem/rules/code-craft-markdown.md +138 -0
- package/src/apothem/rules/code-craft-python.md +154 -0
- package/src/apothem/rules/code-craft-shell.md +192 -0
- package/src/apothem/rules/cognitive-identity-techniques.md +180 -0
- package/src/apothem/rules/cognitive-identity.md +81 -0
- package/src/apothem/rules/context-management-budget.md +46 -0
- package/src/apothem/rules/context-management-protocol.md +161 -0
- package/src/apothem/rules/context-management-scratch.md +128 -0
- package/src/apothem/rules/context-management.md +85 -0
- package/src/apothem/rules/definitiveness-virtues.md +67 -0
- package/src/apothem/rules/definitiveness.md +58 -0
- package/src/apothem/rules/determinism.md +81 -0
- package/src/apothem/rules/disclosure-ledger-markers.md +58 -0
- package/src/apothem/rules/disclosure-ledger.md +52 -0
- package/src/apothem/rules/dynamism.md +38 -0
- package/src/apothem/rules/etc-extension.md +57 -0
- package/src/apothem/rules/expertise-posture-elements.md +68 -0
- package/src/apothem/rules/expertise-posture.md +54 -0
- package/src/apothem/rules/freshness-facade.md +64 -0
- package/src/apothem/rules/harness-adapter-shape-schemas.md +162 -0
- package/src/apothem/rules/harness-adapter-shape.md +42 -0
- package/src/apothem/rules/host-discovery-manifests.md +50 -0
- package/src/apothem/rules/host-discovery.md +56 -0
- package/src/apothem/rules/i18n-discipline-locale-cohorts.md +120 -0
- package/src/apothem/rules/i18n-discipline.md +70 -0
- package/src/apothem/rules/interactive-questions-canonical-shapes.md +590 -0
- package/src/apothem/rules/interactive-questions-detail.md +41 -0
- package/src/apothem/rules/interactive-questions-sweep-matchers.md +184 -0
- package/src/apothem/rules/interactive-questions.md +89 -0
- package/src/apothem/rules/large-file-generation.md +112 -0
- package/src/apothem/rules/large-file-reading.md +59 -0
- package/src/apothem/rules/living-docs.md +85 -0
- package/src/apothem/rules/multi-agent-workflow.md +57 -0
- package/src/apothem/rules/operational-mandates-expanded.md +78 -0
- package/src/apothem/rules/operational-mandates.md +88 -0
- package/src/apothem/rules/option-annotation-form.md +60 -0
- package/src/apothem/rules/option-annotation.md +45 -0
- package/src/apothem/rules/own-voice-reimplementation.md +86 -0
- package/src/apothem/rules/performance-discipline.md +91 -0
- package/src/apothem/rules/persistent-conventions-vigilance-checklist.md +54 -0
- package/src/apothem/rules/persistent-conventions-vigilance.md +61 -0
- package/src/apothem/rules/plain-language.md +56 -0
- package/src/apothem/rules/planning-techniques.md +130 -0
- package/src/apothem/rules/pre-emission-gate-bars.md +86 -0
- package/src/apothem/rules/pre-emission-gate.md +54 -0
- package/src/apothem/rules/production-ready-prs-surfaces.md +162 -0
- package/src/apothem/rules/production-ready-prs.md +83 -0
- package/src/apothem/rules/propagation.md +63 -0
- package/src/apothem/rules/recommend-next-step.md +106 -0
- package/src/apothem/rules/refactoring-discipline.md +76 -0
- package/src/apothem/rules/session-closure.md +44 -0
- package/src/apothem/rules/sota-elevation-exemplars.md +76 -0
- package/src/apothem/rules/sota-elevation.md +52 -0
- package/src/apothem/rules/source-accessibility.md +58 -0
- package/src/apothem/rules/surgical-manipulation.md +48 -0
- package/src/apothem/rules/systemic-participation-relations.md +108 -0
- package/src/apothem/rules/systemic-participation.md +70 -0
- package/src/apothem/rules/ten-dimension-check-dimensions.md +52 -0
- package/src/apothem/rules/ten-dimension-check.md +59 -0
- package/src/apothem/rules/token-budget-discipline.md +81 -0
- package/src/apothem/rules/token-efficiency-rewrite-protocol.md +79 -0
- package/src/apothem/rules/token-efficiency-rewrite.md +77 -0
- package/src/apothem/rules/tool-use-discipline.md +48 -0
- package/src/apothem/rules/visual-leverage.md +102 -0
- package/src/apothem/schemas/NOTICE.md +9 -0
- package/src/apothem/schemas/README.md +104 -0
- package/src/apothem/schemas/__init__.py +176 -0
- package/src/apothem/schemas/advisory-finding.schema.json +111 -0
- package/src/apothem/schemas/agent.schema.json +106 -0
- package/src/apothem/schemas/authorship-header.txt +1 -0
- package/src/apothem/schemas/cohort-manifest.yaml +248 -0
- package/src/apothem/schemas/cohort-metadata-vocabulary.yaml +168 -0
- package/src/apothem/schemas/cohort.schema.json +113 -0
- package/src/apothem/schemas/command.schema.json +68 -0
- package/src/apothem/schemas/compatibility-matrix.yaml +432 -0
- package/src/apothem/schemas/context-fragment.schema.json +64 -0
- package/src/apothem/schemas/freshness-token-denylist.txt +51 -0
- package/src/apothem/schemas/handoff-manifest.yaml +353 -0
- package/src/apothem/schemas/header-exceptions.txt +141 -0
- package/src/apothem/schemas/header-visibility.yaml +39 -0
- package/src/apothem/schemas/learning-signal.schema.json +46 -0
- package/src/apothem/schemas/memory-record.schema.json +61 -0
- package/src/apothem/schemas/output-style.schema.json +40 -0
- package/src/apothem/schemas/plan.schema.json +51 -0
- package/src/apothem/schemas/plugin.schema.json +83 -0
- package/src/apothem/schemas/profile.example.yaml +70 -0
- package/src/apothem/schemas/profile.minimal.yaml +6 -0
- package/src/apothem/schemas/profile.schema.json +396 -0
- package/src/apothem/schemas/reference-token-denylist.txt +25 -0
- package/src/apothem/schemas/skill.schema.json +75 -0
- package/src/apothem/skills/README.md +93 -0
- package/src/apothem/skills/dependency-upgrade/SKILL.md +105 -0
- package/src/apothem/skills/dev-toolkit/SKILL.md +120 -0
- package/src/apothem/skills/diagram-authoring/SKILL.md +113 -0
- package/src/apothem/skills/document-authoring/SKILL.md +118 -0
- package/src/apothem/skills/ecosystem-audit/SKILL.md +108 -0
- package/src/apothem/skills/ecosystem-audit/references/audit-fortress.md +85 -0
- package/src/apothem/skills/ecosystem-audit/references/procedure.md +162 -0
- package/src/apothem/skills/eval-harness/SKILL.md +88 -0
- package/src/apothem/skills/incident-runbook/SKILL.md +92 -0
- package/src/apothem/skills/multi-source-research/SKILL.md +90 -0
- package/src/apothem/skills/plan-suite/SKILL.md +118 -0
- package/src/apothem/skills/plan-suite/master_template.md +1324 -0
- package/src/apothem/skills/projectify/SKILL.md +117 -0
- package/src/apothem/skills/prompt-engineering/SKILL.md +122 -0
- package/src/apothem/skills/refactor-extract/SKILL.md +85 -0
- package/src/apothem/skills/research-suite/SKILL.md +170 -0
- package/src/apothem/skills/research-suite/references/directory-structure.md +47 -0
- package/src/apothem/skills/research-suite/references/lifecycle.md +67 -0
- package/src/apothem/skills/research-suite/references/principal-investigator-framework.md +37 -0
- package/src/apothem/skills/research-suite/references/rigor-mandates.md +30 -0
- package/src/apothem/skills/research-suite/research_template.md +476 -0
- package/src/apothem/skills/secret-rotation/SKILL.md +87 -0
- package/src/apothem/skills/source-synthesis/SKILL.md +92 -0
- package/src/apothem/skills/surgical-guard/SKILL.md +118 -0
- package/src/apothem/skills/test-authoring/SKILL.md +85 -0
- package/src/apothem/skills/vuln-triage/SKILL.md +91 -0
- package/src/apothem/skills/workflow/SKILL.md +139 -0
- package/src/apothem/statuslines/README.md +26 -0
- package/src/apothem/statuslines/__init__.py +20 -0
- package/src/apothem/statuslines/conformity.json +5 -0
- package/src/apothem/statuslines/render.py +334 -0
- package/src/apothem/statuslines/statusline.md +50 -0
- package/src/apothem/templates/README.md +43 -0
- package/src/apothem/templates/agents-md-template.md +80 -0
- package/src/apothem/templates/consideration-log.md +39 -0
- package/src/apothem/templates/expertise-gap-log.md +56 -0
- package/src/apothem/templates/master-index-template.md +93 -0
- package/src/apothem/templates/potency-map.md +53 -0
- package/src/apothem/templates/preservation-audit.md +60 -0
- package/src/apothem/templates/question-resolution-audit.md +52 -0
- package/src/apothem/templates/trace-matrix-template.md +77 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Detect hard-coded model identifiers, version pins, and stale dates.
|
|
4
|
+
|
|
5
|
+
Why this scan exists. Narrative artifacts that bake a specific model
|
|
6
|
+
identifier (``claude-3-opus-20240229``), a SemVer literal embedded in
|
|
7
|
+
prose ("requires foo >= 1.2.3"), or an "as of <date>" claim referring
|
|
8
|
+
to a date older than twelve months become stale silently. Stale claims
|
|
9
|
+
mislead readers and violate the visual-leverage staleness invariant
|
|
10
|
+
that says current-reality assertions carry their own freshness proof.
|
|
11
|
+
This scan surfaces every occurrence so refit phases can replace each
|
|
12
|
+
with a parameterised reference, a relative date, or an updated value
|
|
13
|
+
with a fresh verification stamp.
|
|
14
|
+
|
|
15
|
+
What this scan covers. Three regex families:
|
|
16
|
+
|
|
17
|
+
- **Model-identifier literals.** ``claude-(2|3|3-opus|3-5-sonnet|3-5-haiku
|
|
18
|
+
|3-7-sonnet|opus-4|sonnet-4|haiku-4|opus-4-1|sonnet-4-1|opus-4-5|sonnet-4-5
|
|
19
|
+
|opus-4-7|sonnet-4-6|haiku-4-5)`` and dated suffixes like
|
|
20
|
+
``-20240229``. Any literal occurrence is a finding (the ecosystem
|
|
21
|
+
resolves model selection through the host's settings; a baked
|
|
22
|
+
identifier defeats that surface).
|
|
23
|
+
- **SemVer-in-prose.** A SemVer triple appearing inside narrative
|
|
24
|
+
Markdown that is not adjacent to a known version-pinning surface
|
|
25
|
+
(``CHANGELOG.md`` entries are exempt; lines containing ``version:``
|
|
26
|
+
or ``semver:`` keys are exempt).
|
|
27
|
+
- **Stale "as-of" date claims.** ``\\bas of (\\d{4}-\\d{2}-\\d{2})\\b`` and
|
|
28
|
+
``\\bverified:?\\s+(\\d{4}-\\d{2}-\\d{2})\\b`` patterns; any matched
|
|
29
|
+
date older than 12 months from the run's wall-clock is a finding.
|
|
30
|
+
|
|
31
|
+
What this scan excludes. The repository's own ``CHANGELOG.md`` (where
|
|
32
|
+
SemVer literals legitimately mark release entries) and lines containing
|
|
33
|
+
the literal phrase ``deprecated:`` (which document retirement, not
|
|
34
|
+
current behavior). Memory and plan-artifact records are excluded by
|
|
35
|
+
the shared narrative-surface filter.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import argparse
|
|
41
|
+
import re
|
|
42
|
+
import sys
|
|
43
|
+
from datetime import date, datetime, timedelta, timezone
|
|
44
|
+
from pathlib import Path
|
|
45
|
+
from typing import Any
|
|
46
|
+
|
|
47
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
48
|
+
|
|
49
|
+
from _scan_lib import (
|
|
50
|
+
CONTENT_ROOT,
|
|
51
|
+
NARRATIVE_CLASSES,
|
|
52
|
+
SEVERITY_MEDIUM,
|
|
53
|
+
Hit,
|
|
54
|
+
WalkCallback,
|
|
55
|
+
emit_json,
|
|
56
|
+
load_inventory,
|
|
57
|
+
walk_narrative_surfaces,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Match Anthropic-family model identifiers including dated suffixes.
|
|
61
|
+
# Cover the full pre-4 family plus the 4.x cohort (opus / sonnet / haiku
|
|
62
|
+
# at the 4-0 / 4-1 / 4-5 / 4-6 / 4-7 minor-version axis as documented in
|
|
63
|
+
# the active CLAUDE.md environment block).
|
|
64
|
+
_MODEL_PATTERN = re.compile(
|
|
65
|
+
r"\bclaude-("
|
|
66
|
+
r"2(?:\.\d+)?(?:-\w+)*"
|
|
67
|
+
r"|3-(?:opus|sonnet|haiku)(?:-\d{8})?"
|
|
68
|
+
r"|3[-.]5-(?:opus|sonnet|haiku)(?:-\d{8})?"
|
|
69
|
+
r"|3[-.]7-(?:opus|sonnet|haiku)(?:-\d{8})?"
|
|
70
|
+
r"|(?:opus|sonnet|haiku)-4(?:[-.]\d+)?(?:-\d{8})?(?:\[[^\]]+\])?"
|
|
71
|
+
r")\b"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# SemVer triple captured in two narrowly-scoped contexts:
|
|
75
|
+
# (a) v-prefixed (`v1.2.3` / `v1.2.3-rc1`) — the canonical pin shape.
|
|
76
|
+
# (b) bare triple anchored to a version-context keyword on the same
|
|
77
|
+
# line (``version``, ``release``, ``upgraded to``, ``bump to``).
|
|
78
|
+
# A bare ``X.Y.Z`` without context is far more often a section number
|
|
79
|
+
# (``§4.8.6``), an issue reference (``#1.2.3``), or a regex / literal
|
|
80
|
+
# than a stale version pin; constraining context kills the false-positive
|
|
81
|
+
# class that dominated the un-constrained match.
|
|
82
|
+
_SEMVER_V_PREFIXED = re.compile(
|
|
83
|
+
r"(?<![\w.])v(\d+\.\d+\.\d+(?:-[\w.]+)?(?:\+[\w.]+)?)\b"
|
|
84
|
+
)
|
|
85
|
+
_SEMVER_BARE = re.compile(r"(?<![\w.§#])(\d+\.\d+\.\d+(?:-[\w.]+)?(?:\+[\w.]+)?)\b")
|
|
86
|
+
_VERSION_CONTEXT_RE = re.compile(
|
|
87
|
+
r"\b(version|release|upgrade(?:d)? to|bump(?:ed)? to|requires|"
|
|
88
|
+
r"depends on|pin(?:ned)? to)\b",
|
|
89
|
+
re.IGNORECASE,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Phrases that anchor a date reference to a freshness claim.
|
|
93
|
+
_AS_OF_PATTERN = re.compile(
|
|
94
|
+
r"\b(?:as of|valid as of|verified|verified:|last verified|"
|
|
95
|
+
r"last updated|updated:?)\s+(\d{4}-\d{2}-\d{2})\b",
|
|
96
|
+
re.IGNORECASE,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Lines exempt from the SemVer-in-prose finding because they live in
|
|
100
|
+
# legitimate version-pinning contexts.
|
|
101
|
+
_SEMVER_EXEMPT_PATTERNS = (
|
|
102
|
+
re.compile(r"^\s*version\s*:\s*", re.IGNORECASE),
|
|
103
|
+
re.compile(r"^\s*semver\s*:\s*", re.IGNORECASE),
|
|
104
|
+
re.compile(r"^\s*##\s*\[\d", re.IGNORECASE), # CHANGELOG ## [0.1.5]
|
|
105
|
+
re.compile(r"\bdeprecated:?\b", re.IGNORECASE),
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# File-level exemption: the project's CHANGELOG legitimately enumerates
|
|
109
|
+
# SemVer release entries; date claims there are release dates, not
|
|
110
|
+
# freshness claims.
|
|
111
|
+
_FILE_EXEMPT_FROM_SEMVER = frozenset({"CHANGELOG.md"})
|
|
112
|
+
|
|
113
|
+
# URL prefixes that anchor version strings to legitimate references
|
|
114
|
+
# (badge image URLs, package-registry links, GitHub release pages, etc.).
|
|
115
|
+
# A SemVer match inside a URL is not narrative drift.
|
|
116
|
+
_URL_MARKER_RE = re.compile(r"https?://|\bshields\.io|\bbadgen\.net")
|
|
117
|
+
|
|
118
|
+
# A SemVer match is suppressed when the only context is a configuration
|
|
119
|
+
# surface that legitimately pins versions: lock files, manifests, hook
|
|
120
|
+
# configs. We restrict the SemVer-in-prose scan to Markdown files only;
|
|
121
|
+
# this set captures the Markdown-extension whitelist.
|
|
122
|
+
_SEMVER_SCAN_SUFFIXES = frozenset({".md", ".markdown"})
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _stale_date_threshold() -> date:
|
|
126
|
+
"""Return the cutoff date older than which 'as-of' claims are stale.
|
|
127
|
+
|
|
128
|
+
Twelve months is the staleness window applied uniformly across the
|
|
129
|
+
audit. A shorter window would over-flag legitimate stable claims;
|
|
130
|
+
a longer one would let genuinely-stale assertions persist.
|
|
131
|
+
"""
|
|
132
|
+
return datetime.now(timezone.utc).date() - timedelta(days=365)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _scan_model_ids(rel: str, lineno: int, line: str) -> list[Hit]:
|
|
136
|
+
"""Emit a hit per literal model identifier on the line."""
|
|
137
|
+
hits: list[Hit] = []
|
|
138
|
+
for match in _MODEL_PATTERN.finditer(line):
|
|
139
|
+
token = match.group(0)
|
|
140
|
+
hits.append(
|
|
141
|
+
Hit(
|
|
142
|
+
file=rel,
|
|
143
|
+
line=lineno,
|
|
144
|
+
signal=f"hard-coded-model-identifier: {token}",
|
|
145
|
+
severity=SEVERITY_MEDIUM,
|
|
146
|
+
remediation=(
|
|
147
|
+
"Replace the literal model identifier with a reference"
|
|
148
|
+
" resolved through the host's model selection surface;"
|
|
149
|
+
" bake-in defeats the indirection that lets operators"
|
|
150
|
+
" upgrade."
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
return hits
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _scan_semver_prose(rel: str, lineno: int, line: str) -> list[Hit]:
|
|
158
|
+
"""Emit a hit per SemVer literal that is not URL-anchored or
|
|
159
|
+
inside a configuration-pin context.
|
|
160
|
+
|
|
161
|
+
Two match channels: ``v``-prefixed pins (always flagged when not in
|
|
162
|
+
an exempt context) and bare triples (flagged only when a
|
|
163
|
+
version-context keyword appears on the same line).
|
|
164
|
+
"""
|
|
165
|
+
if any(p.search(line) for p in _SEMVER_EXEMPT_PATTERNS):
|
|
166
|
+
return []
|
|
167
|
+
if _URL_MARKER_RE.search(line):
|
|
168
|
+
return []
|
|
169
|
+
hits: list[Hit] = []
|
|
170
|
+
for match in _SEMVER_V_PREFIXED.finditer(line):
|
|
171
|
+
token = match.group(0)
|
|
172
|
+
hits.append(_semver_hit(rel, lineno, token))
|
|
173
|
+
if _VERSION_CONTEXT_RE.search(line):
|
|
174
|
+
for match in _SEMVER_BARE.finditer(line):
|
|
175
|
+
token = match.group(0)
|
|
176
|
+
hits.append(_semver_hit(rel, lineno, token))
|
|
177
|
+
return hits
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _semver_hit(rel: str, lineno: int, token: str) -> Hit:
|
|
181
|
+
"""Construct the canonical SemVer-in-prose hit record."""
|
|
182
|
+
return Hit(
|
|
183
|
+
file=rel,
|
|
184
|
+
line=lineno,
|
|
185
|
+
signal=f"semver-in-prose: {token}",
|
|
186
|
+
severity=SEVERITY_MEDIUM,
|
|
187
|
+
remediation=(
|
|
188
|
+
"Move the version pin to a configuration surface"
|
|
189
|
+
" (manifest, schema, version file) or convert the"
|
|
190
|
+
" prose claim into a parameterised reference."
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _scan_stale_dates(
|
|
196
|
+
rel: str, lineno: int, line: str, stale_cutoff: date
|
|
197
|
+
) -> list[Hit]:
|
|
198
|
+
"""Emit a hit per 'as-of' claim whose date predates the staleness
|
|
199
|
+
cutoff."""
|
|
200
|
+
hits: list[Hit] = []
|
|
201
|
+
for match in _AS_OF_PATTERN.finditer(line):
|
|
202
|
+
date_str = match.group(1)
|
|
203
|
+
try:
|
|
204
|
+
claim_date = date.fromisoformat(date_str)
|
|
205
|
+
except ValueError:
|
|
206
|
+
continue
|
|
207
|
+
if claim_date >= stale_cutoff:
|
|
208
|
+
continue
|
|
209
|
+
age_days = (stale_cutoff - claim_date).days + 365
|
|
210
|
+
hits.append(
|
|
211
|
+
Hit(
|
|
212
|
+
file=rel,
|
|
213
|
+
line=lineno,
|
|
214
|
+
signal=(
|
|
215
|
+
f"stale-as-of-claim: {date_str} (~{age_days // 30} months old)"
|
|
216
|
+
),
|
|
217
|
+
severity=SEVERITY_MEDIUM,
|
|
218
|
+
remediation=(
|
|
219
|
+
"Re-verify the claim against current state and update"
|
|
220
|
+
" the date stamp, or restate the claim with a"
|
|
221
|
+
" parameterised reference that does not require"
|
|
222
|
+
" periodic re-verification."
|
|
223
|
+
),
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
return hits
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _is_markdown(path_str: str) -> bool:
|
|
230
|
+
"""Return True if the file path looks like a Markdown document."""
|
|
231
|
+
return Path(path_str).suffix.lower() in _SEMVER_SCAN_SUFFIXES
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _scan_callback(stale_cutoff: date) -> WalkCallback:
|
|
235
|
+
"""Build the per-file scanner closure."""
|
|
236
|
+
|
|
237
|
+
def _walk(path: Path, record: dict[str, Any], content: str) -> list[Hit]:
|
|
238
|
+
rel = record["path"]
|
|
239
|
+
is_md = _is_markdown(rel)
|
|
240
|
+
file_exempt_semver = Path(rel).name in _FILE_EXEMPT_FROM_SEMVER
|
|
241
|
+
in_fence = False
|
|
242
|
+
hits: list[Hit] = []
|
|
243
|
+
for lineno, line in enumerate(content.splitlines(), start=1):
|
|
244
|
+
if line.lstrip().startswith("```"):
|
|
245
|
+
in_fence = not in_fence
|
|
246
|
+
continue
|
|
247
|
+
hits.extend(_scan_model_ids(rel, lineno, line))
|
|
248
|
+
if is_md and not in_fence and not file_exempt_semver:
|
|
249
|
+
hits.extend(_scan_semver_prose(rel, lineno, line))
|
|
250
|
+
hits.extend(_scan_stale_dates(rel, lineno, line, stale_cutoff))
|
|
251
|
+
return hits
|
|
252
|
+
|
|
253
|
+
return _walk
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def main(argv: list[str] | None = None) -> int:
|
|
257
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
258
|
+
parser.add_argument(
|
|
259
|
+
"--inventory",
|
|
260
|
+
type=Path,
|
|
261
|
+
default=Path(".audit/inventory.json"),
|
|
262
|
+
)
|
|
263
|
+
parser.add_argument("--root", type=Path, default=CONTENT_ROOT)
|
|
264
|
+
parser.add_argument(
|
|
265
|
+
"--output",
|
|
266
|
+
type=Path,
|
|
267
|
+
default=Path(".audit/drift-stale-tokens.json"),
|
|
268
|
+
)
|
|
269
|
+
args = parser.parse_args(argv)
|
|
270
|
+
|
|
271
|
+
if not args.inventory.exists():
|
|
272
|
+
print(
|
|
273
|
+
f"error: inventory not found at {args.inventory}",
|
|
274
|
+
file=sys.stderr,
|
|
275
|
+
)
|
|
276
|
+
return 1
|
|
277
|
+
|
|
278
|
+
records, sha = load_inventory(args.inventory)
|
|
279
|
+
cutoff = _stale_date_threshold()
|
|
280
|
+
hits = walk_narrative_surfaces(records, args.root, _scan_callback(cutoff))
|
|
281
|
+
emit_json(args.output, "scan_stale_tokens", hits, sha)
|
|
282
|
+
narrative_count = sum(1 for r in records if r.get("class") in NARRATIVE_CLASSES)
|
|
283
|
+
by_signal: dict[str, int] = {}
|
|
284
|
+
for h in hits:
|
|
285
|
+
kind = h.signal.split(":", 1)[0]
|
|
286
|
+
by_signal[kind] = by_signal.get(kind, 0) + 1
|
|
287
|
+
summary = ", ".join(f"{k}={v}" for k, v in sorted(by_signal.items()))
|
|
288
|
+
print(
|
|
289
|
+
f"scan_stale_tokens: {len(hits)} hit(s) across "
|
|
290
|
+
f"{narrative_count} narrative files [{summary or 'none'}]"
|
|
291
|
+
)
|
|
292
|
+
return 0
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Synthesise the eight drift-scan outputs into a single findings doc.
|
|
4
|
+
|
|
5
|
+
Why this tool exists. The eight drift scans each emit a per-scan
|
|
6
|
+
JSON document; the audit-gate consolidation needs one human-readable
|
|
7
|
+
view ranking every finding by severity, grouping by file, and
|
|
8
|
+
pointing at the right refit pass. This tool produces
|
|
9
|
+
``drift-findings.md`` — the canonical synthesis surface.
|
|
10
|
+
|
|
11
|
+
Severity ranking:
|
|
12
|
+
|
|
13
|
+
- HIGH: secret leaks, conflicting directives, broken internal links,
|
|
14
|
+
plans-discipline violations, missing CLAUDE.md mandatory blocks.
|
|
15
|
+
- MEDIUM: stale tokens, frontmatter violations, AI-surfaces drift,
|
|
16
|
+
most non-banner emails, model identifiers.
|
|
17
|
+
- LOW: optional-surface presence informational hits, smart-quote /
|
|
18
|
+
whitespace issues (none currently scanned).
|
|
19
|
+
|
|
20
|
+
What this tool does NOT do. It does not assign verdicts (the
|
|
21
|
+
classification-matrix is the verdict surface) and it does not
|
|
22
|
+
re-run the scans (each scanner runs independently first).
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import json
|
|
29
|
+
from collections import defaultdict
|
|
30
|
+
from collections.abc import Iterable
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from typing import Any, Final
|
|
34
|
+
|
|
35
|
+
_SCAN_FILES: Final[list[tuple[str, str]]] = [
|
|
36
|
+
("scan_drift_features", ".audit/drift-feature-refs.json"),
|
|
37
|
+
("scan_stale_tokens", ".audit/drift-stale-tokens.json"),
|
|
38
|
+
("check_links", ".audit/drift-broken-links.json"),
|
|
39
|
+
("scan_frontmatter", ".audit/drift-frontmatter.json"),
|
|
40
|
+
("conflicting-directives", ".audit/drift-conflicting-directives.json"),
|
|
41
|
+
("scan_secrets_pii", ".audit/drift-secrets-pii.json"),
|
|
42
|
+
("scan_plans_discipline", ".audit/drift-plans-discipline.json"),
|
|
43
|
+
("scan_ai_surfaces_coarse", ".audit/drift-ai-surfaces-coarse.json"),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
_SEVERITY_ORDER: Final[list[str]] = ["HIGH", "MEDIUM", "LOW"]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class Finding:
|
|
51
|
+
scanner: str
|
|
52
|
+
file: str
|
|
53
|
+
line: int
|
|
54
|
+
signal: str
|
|
55
|
+
severity: str
|
|
56
|
+
remediation: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _load_scan(scanner: str, path: Path) -> tuple[list[Finding], dict[str, Any]]:
|
|
60
|
+
"""Return ``(findings, envelope)`` for one scan's output."""
|
|
61
|
+
if not path.exists():
|
|
62
|
+
return [], {"missing": True}
|
|
63
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
64
|
+
findings = [
|
|
65
|
+
Finding(
|
|
66
|
+
scanner=scanner,
|
|
67
|
+
file=h.get("file", ""),
|
|
68
|
+
line=int(h.get("line", 0) or 0),
|
|
69
|
+
signal=h.get("signal", ""),
|
|
70
|
+
severity=h.get("severity", "LOW"),
|
|
71
|
+
remediation=h.get("remediation", ""),
|
|
72
|
+
)
|
|
73
|
+
for h in payload.get("hits", [])
|
|
74
|
+
]
|
|
75
|
+
envelope = {
|
|
76
|
+
"scanner": scanner,
|
|
77
|
+
"hit-count": payload.get("hit-count", len(findings)),
|
|
78
|
+
"missing": False,
|
|
79
|
+
}
|
|
80
|
+
return findings, envelope
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _gather(audit_root: Path) -> tuple[list[Finding], list[dict[str, Any]]]:
|
|
84
|
+
all_findings: list[Finding] = []
|
|
85
|
+
envelopes: list[dict[str, Any]] = []
|
|
86
|
+
for scanner, rel_path in _SCAN_FILES:
|
|
87
|
+
findings, envelope = _load_scan(scanner, audit_root / Path(rel_path).name)
|
|
88
|
+
all_findings.extend(findings)
|
|
89
|
+
envelopes.append(envelope)
|
|
90
|
+
return all_findings, envelopes
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _group_by_severity(
|
|
94
|
+
findings: Iterable[Finding],
|
|
95
|
+
) -> dict[str, list[Finding]]:
|
|
96
|
+
by_sev: dict[str, list[Finding]] = defaultdict(list)
|
|
97
|
+
for f in findings:
|
|
98
|
+
by_sev[f.severity].append(f)
|
|
99
|
+
return by_sev
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _group_by_scanner(
|
|
103
|
+
findings: Iterable[Finding],
|
|
104
|
+
) -> dict[str, list[Finding]]:
|
|
105
|
+
by_scanner: dict[str, list[Finding]] = defaultdict(list)
|
|
106
|
+
for f in findings:
|
|
107
|
+
by_scanner[f.scanner].append(f)
|
|
108
|
+
return by_scanner
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _emit_markdown(
|
|
112
|
+
findings: list[Finding],
|
|
113
|
+
envelopes: list[dict[str, Any]],
|
|
114
|
+
out_path: Path,
|
|
115
|
+
) -> None:
|
|
116
|
+
by_sev = _group_by_severity(findings)
|
|
117
|
+
by_scanner = _group_by_scanner(findings)
|
|
118
|
+
lines: list[str] = []
|
|
119
|
+
lines.append("# Drift Findings")
|
|
120
|
+
lines.append("")
|
|
121
|
+
lines.append(
|
|
122
|
+
"Synthesised from the eight drift scans. Severity ranking per"
|
|
123
|
+
" the synthesis-pass criteria; grouping is by severity first,"
|
|
124
|
+
" then by scanner, then by file."
|
|
125
|
+
)
|
|
126
|
+
lines.append("")
|
|
127
|
+
lines.append("## Per-scan summary")
|
|
128
|
+
lines.append("")
|
|
129
|
+
lines.append("| Scanner | Hit count | Status |")
|
|
130
|
+
lines.append("|---------|----------:|--------|")
|
|
131
|
+
for env in envelopes:
|
|
132
|
+
if env.get("missing"):
|
|
133
|
+
lines.append(f"| `{env['scanner']}` | — | output absent |")
|
|
134
|
+
else:
|
|
135
|
+
lines.append(f"| `{env['scanner']}` | {env['hit-count']} | scan ran |")
|
|
136
|
+
lines.append("")
|
|
137
|
+
lines.append("## Severity totals")
|
|
138
|
+
lines.append("")
|
|
139
|
+
lines.append("| Severity | Count |")
|
|
140
|
+
lines.append("|----------|------:|")
|
|
141
|
+
for sev in _SEVERITY_ORDER:
|
|
142
|
+
lines.append(f"| {sev} | {len(by_sev.get(sev, []))} |")
|
|
143
|
+
lines.append("")
|
|
144
|
+
for sev in _SEVERITY_ORDER:
|
|
145
|
+
sev_findings = by_sev.get(sev, [])
|
|
146
|
+
if not sev_findings:
|
|
147
|
+
continue
|
|
148
|
+
lines.append(f"## {sev} severity ({len(sev_findings)} findings)")
|
|
149
|
+
lines.append("")
|
|
150
|
+
per_scanner = _group_by_scanner(sev_findings)
|
|
151
|
+
for scanner in sorted(per_scanner.keys()):
|
|
152
|
+
scanner_findings = per_scanner[scanner]
|
|
153
|
+
lines.append(f"### `{scanner}` — {len(scanner_findings)} {sev} hit(s)")
|
|
154
|
+
lines.append("")
|
|
155
|
+
lines.append("| File | Line | Signal | Remediation |")
|
|
156
|
+
lines.append("|------|-----:|--------|-------------|")
|
|
157
|
+
for f in sorted(scanner_findings, key=lambda x: (x.file, x.line)):
|
|
158
|
+
signal = f.signal.replace("|", r"\|")
|
|
159
|
+
remediation = f.remediation.replace("|", r"\|")
|
|
160
|
+
# Truncate long signal text for table readability.
|
|
161
|
+
if len(signal) > 100:
|
|
162
|
+
signal = signal[:97] + "..."
|
|
163
|
+
if len(remediation) > 140:
|
|
164
|
+
remediation = remediation[:137] + "..."
|
|
165
|
+
lines.append(f"| `{f.file}` | {f.line} | {signal} | {remediation} |")
|
|
166
|
+
lines.append("")
|
|
167
|
+
lines.append("## Per-scanner total")
|
|
168
|
+
lines.append("")
|
|
169
|
+
lines.append("| Scanner | Total hits |")
|
|
170
|
+
lines.append("|---------|-----------:|")
|
|
171
|
+
for scanner in sorted(by_scanner.keys()):
|
|
172
|
+
lines.append(f"| `{scanner}` | {len(by_scanner[scanner])} |")
|
|
173
|
+
lines.append("")
|
|
174
|
+
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
175
|
+
out_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def main(argv: list[str] | None = None) -> int:
|
|
179
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
180
|
+
parser.add_argument(
|
|
181
|
+
"--audit-dir",
|
|
182
|
+
type=Path,
|
|
183
|
+
default=Path(".audit"),
|
|
184
|
+
)
|
|
185
|
+
parser.add_argument(
|
|
186
|
+
"--output",
|
|
187
|
+
type=Path,
|
|
188
|
+
default=Path(".audit/drift-findings.md"),
|
|
189
|
+
)
|
|
190
|
+
args = parser.parse_args(argv)
|
|
191
|
+
|
|
192
|
+
findings, envelopes = _gather(args.audit_dir)
|
|
193
|
+
_emit_markdown(findings, envelopes, args.output)
|
|
194
|
+
|
|
195
|
+
by_sev = _group_by_severity(findings)
|
|
196
|
+
summary = ", ".join(f"{sev}={len(by_sev.get(sev, []))}" for sev in _SEVERITY_ORDER)
|
|
197
|
+
print(
|
|
198
|
+
f"synthesize_drift: {len(findings)} total findings "
|
|
199
|
+
f"[{summary}] across {len(envelopes)} scans -> {args.output}"
|
|
200
|
+
)
|
|
201
|
+
return 0
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if __name__ == "__main__":
|
|
205
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<!-- SPDX-License-Identifier: MIT -->
|
|
2
|
+
|
|
3
|
+
# benchmarks
|
|
4
|
+
|
|
5
|
+
> **Role.** Per-class performance benchmark drivers. Each script runs an artifact class's representative invocations, measures runtime, compares against the class budget declared in `rules/performance-discipline.md` §1, and exits 0 on PASS / non-zero on FAIL with a measured-vs-budget delta.
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
| File | Purpose |
|
|
10
|
+
|------|---------|
|
|
11
|
+
| `bench_hooks.py` | Hook-handler runtime benchmark against the per-event budgets (`--event=<name>`). |
|
|
12
|
+
| `bench_tests.py` | Test-suite runtime benchmark against the per-suite budgets (full suite and per-module). |
|
|
13
|
+
| `bench_agents.py` | Agent-spawn runtime benchmark against the per-spawn budget. |
|
|
14
|
+
| `bench_validate_ecosystem.py` | Verify-ecosystem-sweep runtime benchmark against the composite and per-check budgets. |
|
|
15
|
+
| `__init__.py` | Package marker. |
|
|
16
|
+
|
|
17
|
+
## Conventions
|
|
18
|
+
|
|
19
|
+
- Budgets are operator-editorial at apply time; the recommended baselines align with the hook-timeout values in the harness configuration and `rules/performance-discipline.md` §1.
|
|
20
|
+
- Budget exceedances surface as high-priority Performance-axis findings.
|
|
21
|
+
|
|
22
|
+
## Operating in this folder
|
|
23
|
+
|
|
24
|
+
- **Budget constant is the single source.** Each driver declares its class's budget as a module-level `Final` constant; it stays aligned with the budget table in `../rules/performance-discipline.md` §1 and the harness hook-timeout values — a budget change is mirrored in both places in the same change-set.
|
|
25
|
+
- **Exit code is the verdict.** 0 = PASS, non-zero = FAIL with a measured-vs-budget delta line. A scaffold driver with no wired fixture reports the budget and exits 0 so the verifier surface stays usable.
|
|
26
|
+
- **Path resolution is anchored relative to `__file__`** (the drivers sit four parents below the repository root); do not hard-code an absolute path.
|
|
27
|
+
- **Adding a driver:** declare the class's budget constant, implement the measurement against the representative invocation, and return the exit-code verdict with a delta line on FAIL. **Revising a budget:** cite a concrete driver (measured workload increase, infrastructure change, dependency upgrade, or rule citation) and update the rule table in the same change-set.
|
|
28
|
+
- Validate a change with `python -m ruff check`, `python -m mypy` for the in-scope modules, and `python -m pytest` (benchmark-driver tests).
|
|
29
|
+
|
|
30
|
+
## Related
|
|
31
|
+
|
|
32
|
+
- [`rules/performance-discipline.md`](../rules/performance-discipline.md) — the per-class budget table the drivers verify.
|
|
33
|
+
- [`hooks/`](../hooks/) — the hook handlers `bench_hooks.py` measures.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Agent-spawn runtime benchmark per the per-spawn budget in
|
|
4
|
+
`src/apothem/rules/performance-discipline.md` §1.
|
|
5
|
+
|
|
6
|
+
Spawn budget: 60 seconds for any of the four agent patterns
|
|
7
|
+
(research / audit / quality / generation).
|
|
8
|
+
|
|
9
|
+
Agent spawns are exercised through the host harness rather than via a
|
|
10
|
+
direct subprocess call; the script stands as a budget anchor with a
|
|
11
|
+
representative-invocation hook that operators wire to their harness.
|
|
12
|
+
When no harness fixture is provided, the script reports the budget and
|
|
13
|
+
exits with the scaffold marker (return code 0) so the verifier surface
|
|
14
|
+
remains usable while detailed fixtures land.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import argparse
|
|
20
|
+
from typing import Final
|
|
21
|
+
|
|
22
|
+
_BUDGET: Final[float] = 60.0
|
|
23
|
+
_PATTERNS: Final[tuple[str, ...]] = ("research", "audit", "quality", "generation")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _representative_spawn() -> float | None:
|
|
27
|
+
"""Return wall-clock seconds for a representative agent spawn.
|
|
28
|
+
|
|
29
|
+
Returns ``None`` when no harness fixture is wired; the caller then
|
|
30
|
+
reports the budget without a measurement.
|
|
31
|
+
"""
|
|
32
|
+
# Harness fixtures are operator-editorial; without one wired, no
|
|
33
|
+
# measurement is attempted. Wire a representative spawn through the
|
|
34
|
+
# host harness here when one becomes available.
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def main(argv: list[str] | None = None) -> int:
|
|
39
|
+
parser = argparse.ArgumentParser(prog="bench_agents")
|
|
40
|
+
parser.add_argument(
|
|
41
|
+
"--pattern",
|
|
42
|
+
required=True,
|
|
43
|
+
choices=_PATTERNS,
|
|
44
|
+
help="Agent pattern to benchmark.",
|
|
45
|
+
)
|
|
46
|
+
args = parser.parse_args(argv)
|
|
47
|
+
|
|
48
|
+
elapsed = _representative_spawn()
|
|
49
|
+
if elapsed is None:
|
|
50
|
+
print(
|
|
51
|
+
f"SCAFFOLD: pattern={args.pattern} budget={_BUDGET}s "
|
|
52
|
+
"(no harness fixture wired; measurement skipped)"
|
|
53
|
+
)
|
|
54
|
+
return 0
|
|
55
|
+
if elapsed <= _BUDGET:
|
|
56
|
+
print(f"PASS: agent {args.pattern} = {elapsed:.3f}s (budget {_BUDGET}s)")
|
|
57
|
+
return 0
|
|
58
|
+
print(f"FAIL: agent {args.pattern} = {elapsed:.3f}s exceeds budget {_BUDGET}s")
|
|
59
|
+
return 1
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
if __name__ == "__main__":
|
|
63
|
+
raise SystemExit(main())
|