@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,620 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""multi-surface-coherence-grep: shared-claim coherence across AI-conventions surfaces.
|
|
4
|
+
|
|
5
|
+
Reads ``tests/fixtures/multi-surface-claims.yaml`` and, for each enumerated
|
|
6
|
+
claim, walks every ``required-in`` surface (and any ``optional-in`` surface
|
|
7
|
+
that exists on disk). For each (claim, surface) pair the validator:
|
|
8
|
+
|
|
9
|
+
1. Computes a token-coverage score using the claim's
|
|
10
|
+
``semantic-equivalence-tokens`` list — case-insensitive substring matches
|
|
11
|
+
divided by the token count. A claim is **present** in a surface when the
|
|
12
|
+
score meets or exceeds ``COVERAGE_THRESHOLD`` AND at least ``MIN_TOKEN_HITS``
|
|
13
|
+
distinct tokens match.
|
|
14
|
+
2. Records contradiction signals — a surface whose matching paragraph carries
|
|
15
|
+
a negation marker against a token the canonical surface (AGENTS.md)
|
|
16
|
+
asserts affirmatively flags ``COHERENCE_CONTRADICTION``.
|
|
17
|
+
3. Honors the ``<!-- coherence-override: <claim-id> -->`` marker per the
|
|
18
|
+
reconciliation rule: when present, the surface is exempted from the claim's
|
|
19
|
+
coherence check and an advisory finding is emitted naming the ADR pairing
|
|
20
|
+
requirement (``COHERENCE_OVERRIDE_HONORED``).
|
|
21
|
+
|
|
22
|
+
Verdict matrix:
|
|
23
|
+
pass — every required-in surface contains every claim with no
|
|
24
|
+
contradictions; claims missing from the canonical surface
|
|
25
|
+
before its refit lands surface as advisory partial-coverage
|
|
26
|
+
findings (``severity: warning``) that do NOT fail the verdict.
|
|
27
|
+
fail — any contradiction or any required-claim absent from a
|
|
28
|
+
required-in surface without an override marker.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import re
|
|
34
|
+
import sys
|
|
35
|
+
from dataclasses import dataclass
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import Final
|
|
38
|
+
|
|
39
|
+
from apothem.conformity._grep_base import GrepResult, run_grep
|
|
40
|
+
|
|
41
|
+
GREP_NAME: Final[str] = "multi-surface-coherence-grep"
|
|
42
|
+
EXIT_PASS: Final[int] = 0
|
|
43
|
+
EXIT_FAIL: Final[int] = 2
|
|
44
|
+
STDIN_FLAG: Final[str] = "--stdin"
|
|
45
|
+
|
|
46
|
+
# Working-tree root anchor. In the repo checkout, parents[3] of
|
|
47
|
+
# ``src/apothem/conformity/<file>.py`` is the working-tree root that
|
|
48
|
+
# carries the claims fixture and the tracked surfaces. In the installed
|
|
49
|
+
# tree (``<install-root>/apothem/conformity/``) the anchor is a coarse
|
|
50
|
+
# filesystem ancestor with no fixture beneath it: hook-mode dispatches
|
|
51
|
+
# degrade to a pass-through via the relative_to() guard in
|
|
52
|
+
# _is_in_scope(), and a direct invocation reports the absent fixture as
|
|
53
|
+
# an advisory pass per _IN_REPO_CHECKOUT below.
|
|
54
|
+
ECOSYSTEM_ROOT: Final[Path] = Path(__file__).resolve().parents[3]
|
|
55
|
+
|
|
56
|
+
# Shape detection: the claims fixture ships only with the repo checkout
|
|
57
|
+
# (where the conformity package sits inside a ``src/`` tree). The
|
|
58
|
+
# installed tree carries the package beside its ``schemas/`` sibling with
|
|
59
|
+
# no ``src/`` parent and no test-fixture corpus, so a missing fixture
|
|
60
|
+
# there is the expected state, not a defect.
|
|
61
|
+
_IN_REPO_CHECKOUT: Final[bool] = Path(__file__).resolve().parents[2].name == "src"
|
|
62
|
+
CLAIMS_FIXTURE_RELATIVE: Final[Path] = (
|
|
63
|
+
Path("tests") / "fixtures" / "multi-surface-claims.yaml"
|
|
64
|
+
)
|
|
65
|
+
ADR_DIR_RELATIVE: Final[Path] = Path("site") / "content" / "docs" / "adr"
|
|
66
|
+
CANONICAL_SURFACE: Final[str] = "AGENTS.md"
|
|
67
|
+
|
|
68
|
+
# Coverage parameters. The fixture's claims carry 3-5 tokens; the threshold
|
|
69
|
+
# triggers a "present" verdict when at least half the tokens AND at least
|
|
70
|
+
# two tokens match.
|
|
71
|
+
COVERAGE_THRESHOLD: Final[float] = 0.5
|
|
72
|
+
MIN_TOKEN_HITS: Final[int] = 2
|
|
73
|
+
|
|
74
|
+
# Negation markers used for contradiction detection. A surface whose
|
|
75
|
+
# matching paragraph carries any of these tokens immediately before a
|
|
76
|
+
# claim token is marked "negated"; a surface without any negation marker
|
|
77
|
+
# in the same window is "affirmative".
|
|
78
|
+
NEGATION_MARKERS: Final[tuple[str, ...]] = (
|
|
79
|
+
"never",
|
|
80
|
+
"must not",
|
|
81
|
+
"must never",
|
|
82
|
+
"shall not",
|
|
83
|
+
"no longer",
|
|
84
|
+
"do not",
|
|
85
|
+
"don't",
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Tokens that are themselves modal verbs or short directive words. Negation
|
|
89
|
+
# analysis skips these because matching them inside their own negation
|
|
90
|
+
# phrase ("MUST NOT" contains "MUST") produces false-positive contradictions
|
|
91
|
+
# whenever the canonical surface lists the modal verbs as policy vocabulary.
|
|
92
|
+
NEGATION_ANALYSIS_SKIP_TOKENS: Final[frozenset[str]] = frozenset(
|
|
93
|
+
{"must", "should", "may", "shall", "can", "will", "no", "never"}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Window before the matched token in which a negation marker is decisive.
|
|
97
|
+
# Restricting to a leading window (rather than ±80) avoids the
|
|
98
|
+
# "MUST NOT" → "MUST" self-shadowing pattern.
|
|
99
|
+
NEGATION_LEADING_WINDOW: Final[int] = 25
|
|
100
|
+
|
|
101
|
+
# Override marker per reconciliation rule §4.7.3.
|
|
102
|
+
OVERRIDE_RE: Final[re.Pattern[str]] = re.compile(
|
|
103
|
+
r"<!--\s*coherence-override:\s*([a-z0-9.\-]+)\s*-->"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
RULE_CLAIM_MISSING: Final[str] = "COHERENCE_CLAIM_MISSING"
|
|
107
|
+
RULE_CONTRADICTION: Final[str] = "COHERENCE_CONTRADICTION"
|
|
108
|
+
RULE_OVERRIDE_HONOURED: Final[str] = "COHERENCE_OVERRIDE_HONORED"
|
|
109
|
+
RULE_FIXTURE_ABSENT: Final[str] = "COHERENCE_FIXTURE_ABSENT"
|
|
110
|
+
RULE_PARTIAL_COVERAGE: Final[str] = "COHERENCE_PARTIAL_COVERAGE"
|
|
111
|
+
|
|
112
|
+
SEVERITY_ERROR: Final[str] = "error"
|
|
113
|
+
SEVERITY_WARNING: Final[str] = "warning"
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass(frozen=True)
|
|
117
|
+
class Finding:
|
|
118
|
+
"""One diagnostic finding emitted during the coherence check."""
|
|
119
|
+
|
|
120
|
+
line: int
|
|
121
|
+
match: str
|
|
122
|
+
context: str
|
|
123
|
+
rule: str
|
|
124
|
+
severity: str = SEVERITY_ERROR
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass(frozen=True)
|
|
128
|
+
class Claim:
|
|
129
|
+
"""One enumerated claim from the multi-surface-claims fixture."""
|
|
130
|
+
|
|
131
|
+
id: str
|
|
132
|
+
claim: str
|
|
133
|
+
required_in: tuple[str, ...]
|
|
134
|
+
optional_in: tuple[str, ...]
|
|
135
|
+
semantic_equivalence_tokens: tuple[str, ...]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
# Minimal YAML claim-list parser
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
# A constrained parser targeting the canonical fixture shape
|
|
142
|
+
# (`tests/fixtures/multi-surface-claims.yaml`). Avoiding a third-party
|
|
143
|
+
# dependency keeps the validator hot-loadable from the orchestrator without
|
|
144
|
+
# environment shaping.
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _parse_claims_yaml(text: str) -> list[Claim]:
|
|
148
|
+
"""Parse the canonical claim-list fixture into a list of ``Claim`` objects."""
|
|
149
|
+
lines = text.splitlines()
|
|
150
|
+
claims: list[Claim] = []
|
|
151
|
+
|
|
152
|
+
in_claims = False
|
|
153
|
+
current: dict[str, object] | None = None
|
|
154
|
+
current_list_key: str | None = None
|
|
155
|
+
|
|
156
|
+
def _flush(record: dict[str, object] | None) -> None:
|
|
157
|
+
if record is None:
|
|
158
|
+
return
|
|
159
|
+
claim_id = record.get("id")
|
|
160
|
+
statement = record.get("claim")
|
|
161
|
+
required = record.get("required-in") or []
|
|
162
|
+
optional = record.get("optional-in") or []
|
|
163
|
+
tokens = record.get("semantic-equivalence-tokens") or []
|
|
164
|
+
if not isinstance(claim_id, str) or not isinstance(statement, str):
|
|
165
|
+
return
|
|
166
|
+
if not isinstance(required, list) or not isinstance(optional, list):
|
|
167
|
+
return
|
|
168
|
+
if not isinstance(tokens, list):
|
|
169
|
+
return
|
|
170
|
+
claims.append(
|
|
171
|
+
Claim(
|
|
172
|
+
id=claim_id,
|
|
173
|
+
claim=statement,
|
|
174
|
+
required_in=tuple(str(s) for s in required),
|
|
175
|
+
optional_in=tuple(str(s) for s in optional),
|
|
176
|
+
semantic_equivalence_tokens=tuple(str(t) for t in tokens),
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
for raw in lines:
|
|
181
|
+
# Strip trailing comments while preserving quoted-string content.
|
|
182
|
+
stripped_for_test = raw.lstrip()
|
|
183
|
+
if stripped_for_test.startswith("#"):
|
|
184
|
+
continue
|
|
185
|
+
if not stripped_for_test:
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
if not in_claims:
|
|
189
|
+
if stripped_for_test.startswith("claims:"):
|
|
190
|
+
in_claims = True
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
# Inside `claims:` block. Indentation is the structural signal.
|
|
194
|
+
indent = len(raw) - len(raw.lstrip())
|
|
195
|
+
|
|
196
|
+
# New claim-record marker: ` - id: foo`
|
|
197
|
+
if indent == 2 and stripped_for_test.startswith("- "):
|
|
198
|
+
_flush(current)
|
|
199
|
+
current = {}
|
|
200
|
+
current_list_key = None
|
|
201
|
+
tail = stripped_for_test[2:]
|
|
202
|
+
if ":" in tail:
|
|
203
|
+
key, _, value = tail.partition(":")
|
|
204
|
+
key = key.strip()
|
|
205
|
+
value = value.strip()
|
|
206
|
+
current[key] = _strip_quotes(value)
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
if current is None:
|
|
210
|
+
continue
|
|
211
|
+
|
|
212
|
+
# Field line at indent 4 — either `key: value` or `key:` (list opener).
|
|
213
|
+
if indent == 4:
|
|
214
|
+
key, _, value = stripped_for_test.partition(":")
|
|
215
|
+
key = key.strip()
|
|
216
|
+
value = value.strip()
|
|
217
|
+
if value == "[]":
|
|
218
|
+
current[key] = []
|
|
219
|
+
current_list_key = None
|
|
220
|
+
elif value:
|
|
221
|
+
current[key] = _strip_quotes(value)
|
|
222
|
+
current_list_key = None
|
|
223
|
+
else:
|
|
224
|
+
current[key] = []
|
|
225
|
+
current_list_key = key
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
# List item at indent 6 — `- value`
|
|
229
|
+
if indent == 6 and stripped_for_test.startswith("- ") and current_list_key:
|
|
230
|
+
value = _strip_quotes(stripped_for_test[2:].strip())
|
|
231
|
+
target = current.get(current_list_key)
|
|
232
|
+
if isinstance(target, list):
|
|
233
|
+
target.append(value)
|
|
234
|
+
|
|
235
|
+
_flush(current)
|
|
236
|
+
return claims
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _strip_quotes(value: str) -> str:
|
|
240
|
+
"""Remove enclosing single or double quotes if present."""
|
|
241
|
+
if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'):
|
|
242
|
+
return value[1:-1]
|
|
243
|
+
return value
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# ---------------------------------------------------------------------------
|
|
247
|
+
# Surface analysis
|
|
248
|
+
# ---------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@dataclass(frozen=True)
|
|
252
|
+
class SurfacePresence:
|
|
253
|
+
"""Whether a claim is present in a surface, plus contradiction signals."""
|
|
254
|
+
|
|
255
|
+
present: bool
|
|
256
|
+
coverage: float
|
|
257
|
+
matched_tokens: tuple[str, ...]
|
|
258
|
+
negated: bool
|
|
259
|
+
matched_paragraph: str
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _surface_path(surface: str) -> Path:
|
|
263
|
+
"""Resolve a surface's on-disk path against the ecosystem root."""
|
|
264
|
+
return ECOSYSTEM_ROOT / surface
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _read_surface(surface: str) -> str | None:
|
|
268
|
+
"""Read a surface file, returning ``None`` when absent."""
|
|
269
|
+
target = _surface_path(surface)
|
|
270
|
+
if not target.is_file():
|
|
271
|
+
return None
|
|
272
|
+
return target.read_text(encoding="utf-8")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _find_best_paragraph(body: str, tokens: tuple[str, ...]) -> tuple[str, int]:
|
|
276
|
+
"""Locate the paragraph with the highest token-match count.
|
|
277
|
+
|
|
278
|
+
Paragraphs are blank-line-separated runs. Returns ``("", 0)`` when no
|
|
279
|
+
paragraph contains any token.
|
|
280
|
+
"""
|
|
281
|
+
paragraphs = re.split(r"\n\s*\n", body)
|
|
282
|
+
best_text = ""
|
|
283
|
+
best_count = 0
|
|
284
|
+
for para in paragraphs:
|
|
285
|
+
para_lower = para.lower()
|
|
286
|
+
count = sum(1 for token in tokens if token.lower() in para_lower)
|
|
287
|
+
if count > best_count:
|
|
288
|
+
best_count = count
|
|
289
|
+
best_text = para
|
|
290
|
+
return best_text, best_count
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _has_negation_near(paragraph: str, token: str) -> bool:
|
|
294
|
+
"""Return True when a negation marker leads *token* within the window.
|
|
295
|
+
|
|
296
|
+
The window is leading-only (the marker must appear before the token,
|
|
297
|
+
within ``NEGATION_LEADING_WINDOW`` characters of its start) to avoid
|
|
298
|
+
self-shadowing — a token that is itself part of a negation phrase
|
|
299
|
+
(e.g., ``MUST`` inside ``MUST NOT``) would otherwise always register
|
|
300
|
+
as negated.
|
|
301
|
+
"""
|
|
302
|
+
if not paragraph or not token:
|
|
303
|
+
return False
|
|
304
|
+
para_lower = paragraph.lower()
|
|
305
|
+
token_lower = token.lower()
|
|
306
|
+
idx = para_lower.find(token_lower)
|
|
307
|
+
if idx == -1:
|
|
308
|
+
return False
|
|
309
|
+
window_start = max(0, idx - NEGATION_LEADING_WINDOW)
|
|
310
|
+
window = para_lower[window_start:idx]
|
|
311
|
+
return any(marker in window for marker in NEGATION_MARKERS)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def _analyse_presence(body: str, claim: Claim) -> SurfacePresence:
|
|
315
|
+
"""Score *body* against *claim*'s semantic-equivalence tokens."""
|
|
316
|
+
tokens = claim.semantic_equivalence_tokens
|
|
317
|
+
if not tokens:
|
|
318
|
+
return SurfacePresence(
|
|
319
|
+
present=False,
|
|
320
|
+
coverage=0.0,
|
|
321
|
+
matched_tokens=(),
|
|
322
|
+
negated=False,
|
|
323
|
+
matched_paragraph="",
|
|
324
|
+
)
|
|
325
|
+
body_lower = body.lower()
|
|
326
|
+
matched = tuple(token for token in tokens if token.lower() in body_lower)
|
|
327
|
+
coverage = len(matched) / len(tokens)
|
|
328
|
+
present = coverage >= COVERAGE_THRESHOLD and len(matched) >= MIN_TOKEN_HITS
|
|
329
|
+
paragraph, _ = _find_best_paragraph(body, tokens)
|
|
330
|
+
negated = False
|
|
331
|
+
if present and matched:
|
|
332
|
+
# Filter modal verbs and short directive words from the negation
|
|
333
|
+
# check (see NEGATION_ANALYSIS_SKIP_TOKENS). A surface is negated
|
|
334
|
+
# against the claim only when EVERY remaining (non-modal) matched
|
|
335
|
+
# token sits in negation context — a single un-negated mention
|
|
336
|
+
# otherwise indicates the claim is asserted affirmatively somewhere.
|
|
337
|
+
analysis_tokens = tuple(
|
|
338
|
+
t for t in matched if t.lower() not in NEGATION_ANALYSIS_SKIP_TOKENS
|
|
339
|
+
)
|
|
340
|
+
if analysis_tokens:
|
|
341
|
+
negated = all(_has_negation_near(paragraph, t) for t in analysis_tokens)
|
|
342
|
+
return SurfacePresence(
|
|
343
|
+
present=present,
|
|
344
|
+
coverage=coverage,
|
|
345
|
+
matched_tokens=matched,
|
|
346
|
+
negated=negated,
|
|
347
|
+
matched_paragraph=paragraph,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _has_override_marker(body: str, claim_id: str) -> bool:
|
|
352
|
+
"""Return True when *body* carries an override marker for *claim_id*."""
|
|
353
|
+
return any(match.group(1) == claim_id for match in OVERRIDE_RE.finditer(body))
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _adr_dir_populated() -> bool:
|
|
357
|
+
"""Return True when the site ADR directory contains at least one ``*.md`` file."""
|
|
358
|
+
adr_dir = ECOSYSTEM_ROOT / ADR_DIR_RELATIVE
|
|
359
|
+
if not adr_dir.is_dir():
|
|
360
|
+
return False
|
|
361
|
+
return any(adr_dir.glob("*.md"))
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# ---------------------------------------------------------------------------
|
|
365
|
+
# Public check entry point
|
|
366
|
+
# ---------------------------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _evaluate_claim(
|
|
370
|
+
claim: Claim,
|
|
371
|
+
surface_bodies: dict[str, str],
|
|
372
|
+
) -> list[Finding]:
|
|
373
|
+
"""Per-claim coherence evaluation across required and optional surfaces."""
|
|
374
|
+
findings: list[Finding] = []
|
|
375
|
+
|
|
376
|
+
# Step 1 — assemble per-surface presence records.
|
|
377
|
+
presence: dict[str, SurfacePresence] = {}
|
|
378
|
+
overrides: dict[str, bool] = {}
|
|
379
|
+
for surface, body in surface_bodies.items():
|
|
380
|
+
presence[surface] = _analyse_presence(body, claim)
|
|
381
|
+
overrides[surface] = _has_override_marker(body, claim.id)
|
|
382
|
+
|
|
383
|
+
canonical_present = (
|
|
384
|
+
CANONICAL_SURFACE in presence and presence[CANONICAL_SURFACE].present
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Step 2 — required-in surfaces must contain the claim (unless override).
|
|
388
|
+
for surface in claim.required_in:
|
|
389
|
+
surface_body = surface_bodies.get(surface)
|
|
390
|
+
if surface_body is None:
|
|
391
|
+
findings.append(
|
|
392
|
+
Finding(
|
|
393
|
+
line=0,
|
|
394
|
+
match=surface,
|
|
395
|
+
context=(
|
|
396
|
+
f"required surface {surface!r} for claim {claim.id!r}"
|
|
397
|
+
f" is absent on disk"
|
|
398
|
+
),
|
|
399
|
+
rule=RULE_CLAIM_MISSING,
|
|
400
|
+
severity=SEVERITY_ERROR,
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
continue
|
|
404
|
+
record = presence[surface]
|
|
405
|
+
if record.present:
|
|
406
|
+
continue
|
|
407
|
+
if overrides[surface]:
|
|
408
|
+
findings.append(
|
|
409
|
+
Finding(
|
|
410
|
+
line=0,
|
|
411
|
+
match=surface,
|
|
412
|
+
context=(
|
|
413
|
+
f"surface {surface!r} carries an override marker for"
|
|
414
|
+
f" claim {claim.id!r}; the surface is exempt from the"
|
|
415
|
+
f" coherence check"
|
|
416
|
+
),
|
|
417
|
+
rule=RULE_OVERRIDE_HONOURED,
|
|
418
|
+
severity=SEVERITY_WARNING,
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
continue
|
|
422
|
+
# Pre-Phase-05A expected-state: when the canonical surface itself
|
|
423
|
+
# does not yet carry the claim, partial coverage is advisory rather
|
|
424
|
+
# than an error. The downstream refit landing the canonical claim
|
|
425
|
+
# promotes the finding to an error on the next run.
|
|
426
|
+
if not canonical_present and surface != CANONICAL_SURFACE:
|
|
427
|
+
findings.append(
|
|
428
|
+
Finding(
|
|
429
|
+
line=0,
|
|
430
|
+
match=surface,
|
|
431
|
+
context=(
|
|
432
|
+
f"claim {claim.id!r} present in {surface!r} but absent"
|
|
433
|
+
f" in canonical surface {CANONICAL_SURFACE!r};"
|
|
434
|
+
f" advisory pre-canonical-refit"
|
|
435
|
+
),
|
|
436
|
+
rule=RULE_PARTIAL_COVERAGE,
|
|
437
|
+
severity=SEVERITY_WARNING,
|
|
438
|
+
)
|
|
439
|
+
)
|
|
440
|
+
continue
|
|
441
|
+
if surface == CANONICAL_SURFACE:
|
|
442
|
+
findings.append(
|
|
443
|
+
Finding(
|
|
444
|
+
line=0,
|
|
445
|
+
match=surface,
|
|
446
|
+
context=(
|
|
447
|
+
f"claim {claim.id!r} absent in canonical surface"
|
|
448
|
+
f" {surface!r}; advisory pre-canonical-refit"
|
|
449
|
+
),
|
|
450
|
+
rule=RULE_PARTIAL_COVERAGE,
|
|
451
|
+
severity=SEVERITY_WARNING,
|
|
452
|
+
)
|
|
453
|
+
)
|
|
454
|
+
continue
|
|
455
|
+
findings.append(
|
|
456
|
+
Finding(
|
|
457
|
+
line=0,
|
|
458
|
+
match=surface,
|
|
459
|
+
context=(
|
|
460
|
+
f"claim {claim.id!r} required in {surface!r} but not"
|
|
461
|
+
f" detected (coverage {record.coverage:.2f},"
|
|
462
|
+
f" matched-tokens {list(record.matched_tokens)})"
|
|
463
|
+
),
|
|
464
|
+
rule=RULE_CLAIM_MISSING,
|
|
465
|
+
severity=SEVERITY_ERROR,
|
|
466
|
+
)
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
# Step 3 — optional-in surfaces only contribute contradictions; their
|
|
470
|
+
# absence is silent. (Inclusion-by-default is governed by the opt-in
|
|
471
|
+
# ratification record at `.audit/multi-surface-opt-in.yml`, not by this
|
|
472
|
+
# validator.)
|
|
473
|
+
|
|
474
|
+
# Step 4 — contradiction detection. A non-canonical surface whose
|
|
475
|
+
# matching paragraph fully negates the claim's tokens contradicts a
|
|
476
|
+
# canonical surface that asserts the claim affirmatively.
|
|
477
|
+
canonical_record = presence.get(CANONICAL_SURFACE)
|
|
478
|
+
if canonical_record is None or not canonical_record.present:
|
|
479
|
+
return findings
|
|
480
|
+
if canonical_record.negated:
|
|
481
|
+
# Canonical voice itself negates — coherence cannot be assessed
|
|
482
|
+
# against the canonical surface for this claim.
|
|
483
|
+
return findings
|
|
484
|
+
for surface, record in presence.items():
|
|
485
|
+
if surface == CANONICAL_SURFACE:
|
|
486
|
+
continue
|
|
487
|
+
if not record.present:
|
|
488
|
+
continue
|
|
489
|
+
if overrides[surface]:
|
|
490
|
+
continue
|
|
491
|
+
if record.negated:
|
|
492
|
+
findings.append(
|
|
493
|
+
Finding(
|
|
494
|
+
line=0,
|
|
495
|
+
match=surface,
|
|
496
|
+
context=(
|
|
497
|
+
f"surface {surface!r} negates claim {claim.id!r}"
|
|
498
|
+
f" while canonical surface {CANONICAL_SURFACE!r}"
|
|
499
|
+
f" asserts it affirmatively"
|
|
500
|
+
),
|
|
501
|
+
rule=RULE_CONTRADICTION,
|
|
502
|
+
severity=SEVERITY_ERROR,
|
|
503
|
+
)
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
return findings
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _is_in_scope(path: Path) -> bool:
|
|
510
|
+
"""Return True when *path* is the claims fixture or any tracked surface."""
|
|
511
|
+
try:
|
|
512
|
+
rel = path.resolve().relative_to(ECOSYSTEM_ROOT)
|
|
513
|
+
except ValueError:
|
|
514
|
+
return False
|
|
515
|
+
if rel == CLAIMS_FIXTURE_RELATIVE:
|
|
516
|
+
return True
|
|
517
|
+
fixture = ECOSYSTEM_ROOT / CLAIMS_FIXTURE_RELATIVE
|
|
518
|
+
if not fixture.is_file():
|
|
519
|
+
return False
|
|
520
|
+
surfaces: set[str] = set()
|
|
521
|
+
for claim in _parse_claims_yaml(fixture.read_text(encoding="utf-8")):
|
|
522
|
+
surfaces.update(claim.required_in)
|
|
523
|
+
surfaces.update(claim.optional_in)
|
|
524
|
+
return rel.as_posix() in surfaces
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def check(content: str, path: Path | None = None) -> GrepResult:
|
|
528
|
+
"""Validate shared-claim coherence across AI-conventions surfaces.
|
|
529
|
+
|
|
530
|
+
Hook-mode pass-through. When *path* is set and is neither the claims
|
|
531
|
+
fixture nor any surface enumerated in the fixture, the validator
|
|
532
|
+
returns ``passed=True`` immediately. The full tree-level scan runs on
|
|
533
|
+
in-scope dispatches and on CLI mode without a path argument.
|
|
534
|
+
|
|
535
|
+
Returns:
|
|
536
|
+
``GrepResult`` with ``passed=True`` when no error-severity findings
|
|
537
|
+
emit. Warning-severity findings (partial coverage pre-canonical-refit,
|
|
538
|
+
honored overrides) are reported but do not fail the verdict.
|
|
539
|
+
"""
|
|
540
|
+
# `content` is unused — interface parity with the orchestrator's
|
|
541
|
+
# _CheckCallable Protocol.
|
|
542
|
+
_ = content
|
|
543
|
+
if path is not None and not _is_in_scope(path):
|
|
544
|
+
return GrepResult(
|
|
545
|
+
grep=GREP_NAME,
|
|
546
|
+
path=str(path),
|
|
547
|
+
passed=True,
|
|
548
|
+
note="check skipped (scope not resolvable)",
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
fixture = ECOSYSTEM_ROOT / CLAIMS_FIXTURE_RELATIVE
|
|
552
|
+
if not fixture.is_file():
|
|
553
|
+
# In the repo checkout the fixture is load-bearing and its absence
|
|
554
|
+
# is an error. Outside the checkout (installed tree) the fixture is
|
|
555
|
+
# not shipped, so the absence is an advisory pass — there is no
|
|
556
|
+
# surface corpus to assert coherence against.
|
|
557
|
+
absence_severity = SEVERITY_ERROR if _IN_REPO_CHECKOUT else SEVERITY_WARNING
|
|
558
|
+
return GrepResult(
|
|
559
|
+
grep=GREP_NAME,
|
|
560
|
+
path=str(fixture),
|
|
561
|
+
passed=not _IN_REPO_CHECKOUT,
|
|
562
|
+
findings=[
|
|
563
|
+
Finding(
|
|
564
|
+
line=0,
|
|
565
|
+
match=str(CLAIMS_FIXTURE_RELATIVE.as_posix()),
|
|
566
|
+
context=(
|
|
567
|
+
f"claim-list fixture absent at"
|
|
568
|
+
f" {CLAIMS_FIXTURE_RELATIVE.as_posix()}"
|
|
569
|
+
),
|
|
570
|
+
rule=RULE_FIXTURE_ABSENT,
|
|
571
|
+
severity=absence_severity,
|
|
572
|
+
)
|
|
573
|
+
],
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
claims = _parse_claims_yaml(fixture.read_text(encoding="utf-8"))
|
|
577
|
+
|
|
578
|
+
# Cache surface bodies (a surface may appear in multiple claims).
|
|
579
|
+
surface_cache: dict[str, str] = {}
|
|
580
|
+
relevant_surfaces: set[str] = set()
|
|
581
|
+
for claim in claims:
|
|
582
|
+
relevant_surfaces.update(claim.required_in)
|
|
583
|
+
relevant_surfaces.update(claim.optional_in)
|
|
584
|
+
for surface in relevant_surfaces:
|
|
585
|
+
body = _read_surface(surface)
|
|
586
|
+
if body is not None:
|
|
587
|
+
surface_cache[surface] = body
|
|
588
|
+
|
|
589
|
+
findings: list[Finding] = []
|
|
590
|
+
for claim in claims:
|
|
591
|
+
findings.extend(_evaluate_claim(claim, surface_cache))
|
|
592
|
+
|
|
593
|
+
# Emit a single advisory if any override marker is in flight but
|
|
594
|
+
# The ADR directory is empty — the spec requires an ADR pairing.
|
|
595
|
+
has_overrides = any(f.rule == RULE_OVERRIDE_HONOURED for f in findings)
|
|
596
|
+
if has_overrides and not _adr_dir_populated():
|
|
597
|
+
findings.append(
|
|
598
|
+
Finding(
|
|
599
|
+
line=0,
|
|
600
|
+
match=str(ADR_DIR_RELATIVE.as_posix()),
|
|
601
|
+
context=(
|
|
602
|
+
"override marker(s) in flight but no ADR files exist at"
|
|
603
|
+
f" {ADR_DIR_RELATIVE.as_posix()}; pairing pending"
|
|
604
|
+
),
|
|
605
|
+
rule=RULE_OVERRIDE_HONOURED,
|
|
606
|
+
severity=SEVERITY_WARNING,
|
|
607
|
+
)
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
error_findings = [f for f in findings if f.severity == SEVERITY_ERROR]
|
|
611
|
+
return GrepResult(
|
|
612
|
+
grep=GREP_NAME,
|
|
613
|
+
path=str(fixture),
|
|
614
|
+
passed=not error_findings,
|
|
615
|
+
findings=findings,
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
if __name__ == "__main__":
|
|
620
|
+
sys.exit(run_grep(check, sys.argv))
|