@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,534 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
"""Validate plan-suite structure: suite-locality, closed vocab, numeric prefix.
|
|
4
|
+
|
|
5
|
+
Why this validator exists. The plan-suite layout discipline is declared
|
|
6
|
+
in prose across two rules but, until this matcher, no mechanical check
|
|
7
|
+
enforced it. ``rules/context-management-scratch.md`` 2 fixes the
|
|
8
|
+
suite-locality invariant (``_spec/`` / ``_inputs/`` / ``_outputs/`` exist
|
|
9
|
+
only as direct children of a suite folder), the disjoint purpose
|
|
10
|
+
vocabularies (``{spec}`` exclusive to ``_spec/``;
|
|
11
|
+
``{forge, notes, triage, draft, decisions, prose, requirements}``
|
|
12
|
+
exclusive to ``_inputs/``), and the ``_outputs/`` closed purpose set
|
|
13
|
+
(``{report, audit, rollup, metrics, export}`` or a kebab-topic name).
|
|
14
|
+
``rules/canonical-layout-reporting-tiers.md`` 2.1 fixes the
|
|
15
|
+
numeric-prefix discipline: ``NN-topic`` phase folders, ``NNL-subtopic``
|
|
16
|
+
sub-phase folders, and NO numeric prefix on suite-root singletons. This
|
|
17
|
+
validator operationalizes both rule sections as a single corpus-level
|
|
18
|
+
sweep over every ``<root>/.plans/<suite>/`` directory.
|
|
19
|
+
|
|
20
|
+
Detection strategy. The validator walks every immediate child of
|
|
21
|
+
``<root>/.plans/`` (each is a suite folder) and asserts, per suite:
|
|
22
|
+
|
|
23
|
+
1. Suite-locality: ``_spec/`` / ``_inputs/`` / ``_outputs/`` appear only
|
|
24
|
+
as DIRECT children of a suite folder, never at ``.plans/`` root and
|
|
25
|
+
never nested deeper inside the suite.
|
|
26
|
+
2. ``_spec/`` holds only ``spec.md`` at its root, plus the allowed
|
|
27
|
+
subdirectories ``supporting/`` / ``diagrams/`` / ``citations/``.
|
|
28
|
+
3. Disjoint purpose: no ``spec.md`` inside ``_inputs/``; no
|
|
29
|
+
``_inputs/``-vocabulary file (``forge.md`` / ``notes.md`` / ...)
|
|
30
|
+
inside ``_spec/``.
|
|
31
|
+
4. ``_outputs/`` top-level file purposes match the closed set
|
|
32
|
+
``{report, audit, rollup, metrics, export}-*`` OR a kebab-topic name
|
|
33
|
+
(kebab-topic names are explicitly allowed by the rule).
|
|
34
|
+
5. Numeric-prefix discipline: every ``phases/`` child directory matches
|
|
35
|
+
``NN-topic``; every sub-phase directory matches ``NNL-subtopic``;
|
|
36
|
+
suite-root singletons carry NO numeric prefix; a ``00-X.md`` with no
|
|
37
|
+
``01-X`` sibling outside ``phases/`` is a finding.
|
|
38
|
+
6. Phase coherence: every ``phases/NN-topic/`` contains a ``PHASE.md``.
|
|
39
|
+
|
|
40
|
+
Conservatism. The checks are deliberately low-false-positive. Unknown
|
|
41
|
+
underscore-prefixed directories (e.g. ``_notes/``) are NOT flagged --- the
|
|
42
|
+
rule's closed vocabulary governs only the three named directories, so a
|
|
43
|
+
sibling working directory outside that set carries no obligation under
|
|
44
|
+
this matcher. Kebab-topic filenames in ``_outputs/`` are accepted
|
|
45
|
+
wholesale (the rule admits them). The phase-folder regex tolerates any
|
|
46
|
+
kebab/alnum topic tail after the ``NN-`` prefix.
|
|
47
|
+
|
|
48
|
+
Exit semantics. Exits 0 (EXIT_PASS) when no findings; exits 2
|
|
49
|
+
(EXIT_FAIL) on any finding, matching the conformity-gate orchestrator's
|
|
50
|
+
EXIT_FAIL constant. When the root carries no ``.plans/`` directory the
|
|
51
|
+
validator passes vacuously (there is no suite to validate).
|
|
52
|
+
|
|
53
|
+
Bindings. Operationalizes ``rules/context-management-scratch.md`` 2
|
|
54
|
+
(suite-locality + closed vocabularies) and
|
|
55
|
+
``rules/canonical-layout-reporting-tiers.md`` 2.1 (numeric-prefix
|
|
56
|
+
discipline). Registered in ``gate.py`` STANDALONE_MODULES as
|
|
57
|
+
``plan-suite-structure-grep`` for ``--all`` and
|
|
58
|
+
``--check plan-suite-structure`` dispatch.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
from __future__ import annotations
|
|
62
|
+
|
|
63
|
+
import json
|
|
64
|
+
import re
|
|
65
|
+
import sys
|
|
66
|
+
from dataclasses import asdict, dataclass, field
|
|
67
|
+
from pathlib import Path
|
|
68
|
+
from typing import Final
|
|
69
|
+
|
|
70
|
+
GREP_NAME: Final[str] = "plan-suite-structure-grep"
|
|
71
|
+
RULE_ANCHOR: Final[str] = (
|
|
72
|
+
"context-management-scratch 2 (suite-locality + closed vocab) + "
|
|
73
|
+
"canonical-layout-reporting-tiers 2.1 (numeric-prefix discipline)"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
EXIT_PASS: Final[int] = 0
|
|
77
|
+
EXIT_FAIL: Final[int] = 2
|
|
78
|
+
|
|
79
|
+
# The three suite-local artifact-class directories. Each MUST appear only as a
|
|
80
|
+
# direct child of a suite folder, never at .plans/ root and never nested
|
|
81
|
+
# deeper inside the suite.
|
|
82
|
+
_ARTIFACT_CLASS_DIRS: Final[frozenset[str]] = frozenset(
|
|
83
|
+
{"_spec", "_inputs", "_outputs"}
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# `_spec/` closed vocabulary: the singleton `spec.md` plus three optional
|
|
87
|
+
# structural subdirectories. Anything else at the `_spec/` root is a finding.
|
|
88
|
+
_SPEC_SINGLETON: Final[str] = "spec.md"
|
|
89
|
+
_SPEC_ALLOWED_SUBDIRS: Final[frozenset[str]] = frozenset(
|
|
90
|
+
{"supporting", "diagrams", "citations"}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# `_inputs/` closed-purpose canonical filename vocabulary. These canonical names
|
|
94
|
+
# are exclusive to `_inputs/`; a `_inputs/`-vocabulary file appearing in `_spec/`
|
|
95
|
+
# is cross-contamination. Free-form kebab-topic files are also allowed in
|
|
96
|
+
# `_inputs/`, so this set is used only to detect `_spec/` contamination, never to
|
|
97
|
+
# reject `_inputs/` members.
|
|
98
|
+
_INPUTS_CANONICAL_NAMES: Final[frozenset[str]] = frozenset(
|
|
99
|
+
{
|
|
100
|
+
"forge.md",
|
|
101
|
+
"notes.md",
|
|
102
|
+
"triage.md",
|
|
103
|
+
"draft.md",
|
|
104
|
+
"decisions.md",
|
|
105
|
+
"prose.md",
|
|
106
|
+
"requirements.md",
|
|
107
|
+
}
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# `_outputs/` closed purpose set. A top-level `_outputs/` filename either has a
|
|
111
|
+
# purpose prefix from this set (`report-*`, `audit-*`, ...) OR is a kebab-topic
|
|
112
|
+
# name; both are allowed by the rule. Non-kebab, non-purpose-prefixed names are
|
|
113
|
+
# findings.
|
|
114
|
+
_OUTPUTS_PURPOSES: Final[frozenset[str]] = frozenset(
|
|
115
|
+
{"report", "audit", "rollup", "metrics", "export"}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Suite-root singletons that, by the numeric-prefix discipline, carry NO numeric
|
|
119
|
+
# prefix. They are siblings of one another, not an ordered sequence. The set is
|
|
120
|
+
# the documented root-singleton class plus the README a suite may maintain.
|
|
121
|
+
_SUITE_ROOT_SINGLETONS: Final[frozenset[str]] = frozenset(
|
|
122
|
+
{
|
|
123
|
+
"PREAMBLE.md",
|
|
124
|
+
"MASTER-PLAN.md",
|
|
125
|
+
"MASTER-INDEX.md",
|
|
126
|
+
"PROGRESS.md",
|
|
127
|
+
"PLAN-NOTES.md",
|
|
128
|
+
"COMPLETION.md",
|
|
129
|
+
"TRACE-MATRIX.md",
|
|
130
|
+
"README.md",
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Phase folder: `NN-topic` --- a two-or-more-digit ordinal prefix, a hyphen, then
|
|
135
|
+
# a kebab/alnum topic tail. Sub-phase folder: `NNL-subtopic` --- the same ordinal
|
|
136
|
+
# prefix immediately followed by one-or-more uppercase letters, then the hyphen
|
|
137
|
+
# and topic. Both regexes anchor the whole directory name.
|
|
138
|
+
_PHASE_DIR_RE: Final[re.Pattern[str]] = re.compile(r"^\d{2,}-[a-z0-9][a-z0-9-]*$")
|
|
139
|
+
_SUBPHASE_DIR_RE: Final[re.Pattern[str]] = re.compile(
|
|
140
|
+
r"^\d{2,}[A-Z]+-[a-z0-9][a-z0-9-]*$"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# A bare numeric-prefix on a Markdown filename: `NN-...`. Used to flag a
|
|
144
|
+
# decorative ordinal prefix on a file living OUTSIDE `phases/` whose sibling set
|
|
145
|
+
# carries no ordering sequence.
|
|
146
|
+
_NUMERIC_PREFIX_FILE_RE: Final[re.Pattern[str]] = re.compile(r"^(\d{2,})-(.+)$")
|
|
147
|
+
|
|
148
|
+
# Kebab-case filename body (before the suffix): lowercase alnum segments joined
|
|
149
|
+
# by single hyphens. Used to accept free-form `_outputs/` topic names.
|
|
150
|
+
_KEBAB_STEM_RE: Final[re.Pattern[str]] = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
151
|
+
|
|
152
|
+
# Date-stamped purpose form `purpose-YYYY-MM-DD`: the rule's canonical
|
|
153
|
+
# `_outputs/` filename pattern. The leading purpose token must be in the closed
|
|
154
|
+
# set; the trailing date is verified by shape.
|
|
155
|
+
_DATED_PURPOSE_RE: Final[re.Pattern[str]] = re.compile(r"^([a-z]+)-\d{4}-\d{2}-\d{2}$")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@dataclass(frozen=True)
|
|
159
|
+
class Finding:
|
|
160
|
+
"""One plan-suite structural violation."""
|
|
161
|
+
|
|
162
|
+
suite: str
|
|
163
|
+
path: str
|
|
164
|
+
issue: str
|
|
165
|
+
detail: str
|
|
166
|
+
rule: str = RULE_ANCHOR
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass(frozen=True)
|
|
170
|
+
class GrepResult:
|
|
171
|
+
"""Aggregated walk result for a single ``.plans/`` sweep."""
|
|
172
|
+
|
|
173
|
+
grep: str
|
|
174
|
+
root: str
|
|
175
|
+
suite_count: int
|
|
176
|
+
passed: bool
|
|
177
|
+
findings: list[Finding] = field(default_factory=list)
|
|
178
|
+
|
|
179
|
+
def to_json(self) -> str:
|
|
180
|
+
payload = {
|
|
181
|
+
"grep": self.grep,
|
|
182
|
+
"root": self.root,
|
|
183
|
+
"suite_count": self.suite_count,
|
|
184
|
+
"passed": self.passed,
|
|
185
|
+
"findings": [asdict(f) for f in self.findings],
|
|
186
|
+
}
|
|
187
|
+
return json.dumps(payload, indent=2)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _outputs_filename_conformant(stem: str) -> bool:
|
|
191
|
+
"""Return True iff an ``_outputs/`` top-level file stem is conformant.
|
|
192
|
+
|
|
193
|
+
Conformant when the stem is a dated purpose (``report-2026-06-16``), a
|
|
194
|
+
bare-purpose token (``report``), a purpose-prefixed name
|
|
195
|
+
(``report-foo``), or a free-form kebab-topic name (``i18n-gap``). The
|
|
196
|
+
rule admits kebab-topic names wholesale, so the kebab test is the broad
|
|
197
|
+
accept; the purpose tests exist to document the canonical forms.
|
|
198
|
+
"""
|
|
199
|
+
dated = _DATED_PURPOSE_RE.match(stem)
|
|
200
|
+
if dated is not None and dated.group(1) in _OUTPUTS_PURPOSES:
|
|
201
|
+
return True
|
|
202
|
+
# Kebab-topic (which also covers `report-foo`, `audit-bar`, and bare
|
|
203
|
+
# `report`) is the rule's admitted free-form form.
|
|
204
|
+
return _KEBAB_STEM_RE.match(stem) is not None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _check_spec_dir(suite: str, spec_dir: Path, findings: list[Finding]) -> None:
|
|
208
|
+
"""Validate a suite's ``_spec/`` directory closed vocabulary."""
|
|
209
|
+
for child in sorted(spec_dir.iterdir(), key=lambda p: p.name):
|
|
210
|
+
name = child.name
|
|
211
|
+
if child.is_dir():
|
|
212
|
+
if name not in _SPEC_ALLOWED_SUBDIRS:
|
|
213
|
+
findings.append(
|
|
214
|
+
Finding(
|
|
215
|
+
suite=suite,
|
|
216
|
+
path=f"_spec/{name}/",
|
|
217
|
+
issue="non-vocab _spec subdirectory",
|
|
218
|
+
detail=(
|
|
219
|
+
f"_spec/ admits only the subdirectories "
|
|
220
|
+
f"{sorted(_SPEC_ALLOWED_SUBDIRS)}; {name!r} is "
|
|
221
|
+
"outside the closed vocabulary"
|
|
222
|
+
),
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
continue
|
|
226
|
+
# A file at the _spec/ root.
|
|
227
|
+
if name in _INPUTS_CANONICAL_NAMES:
|
|
228
|
+
findings.append(
|
|
229
|
+
Finding(
|
|
230
|
+
suite=suite,
|
|
231
|
+
path=f"_spec/{name}",
|
|
232
|
+
issue="purpose-vocabulary contamination",
|
|
233
|
+
detail=(
|
|
234
|
+
f"{name!r} is an _inputs/ canonical-purpose name; the "
|
|
235
|
+
"purpose vocabularies are disjoint --- it must not live "
|
|
236
|
+
"in _spec/"
|
|
237
|
+
),
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
elif name != _SPEC_SINGLETON:
|
|
241
|
+
findings.append(
|
|
242
|
+
Finding(
|
|
243
|
+
suite=suite,
|
|
244
|
+
path=f"_spec/{name}",
|
|
245
|
+
issue="non-singleton _spec file",
|
|
246
|
+
detail=(
|
|
247
|
+
f"_spec/ holds only the singleton {_SPEC_SINGLETON!r} at "
|
|
248
|
+
f"its root; {name!r} is not the authoritative spec file"
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _check_inputs_dir(suite: str, inputs_dir: Path, findings: list[Finding]) -> None:
|
|
255
|
+
"""Validate a suite's ``_inputs/`` directory for ``_spec/`` contamination.
|
|
256
|
+
|
|
257
|
+
The only disjoint-vocabulary violation detectable inside ``_inputs/`` is a
|
|
258
|
+
``spec.md`` (the ``_spec/`` singleton) appearing here; everything else in
|
|
259
|
+
``_inputs/`` is admitted (canonical-purpose names OR free-form kebab topics).
|
|
260
|
+
"""
|
|
261
|
+
spec_here = inputs_dir / _SPEC_SINGLETON
|
|
262
|
+
if spec_here.is_file():
|
|
263
|
+
findings.append(
|
|
264
|
+
Finding(
|
|
265
|
+
suite=suite,
|
|
266
|
+
path=f"_inputs/{_SPEC_SINGLETON}",
|
|
267
|
+
issue="purpose-vocabulary contamination",
|
|
268
|
+
detail=(
|
|
269
|
+
f"{_SPEC_SINGLETON!r} is the _spec/ singleton; the purpose "
|
|
270
|
+
"vocabularies are disjoint --- it must not live in _inputs/. "
|
|
271
|
+
"Promote it to _spec/ per the forge->spec lifecycle"
|
|
272
|
+
),
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _check_outputs_dir(suite: str, outputs_dir: Path, findings: list[Finding]) -> None:
|
|
278
|
+
"""Validate a suite's ``_outputs/`` top-level filename purposes."""
|
|
279
|
+
for child in sorted(outputs_dir.iterdir(), key=lambda p: p.name):
|
|
280
|
+
if child.is_dir():
|
|
281
|
+
# Sub-directory mirrors (e.g. `<phase-slug>/REPORT.md`) are allowed;
|
|
282
|
+
# their names are kebab phase-slugs --- accept conservatively.
|
|
283
|
+
continue
|
|
284
|
+
stem = child.stem
|
|
285
|
+
if not _outputs_filename_conformant(stem):
|
|
286
|
+
findings.append(
|
|
287
|
+
Finding(
|
|
288
|
+
suite=suite,
|
|
289
|
+
path=f"_outputs/{child.name}",
|
|
290
|
+
issue="non-vocab _outputs filename",
|
|
291
|
+
detail=(
|
|
292
|
+
f"_outputs/ filenames carry a closed purpose prefix "
|
|
293
|
+
f"{sorted(_OUTPUTS_PURPOSES)} (optionally date-stamped) "
|
|
294
|
+
"or a kebab-topic name; "
|
|
295
|
+
f"{child.name!r} matches neither"
|
|
296
|
+
),
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _check_phases_dir(suite: str, phases_dir: Path, findings: list[Finding]) -> None:
|
|
302
|
+
"""Validate a suite's ``phases/`` numeric-prefix + PHASE.md discipline."""
|
|
303
|
+
for child in sorted(phases_dir.iterdir(), key=lambda p: p.name):
|
|
304
|
+
if not child.is_dir():
|
|
305
|
+
# A stray file directly under phases/ is not a phase folder; the
|
|
306
|
+
# phase apparatus expects only NN-topic directories here.
|
|
307
|
+
findings.append(
|
|
308
|
+
Finding(
|
|
309
|
+
suite=suite,
|
|
310
|
+
path=f"phases/{child.name}",
|
|
311
|
+
issue="non-directory under phases/",
|
|
312
|
+
detail=(
|
|
313
|
+
"phases/ contains only NN-topic phase folders; "
|
|
314
|
+
f"{child.name!r} is a stray file"
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
continue
|
|
319
|
+
if _PHASE_DIR_RE.match(child.name) is None:
|
|
320
|
+
findings.append(
|
|
321
|
+
Finding(
|
|
322
|
+
suite=suite,
|
|
323
|
+
path=f"phases/{child.name}/",
|
|
324
|
+
issue="phase folder not NN-topic",
|
|
325
|
+
detail=(
|
|
326
|
+
f"every phases/ child folder must match NN-topic "
|
|
327
|
+
f"(e.g. 01-discovery); {child.name!r} does not"
|
|
328
|
+
),
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
continue
|
|
332
|
+
# Phase coherence: every phase folder carries a PHASE.md.
|
|
333
|
+
if not (child / "PHASE.md").is_file():
|
|
334
|
+
findings.append(
|
|
335
|
+
Finding(
|
|
336
|
+
suite=suite,
|
|
337
|
+
path=f"phases/{child.name}/PHASE.md",
|
|
338
|
+
issue="phase missing PHASE.md",
|
|
339
|
+
detail=(
|
|
340
|
+
f"phase folder {child.name!r} contains no PHASE.md; "
|
|
341
|
+
"every phase declares its scope/tasks/verification there"
|
|
342
|
+
),
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
# Sub-phase folders (directories inside a phase folder) must match
|
|
346
|
+
# NNL-subtopic. Non-matching sub-directories are findings; files
|
|
347
|
+
# (PHASE.md, REPORT.md, ...) are ignored.
|
|
348
|
+
for sub in sorted(child.iterdir(), key=lambda p: p.name):
|
|
349
|
+
if not sub.is_dir():
|
|
350
|
+
continue
|
|
351
|
+
if _SUBPHASE_DIR_RE.match(sub.name) is None:
|
|
352
|
+
findings.append(
|
|
353
|
+
Finding(
|
|
354
|
+
suite=suite,
|
|
355
|
+
path=f"phases/{child.name}/{sub.name}/",
|
|
356
|
+
issue="sub-phase folder not NNL-subtopic",
|
|
357
|
+
detail=(
|
|
358
|
+
"every sub-phase folder must match NNL-subtopic "
|
|
359
|
+
f"(e.g. 02A-foo); {sub.name!r} does not"
|
|
360
|
+
),
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def _check_root_numeric_prefixes(
|
|
366
|
+
suite: str, suite_dir: Path, findings: list[Finding]
|
|
367
|
+
) -> None:
|
|
368
|
+
"""Flag decorative numeric prefixes on suite-root files.
|
|
369
|
+
|
|
370
|
+
A suite-root ``NN-X.md`` file outside ``phases/`` carries a numeric prefix
|
|
371
|
+
only when it is part of an ordered sequence --- i.e. an ``NN-X`` sibling
|
|
372
|
+
exists sharing the same topic tail. A lone ``00-X.md`` with no ``01-X``
|
|
373
|
+
sibling is a decorative prefix and a finding per the numeric-prefix
|
|
374
|
+
discipline.
|
|
375
|
+
"""
|
|
376
|
+
prefixed: list[tuple[Path, str]] = []
|
|
377
|
+
for child in suite_dir.iterdir():
|
|
378
|
+
if not child.is_file():
|
|
379
|
+
continue
|
|
380
|
+
match = _NUMERIC_PREFIX_FILE_RE.match(child.name)
|
|
381
|
+
if match is not None:
|
|
382
|
+
prefixed.append((child, match.group(2)))
|
|
383
|
+
# Group by topic tail; a tail with a single member is a lone decorative
|
|
384
|
+
# prefix. A known suite-root singleton that somehow carries a prefix is
|
|
385
|
+
# always a finding (it should never be prefixed at all).
|
|
386
|
+
tails: dict[str, int] = {}
|
|
387
|
+
for _child, tail in prefixed:
|
|
388
|
+
tails[tail] = tails.get(tail, 0) + 1
|
|
389
|
+
for child, tail in prefixed:
|
|
390
|
+
is_lone = tails[tail] < 2
|
|
391
|
+
is_singleton = child.name in _SUITE_ROOT_SINGLETONS or any(
|
|
392
|
+
child.name.endswith(s) for s in _SUITE_ROOT_SINGLETONS
|
|
393
|
+
)
|
|
394
|
+
if is_lone or is_singleton:
|
|
395
|
+
findings.append(
|
|
396
|
+
Finding(
|
|
397
|
+
suite=suite,
|
|
398
|
+
path=child.name,
|
|
399
|
+
issue="decorative numeric prefix on suite-root file",
|
|
400
|
+
detail=(
|
|
401
|
+
"numeric prefixes convey ordering and belong only on "
|
|
402
|
+
"phases/ folders with a sibling sequence; "
|
|
403
|
+
f"{child.name!r} is a suite-root singleton with no "
|
|
404
|
+
"ordered sibling, so the prefix is meaningless decoration"
|
|
405
|
+
),
|
|
406
|
+
)
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def _scan_nested_artifact_dirs(
|
|
411
|
+
suite: str, suite_dir: Path, findings: list[Finding]
|
|
412
|
+
) -> None:
|
|
413
|
+
"""Flag ``_spec/`` / ``_inputs/`` / ``_outputs/`` nested below the suite root.
|
|
414
|
+
|
|
415
|
+
The suite-locality invariant fixes these three directories as DIRECT
|
|
416
|
+
children of the suite folder. An instance appearing deeper inside the suite
|
|
417
|
+
(e.g. ``phases/NN-x/_spec/``) is a suite-locality violation.
|
|
418
|
+
"""
|
|
419
|
+
for path in suite_dir.rglob("*"):
|
|
420
|
+
if not path.is_dir():
|
|
421
|
+
continue
|
|
422
|
+
if path.name not in _ARTIFACT_CLASS_DIRS:
|
|
423
|
+
continue
|
|
424
|
+
# The direct-child instances are the canonical ones; skip them.
|
|
425
|
+
if path.parent == suite_dir:
|
|
426
|
+
continue
|
|
427
|
+
rel = path.relative_to(suite_dir).as_posix()
|
|
428
|
+
findings.append(
|
|
429
|
+
Finding(
|
|
430
|
+
suite=suite,
|
|
431
|
+
path=f"{rel}/",
|
|
432
|
+
issue="nested artifact-class directory",
|
|
433
|
+
detail=(
|
|
434
|
+
f"{path.name}/ must be a DIRECT child of the suite folder; "
|
|
435
|
+
f"found nested at {rel}/ --- suite-locality violation"
|
|
436
|
+
),
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _check_suite(suite_dir: Path, findings: list[Finding]) -> None:
|
|
442
|
+
"""Validate a single ``<root>/.plans/<suite>/`` directory."""
|
|
443
|
+
suite = suite_dir.name
|
|
444
|
+
spec_dir = suite_dir / "_spec"
|
|
445
|
+
inputs_dir = suite_dir / "_inputs"
|
|
446
|
+
outputs_dir = suite_dir / "_outputs"
|
|
447
|
+
phases_dir = suite_dir / "phases"
|
|
448
|
+
if spec_dir.is_dir():
|
|
449
|
+
_check_spec_dir(suite, spec_dir, findings)
|
|
450
|
+
if inputs_dir.is_dir():
|
|
451
|
+
_check_inputs_dir(suite, inputs_dir, findings)
|
|
452
|
+
if outputs_dir.is_dir():
|
|
453
|
+
_check_outputs_dir(suite, outputs_dir, findings)
|
|
454
|
+
if phases_dir.is_dir():
|
|
455
|
+
_check_phases_dir(suite, phases_dir, findings)
|
|
456
|
+
_check_root_numeric_prefixes(suite, suite_dir, findings)
|
|
457
|
+
_scan_nested_artifact_dirs(suite, suite_dir, findings)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def _check_plans_tree(plans_root: Path, label: str, findings: list[Finding]) -> int:
|
|
461
|
+
"""Validate every suite under a single canonical plans tree.
|
|
462
|
+
|
|
463
|
+
*plans_root* is the sole canonical project-local plans location
|
|
464
|
+
(``<root>/.apothem/plans``); *label* is the display-prefix used in
|
|
465
|
+
root-level findings. Returns the number of suite folders inspected.
|
|
466
|
+
"""
|
|
467
|
+
suite_count = 0
|
|
468
|
+
if not plans_root.is_dir():
|
|
469
|
+
return suite_count
|
|
470
|
+
# Root-level suite-locality: the artifact-class directories must NOT
|
|
471
|
+
# appear directly under the plans tree (only inside a suite folder).
|
|
472
|
+
for entry in sorted(plans_root.iterdir(), key=lambda p: p.name):
|
|
473
|
+
if entry.is_dir() and entry.name in _ARTIFACT_CLASS_DIRS:
|
|
474
|
+
findings.append(
|
|
475
|
+
Finding(
|
|
476
|
+
suite=f"<{label} root>",
|
|
477
|
+
path=f"{label}/{entry.name}/",
|
|
478
|
+
issue=f"artifact-class directory at {label} root",
|
|
479
|
+
detail=(
|
|
480
|
+
f"{entry.name}/ must live inside a suite folder, not "
|
|
481
|
+
f"directly under {label}/ --- suite-locality violation"
|
|
482
|
+
),
|
|
483
|
+
)
|
|
484
|
+
)
|
|
485
|
+
# Each remaining sub-directory of the plans tree is a suite folder.
|
|
486
|
+
for suite_dir in sorted(plans_root.iterdir(), key=lambda p: p.name):
|
|
487
|
+
if not suite_dir.is_dir():
|
|
488
|
+
continue
|
|
489
|
+
if suite_dir.name in _ARTIFACT_CLASS_DIRS:
|
|
490
|
+
# Already flagged above as a root-level violation; not a suite.
|
|
491
|
+
continue
|
|
492
|
+
suite_count += 1
|
|
493
|
+
_check_suite(suite_dir, findings)
|
|
494
|
+
return suite_count
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def check(root: Path) -> GrepResult:
|
|
498
|
+
"""Walk every suite under the canonical project-local plans tree.
|
|
499
|
+
|
|
500
|
+
The sole canonical project-local plans location is ``<root>/.apothem/plans``
|
|
501
|
+
(the shared Apothem working directory's plans child); a legacy
|
|
502
|
+
``<root>/.plans`` tree is no longer canonical — operators upgrade it via
|
|
503
|
+
``apothem migrate-workspace``. Suites under the canonical tree are validated,
|
|
504
|
+
flagging structural violations.
|
|
505
|
+
"""
|
|
506
|
+
findings: list[Finding] = []
|
|
507
|
+
suite_count = 0
|
|
508
|
+
suite_count += _check_plans_tree(
|
|
509
|
+
root / ".apothem" / "plans", ".apothem/plans", findings
|
|
510
|
+
)
|
|
511
|
+
return GrepResult(
|
|
512
|
+
grep=GREP_NAME,
|
|
513
|
+
root=str(root),
|
|
514
|
+
suite_count=suite_count,
|
|
515
|
+
passed=not findings,
|
|
516
|
+
findings=findings,
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _read_input(argv: list[str]) -> Path:
|
|
521
|
+
if len(argv) >= 2:
|
|
522
|
+
return Path(argv[1])
|
|
523
|
+
return Path.cwd()
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _main(argv: list[str]) -> int:
|
|
527
|
+
root = _read_input(argv)
|
|
528
|
+
result = check(root)
|
|
529
|
+
print(result.to_json())
|
|
530
|
+
return EXIT_PASS if result.passed else EXIT_FAIL
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
if __name__ == "__main__":
|
|
534
|
+
sys.exit(_main(sys.argv))
|