@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.
Files changed (674) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/LICENSE +21 -0
  3. package/LICENSES/MIT.txt +18 -0
  4. package/LICENSES/PSF-2.0.txt +47 -0
  5. package/README.md +549 -0
  6. package/bin/README.md +37 -0
  7. package/bin/apothem.mjs +78 -0
  8. package/package.json +75 -0
  9. package/pyproject.toml +347 -0
  10. package/src/apothem/README.md +52 -0
  11. package/src/apothem/__init__.py +66 -0
  12. package/src/apothem/__main__.py +28 -0
  13. package/src/apothem/_vendor/.keep +0 -0
  14. package/src/apothem/_vendor/__init__.py +25 -0
  15. package/src/apothem/_vendor/attr/__init__.py +104 -0
  16. package/src/apothem/_vendor/attr/__init__.pyi +389 -0
  17. package/src/apothem/_vendor/attr/_cmp.py +160 -0
  18. package/src/apothem/_vendor/attr/_cmp.pyi +13 -0
  19. package/src/apothem/_vendor/attr/_compat.py +99 -0
  20. package/src/apothem/_vendor/attr/_config.py +31 -0
  21. package/src/apothem/_vendor/attr/_funcs.py +497 -0
  22. package/src/apothem/_vendor/attr/_make.py +3406 -0
  23. package/src/apothem/_vendor/attr/_next_gen.py +674 -0
  24. package/src/apothem/_vendor/attr/_typing_compat.pyi +15 -0
  25. package/src/apothem/_vendor/attr/_version_info.py +89 -0
  26. package/src/apothem/_vendor/attr/_version_info.pyi +9 -0
  27. package/src/apothem/_vendor/attr/converters.py +162 -0
  28. package/src/apothem/_vendor/attr/converters.pyi +19 -0
  29. package/src/apothem/_vendor/attr/exceptions.py +95 -0
  30. package/src/apothem/_vendor/attr/exceptions.pyi +17 -0
  31. package/src/apothem/_vendor/attr/filters.py +72 -0
  32. package/src/apothem/_vendor/attr/filters.pyi +6 -0
  33. package/src/apothem/_vendor/attr/py.typed +0 -0
  34. package/src/apothem/_vendor/attr/setters.py +79 -0
  35. package/src/apothem/_vendor/attr/setters.pyi +20 -0
  36. package/src/apothem/_vendor/attr/validators.py +750 -0
  37. package/src/apothem/_vendor/attr/validators.pyi +140 -0
  38. package/src/apothem/_vendor/attr.LICENSE +21 -0
  39. package/src/apothem/_vendor/attrs/__init__.py +72 -0
  40. package/src/apothem/_vendor/attrs/__init__.pyi +314 -0
  41. package/src/apothem/_vendor/attrs/converters.py +3 -0
  42. package/src/apothem/_vendor/attrs/exceptions.py +3 -0
  43. package/src/apothem/_vendor/attrs/filters.py +3 -0
  44. package/src/apothem/_vendor/attrs/py.typed +0 -0
  45. package/src/apothem/_vendor/attrs/setters.py +3 -0
  46. package/src/apothem/_vendor/attrs/validators.py +3 -0
  47. package/src/apothem/_vendor/attrs.LICENSE +21 -0
  48. package/src/apothem/_vendor/jsonschema/__init__.py +120 -0
  49. package/src/apothem/_vendor/jsonschema/__main__.py +6 -0
  50. package/src/apothem/_vendor/jsonschema/_format.py +546 -0
  51. package/src/apothem/_vendor/jsonschema/_keywords.py +449 -0
  52. package/src/apothem/_vendor/jsonschema/_legacy_keywords.py +449 -0
  53. package/src/apothem/_vendor/jsonschema/_types.py +204 -0
  54. package/src/apothem/_vendor/jsonschema/_typing.py +29 -0
  55. package/src/apothem/_vendor/jsonschema/_utils.py +355 -0
  56. package/src/apothem/_vendor/jsonschema/benchmarks/__init__.py +5 -0
  57. package/src/apothem/_vendor/jsonschema/benchmarks/const_vs_enum.py +30 -0
  58. package/src/apothem/_vendor/jsonschema/benchmarks/contains.py +28 -0
  59. package/src/apothem/_vendor/jsonschema/benchmarks/import_benchmark.py +31 -0
  60. package/src/apothem/_vendor/jsonschema/benchmarks/issue232/issue.json +2653 -0
  61. package/src/apothem/_vendor/jsonschema/benchmarks/issue232.py +25 -0
  62. package/src/apothem/_vendor/jsonschema/benchmarks/json_schema_test_suite.py +12 -0
  63. package/src/apothem/_vendor/jsonschema/benchmarks/nested_schemas.py +56 -0
  64. package/src/apothem/_vendor/jsonschema/benchmarks/subcomponents.py +42 -0
  65. package/src/apothem/_vendor/jsonschema/benchmarks/unused_registry.py +35 -0
  66. package/src/apothem/_vendor/jsonschema/benchmarks/useless_applicator_schemas.py +106 -0
  67. package/src/apothem/_vendor/jsonschema/benchmarks/useless_keywords.py +32 -0
  68. package/src/apothem/_vendor/jsonschema/benchmarks/validator_creation.py +14 -0
  69. package/src/apothem/_vendor/jsonschema/cli.py +292 -0
  70. package/src/apothem/_vendor/jsonschema/exceptions.py +490 -0
  71. package/src/apothem/_vendor/jsonschema/protocols.py +230 -0
  72. package/src/apothem/_vendor/jsonschema/validators.py +1410 -0
  73. package/src/apothem/_vendor/jsonschema.LICENSE +19 -0
  74. package/src/apothem/_vendor/jsonschema_specifications/__init__.py +12 -0
  75. package/src/apothem/_vendor/jsonschema_specifications/_core.py +38 -0
  76. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/metaschema.json +42 -0
  77. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/applicator +56 -0
  78. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/content +17 -0
  79. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/core +57 -0
  80. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/format +14 -0
  81. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/meta-data +37 -0
  82. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft201909/vocabularies/validation +98 -0
  83. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/metaschema.json +58 -0
  84. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/applicator +48 -0
  85. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/content +17 -0
  86. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/core +51 -0
  87. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-annotation +14 -0
  88. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/format-assertion +14 -0
  89. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/meta-data +37 -0
  90. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/unevaluated +15 -0
  91. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft202012/vocabularies/validation +98 -0
  92. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft3/metaschema.json +172 -0
  93. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft4/metaschema.json +149 -0
  94. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft6/metaschema.json +153 -0
  95. package/src/apothem/_vendor/jsonschema_specifications/schemas/draft7/metaschema.json +166 -0
  96. package/src/apothem/_vendor/jsonschema_specifications.LICENSE +19 -0
  97. package/src/apothem/_vendor/referencing/__init__.py +7 -0
  98. package/src/apothem/_vendor/referencing/_attrs.py +31 -0
  99. package/src/apothem/_vendor/referencing/_attrs.pyi +21 -0
  100. package/src/apothem/_vendor/referencing/_core.py +739 -0
  101. package/src/apothem/_vendor/referencing/exceptions.py +165 -0
  102. package/src/apothem/_vendor/referencing/jsonschema.py +642 -0
  103. package/src/apothem/_vendor/referencing/py.typed +0 -0
  104. package/src/apothem/_vendor/referencing/retrieval.py +94 -0
  105. package/src/apothem/_vendor/referencing/typing.py +61 -0
  106. package/src/apothem/_vendor/referencing.LICENSE +19 -0
  107. package/src/apothem/_vendor/rpds/__init__.py +251 -0
  108. package/src/apothem/_vendor/typing_extensions.LICENSE +279 -0
  109. package/src/apothem/_vendor/typing_extensions.py +4317 -0
  110. package/src/apothem/_vendor/vendor.txt +22 -0
  111. package/src/apothem/_vendor/yaml/__init__.py +389 -0
  112. package/src/apothem/_vendor/yaml/composer.py +138 -0
  113. package/src/apothem/_vendor/yaml/constructor.py +748 -0
  114. package/src/apothem/_vendor/yaml/cyaml.py +100 -0
  115. package/src/apothem/_vendor/yaml/dumper.py +61 -0
  116. package/src/apothem/_vendor/yaml/emitter.py +1137 -0
  117. package/src/apothem/_vendor/yaml/error.py +74 -0
  118. package/src/apothem/_vendor/yaml/events.py +85 -0
  119. package/src/apothem/_vendor/yaml/loader.py +63 -0
  120. package/src/apothem/_vendor/yaml/nodes.py +48 -0
  121. package/src/apothem/_vendor/yaml/parser.py +588 -0
  122. package/src/apothem/_vendor/yaml/reader.py +185 -0
  123. package/src/apothem/_vendor/yaml/representer.py +388 -0
  124. package/src/apothem/_vendor/yaml/resolver.py +226 -0
  125. package/src/apothem/_vendor/yaml/scanner.py +1435 -0
  126. package/src/apothem/_vendor/yaml/serializer.py +110 -0
  127. package/src/apothem/_vendor/yaml/tokens.py +103 -0
  128. package/src/apothem/_vendor/yaml.LICENSE +20 -0
  129. package/src/apothem/agents/README.md +60 -0
  130. package/src/apothem/agents/codebase-explorer.md +91 -0
  131. package/src/apothem/agents/convention-auditor.md +93 -0
  132. package/src/apothem/agents/dependency-auditor.md +97 -0
  133. package/src/apothem/agents/fact-checker.md +84 -0
  134. package/src/apothem/agents/mcp-builder.md +86 -0
  135. package/src/apothem/agents/memory-auditor.md +93 -0
  136. package/src/apothem/agents/prompt-evaluator.md +87 -0
  137. package/src/apothem/agents/quality-gate.md +103 -0
  138. package/src/apothem/agents/refactor-surgeon.md +74 -0
  139. package/src/apothem/agents/research-scout.md +73 -0
  140. package/src/apothem/agents/security-scanner.md +83 -0
  141. package/src/apothem/agents/test-runner.md +84 -0
  142. package/src/apothem/audit/README.md +73 -0
  143. package/src/apothem/audit/_scan_lib.py +182 -0
  144. package/src/apothem/audit/analyze_graph.py +260 -0
  145. package/src/apothem/audit/build_capability_graph.py +607 -0
  146. package/src/apothem/audit/build_inventory.py +657 -0
  147. package/src/apothem/audit/build_plans_provenance.py +997 -0
  148. package/src/apothem/audit/check_links.py +389 -0
  149. package/src/apothem/audit/classify_artifacts.py +381 -0
  150. package/src/apothem/audit/deprecated-tokens.txt +10 -0
  151. package/src/apothem/audit/execute_plans_migration.py +491 -0
  152. package/src/apothem/audit/known-projects.txt +15 -0
  153. package/src/apothem/audit/render_capability_index.py +467 -0
  154. package/src/apothem/audit/render_inventory.py +405 -0
  155. package/src/apothem/audit/scan_ai_surfaces.py +1125 -0
  156. package/src/apothem/audit/scan_ai_surfaces_coarse.py +261 -0
  157. package/src/apothem/audit/scan_drift_features.py +143 -0
  158. package/src/apothem/audit/scan_frontmatter.py +293 -0
  159. package/src/apothem/audit/scan_header_coverage.py +1134 -0
  160. package/src/apothem/audit/scan_plan_leakage.py +540 -0
  161. package/src/apothem/audit/scan_plans_discipline.py +188 -0
  162. package/src/apothem/audit/scan_secrets_pii.py +245 -0
  163. package/src/apothem/audit/scan_stale_tokens.py +296 -0
  164. package/src/apothem/audit/synthesize_drift.py +205 -0
  165. package/src/apothem/benchmarks/README.md +33 -0
  166. package/src/apothem/benchmarks/__init__.py +3 -0
  167. package/src/apothem/benchmarks/bench_agents.py +63 -0
  168. package/src/apothem/benchmarks/bench_hooks.py +93 -0
  169. package/src/apothem/benchmarks/bench_install.py +58 -0
  170. package/src/apothem/benchmarks/bench_tests.py +93 -0
  171. package/src/apothem/benchmarks/bench_validate_ecosystem.py +84 -0
  172. package/src/apothem/cli/README.md +33 -0
  173. package/src/apothem/cli/__init__.py +229 -0
  174. package/src/apothem/cli/_cmd_completion.py +88 -0
  175. package/src/apothem/cli/_cmd_diff.py +181 -0
  176. package/src/apothem/cli/_cmd_doctor.py +143 -0
  177. package/src/apothem/cli/_cmd_harnesses.py +167 -0
  178. package/src/apothem/cli/_cmd_install.py +327 -0
  179. package/src/apothem/cli/_cmd_migrate_workspace.py +143 -0
  180. package/src/apothem/cli/_cmd_profile.py +341 -0
  181. package/src/apothem/cli/_cmd_status.py +180 -0
  182. package/src/apothem/cli/_cmd_uninstall.py +215 -0
  183. package/src/apothem/cli/_cmd_update.py +397 -0
  184. package/src/apothem/cli/_cmd_verify.py +194 -0
  185. package/src/apothem/cli/_common_flags.py +90 -0
  186. package/src/apothem/cli/_epilogs.py +296 -0
  187. package/src/apothem/cli/_helpers.py +857 -0
  188. package/src/apothem/cli/_json_formatter.py +21 -0
  189. package/src/apothem/cli/_materialize.py +376 -0
  190. package/src/apothem/cli/completions/apothem.bash +30 -0
  191. package/src/apothem/cli/completions/apothem.fish +19 -0
  192. package/src/apothem/cli/completions/apothem.ps1 +27 -0
  193. package/src/apothem/cli/completions/apothem.zsh +42 -0
  194. package/src/apothem/cli/reference_export.py +126 -0
  195. package/src/apothem/commands/README.md +125 -0
  196. package/src/apothem/commands/a11y-audit.md +203 -0
  197. package/src/apothem/commands/architecture-review.md +194 -0
  198. package/src/apothem/commands/audit.md +165 -0
  199. package/src/apothem/commands/code-audit.md +218 -0
  200. package/src/apothem/commands/code-review.md +193 -0
  201. package/src/apothem/commands/dependency-audit.md +209 -0
  202. package/src/apothem/commands/docs-review.md +199 -0
  203. package/src/apothem/commands/elevate.md +285 -0
  204. package/src/apothem/commands/eval.md +149 -0
  205. package/src/apothem/commands/fortress.md +172 -0
  206. package/src/apothem/commands/freshify.md +168 -0
  207. package/src/apothem/commands/github-deploy-fresh.md +178 -0
  208. package/src/apothem/commands/github-deploy-next.md +167 -0
  209. package/src/apothem/commands/perf-audit.md +198 -0
  210. package/src/apothem/commands/plan-amend.md +104 -0
  211. package/src/apothem/commands/plan-audit.md +127 -0
  212. package/src/apothem/commands/plan-design.md +257 -0
  213. package/src/apothem/commands/plan-execute.md +495 -0
  214. package/src/apothem/commands/plan-generate.md +351 -0
  215. package/src/apothem/commands/plan-review.md +555 -0
  216. package/src/apothem/commands/plan-spec.md +359 -0
  217. package/src/apothem/commands/plan-status.md +222 -0
  218. package/src/apothem/commands/plan.md +173 -0
  219. package/src/apothem/commands/projectify.md +142 -0
  220. package/src/apothem/commands/release-readiness.md +142 -0
  221. package/src/apothem/commands/research-analysis.md +241 -0
  222. package/src/apothem/commands/research-design.md +231 -0
  223. package/src/apothem/commands/research-disseminate.md +225 -0
  224. package/src/apothem/commands/research-experiment.md +232 -0
  225. package/src/apothem/commands/research-ideate.md +213 -0
  226. package/src/apothem/commands/research-paper.md +252 -0
  227. package/src/apothem/commands/research-proposal.md +220 -0
  228. package/src/apothem/commands/research-publish.md +255 -0
  229. package/src/apothem/commands/research-review.md +251 -0
  230. package/src/apothem/commands/research-sources.md +266 -0
  231. package/src/apothem/commands/research-spec.md +255 -0
  232. package/src/apothem/commands/research-synthesis.md +233 -0
  233. package/src/apothem/commands/research-theory.md +218 -0
  234. package/src/apothem/commands/research.md +181 -0
  235. package/src/apothem/commands/security-audit.md +196 -0
  236. package/src/apothem/commands/supply-chain-audit.md +192 -0
  237. package/src/apothem/commands/test-suite.md +146 -0
  238. package/src/apothem/commands/threat-model-audit.md +199 -0
  239. package/src/apothem/commands/ux-review.md +202 -0
  240. package/src/apothem/commands/workflow.md +162 -0
  241. package/src/apothem/conformity/README.md +173 -0
  242. package/src/apothem/conformity/__init__.py +1 -0
  243. package/src/apothem/conformity/_grep_base.py +93 -0
  244. package/src/apothem/conformity/agent_capability_grep.py +306 -0
  245. package/src/apothem/conformity/agents_md_coverage_grep.py +382 -0
  246. package/src/apothem/conformity/agnosticism_grep.py +311 -0
  247. package/src/apothem/conformity/always_on_budget_grep.py +318 -0
  248. package/src/apothem/conformity/bare_except_grep.py +115 -0
  249. package/src/apothem/conformity/binding_reciprocity_grep.py +151 -0
  250. package/src/apothem/conformity/brand_mark_grep.py +272 -0
  251. package/src/apothem/conformity/commented_out_code_grep.py +176 -0
  252. package/src/apothem/conformity/completion_claim_grep.py +169 -0
  253. package/src/apothem/conformity/conventional_commit_grep.py +319 -0
  254. package/src/apothem/conformity/copilot_instructions_presence_grep.py +324 -0
  255. package/src/apothem/conformity/cross_platform_matrix_grep.py +297 -0
  256. package/src/apothem/conformity/determinism_grep.py +306 -0
  257. package/src/apothem/conformity/diagram_staleness_grep.py +154 -0
  258. package/src/apothem/conformity/dynamism_grep.py +284 -0
  259. package/src/apothem/conformity/editorconfig_presence_grep.py +281 -0
  260. package/src/apothem/conformity/file_header_grep.py +502 -0
  261. package/src/apothem/conformity/freshness_token_grep.py +233 -0
  262. package/src/apothem/conformity/frontmatter_grep.py +274 -0
  263. package/src/apothem/conformity/frontmatter_value_grep.py +386 -0
  264. package/src/apothem/conformity/gate.py +1386 -0
  265. package/src/apothem/conformity/gitattributes_presence_grep.py +238 -0
  266. package/src/apothem/conformity/harden_runner_grep.py +320 -0
  267. package/src/apothem/conformity/hedging_grep.py +129 -0
  268. package/src/apothem/conformity/license_author_consistency_grep.py +204 -0
  269. package/src/apothem/conformity/link_check.py +327 -0
  270. package/src/apothem/conformity/magic_number_grep.py +182 -0
  271. package/src/apothem/conformity/multi_surface_coherence_grep.py +620 -0
  272. package/src/apothem/conformity/naming_grep.py +224 -0
  273. package/src/apothem/conformity/no_global_plans_grep.py +339 -0
  274. package/src/apothem/conformity/no_toplevel_docs_grep.py +120 -0
  275. package/src/apothem/conformity/oidc_trusted_publishing_grep.py +291 -0
  276. package/src/apothem/conformity/option_annotation_grep.py +352 -0
  277. package/src/apothem/conformity/orphan_output_grep.py +206 -0
  278. package/src/apothem/conformity/permissions_minimum_scope_grep.py +299 -0
  279. package/src/apothem/conformity/plain_language_grep.py +559 -0
  280. package/src/apothem/conformity/plan_next_step_consistency_grep.py +450 -0
  281. package/src/apothem/conformity/plan_suite_structure_grep.py +534 -0
  282. package/src/apothem/conformity/plans_discipline_language_grep.py +245 -0
  283. package/src/apothem/conformity/production_ready_pr_grep.py +200 -0
  284. package/src/apothem/conformity/recommend_next_step_grep.py +250 -0
  285. package/src/apothem/conformity/redundancy_grep.py +401 -0
  286. package/src/apothem/conformity/reference_token_grep.py +230 -0
  287. package/src/apothem/conformity/registry_capability_consistency_grep.py +368 -0
  288. package/src/apothem/conformity/secret_leak_grep.py +193 -0
  289. package/src/apothem/conformity/semver_stability_grep.py +358 -0
  290. package/src/apothem/conformity/smoke_install_grep.py +194 -0
  291. package/src/apothem/conformity/static_version_grep.py +284 -0
  292. package/src/apothem/conformity/token_efficiency_grep.py +185 -0
  293. package/src/apothem/conformity/unpinned_action_grep.py +115 -0
  294. package/src/apothem/conformity/user_confirm_grep.py +74 -0
  295. package/src/apothem/conformity/workflow_concurrency_grep.py +283 -0
  296. package/src/apothem/harnesses/README.md +63 -0
  297. package/src/apothem/harnesses/__init__.py +16 -0
  298. package/src/apothem/harnesses/_shared/README.md +36 -0
  299. package/src/apothem/harnesses/_shared/__init__.py +12 -0
  300. package/src/apothem/harnesses/_shared/install_driver.py +281 -0
  301. package/src/apothem/harnesses/_shared/install_driver_apply.py +612 -0
  302. package/src/apothem/harnesses/_shared/install_driver_backup.py +535 -0
  303. package/src/apothem/harnesses/_shared/install_driver_converters.py +310 -0
  304. package/src/apothem/harnesses/_shared/install_driver_lifecycle.py +495 -0
  305. package/src/apothem/harnesses/_shared/install_driver_materialize.py +675 -0
  306. package/src/apothem/harnesses/_shared/install_driver_merge.py +656 -0
  307. package/src/apothem/harnesses/_shared/install_driver_pathsafety.py +137 -0
  308. package/src/apothem/harnesses/_shared/install_driver_planvalidation.py +240 -0
  309. package/src/apothem/harnesses/_shared/install_driver_removal.py +366 -0
  310. package/src/apothem/harnesses/_shared/install_driver_treeops.py +248 -0
  311. package/src/apothem/harnesses/_shared/install_driver_types.py +330 -0
  312. package/src/apothem/harnesses/_shared/wrapper_factories.py +448 -0
  313. package/src/apothem/harnesses/antigravity/STANDARD-CONVENTION-PIN.md +91 -0
  314. package/src/apothem/harnesses/antigravity/__init__.py +70 -0
  315. package/src/apothem/harnesses/antigravity/capabilities.yml +40 -0
  316. package/src/apothem/harnesses/antigravity/install.py +63 -0
  317. package/src/apothem/harnesses/antigravity/templates/GEMINI.md +40 -0
  318. package/src/apothem/harnesses/antigravity/templates/plugin.json +5 -0
  319. package/src/apothem/harnesses/antigravity/uninstall.py +22 -0
  320. package/src/apothem/harnesses/antigravity/update.py +10 -0
  321. package/src/apothem/harnesses/antigravity/verify.py +11 -0
  322. package/src/apothem/harnesses/claude_code/STANDARD-CONVENTION-PIN.md +65 -0
  323. package/src/apothem/harnesses/claude_code/__init__.py +107 -0
  324. package/src/apothem/harnesses/claude_code/capabilities.yml +42 -0
  325. package/src/apothem/harnesses/claude_code/install.py +147 -0
  326. package/src/apothem/harnesses/claude_code/templates/settings.json +351 -0
  327. package/src/apothem/harnesses/claude_code/uninstall.py +23 -0
  328. package/src/apothem/harnesses/claude_code/update.py +10 -0
  329. package/src/apothem/harnesses/claude_code/verify.py +11 -0
  330. package/src/apothem/harnesses/codebuddy/STANDARD-CONVENTION-PIN.md +74 -0
  331. package/src/apothem/harnesses/codebuddy/__init__.py +49 -0
  332. package/src/apothem/harnesses/codebuddy/capabilities.yml +34 -0
  333. package/src/apothem/harnesses/codebuddy/install.py +40 -0
  334. package/src/apothem/harnesses/codebuddy/templates/apothem-rules.md +37 -0
  335. package/src/apothem/harnesses/codebuddy/uninstall.py +25 -0
  336. package/src/apothem/harnesses/codebuddy/update.py +10 -0
  337. package/src/apothem/harnesses/codebuddy/verify.py +11 -0
  338. package/src/apothem/harnesses/codex/STANDARD-CONVENTION-PIN.md +79 -0
  339. package/src/apothem/harnesses/codex/__init__.py +72 -0
  340. package/src/apothem/harnesses/codex/capabilities.yml +40 -0
  341. package/src/apothem/harnesses/codex/install.py +69 -0
  342. package/src/apothem/harnesses/codex/templates/AGENTS.md +40 -0
  343. package/src/apothem/harnesses/codex/templates/hooks.json +127 -0
  344. package/src/apothem/harnesses/codex/uninstall.py +23 -0
  345. package/src/apothem/harnesses/codex/update.py +10 -0
  346. package/src/apothem/harnesses/codex/verify.py +11 -0
  347. package/src/apothem/harnesses/cursor/STANDARD-CONVENTION-PIN.md +79 -0
  348. package/src/apothem/harnesses/cursor/__init__.py +48 -0
  349. package/src/apothem/harnesses/cursor/capabilities.yml +42 -0
  350. package/src/apothem/harnesses/cursor/install.py +38 -0
  351. package/src/apothem/harnesses/cursor/templates/apothem-rules.mdc +40 -0
  352. package/src/apothem/harnesses/cursor/uninstall.py +25 -0
  353. package/src/apothem/harnesses/cursor/update.py +10 -0
  354. package/src/apothem/harnesses/cursor/verify.py +11 -0
  355. package/src/apothem/harnesses/gemini_cli/STANDARD-CONVENTION-PIN.md +102 -0
  356. package/src/apothem/harnesses/gemini_cli/__init__.py +52 -0
  357. package/src/apothem/harnesses/gemini_cli/capabilities.yml +43 -0
  358. package/src/apothem/harnesses/gemini_cli/install.py +43 -0
  359. package/src/apothem/harnesses/gemini_cli/templates/GEMINI.md +38 -0
  360. package/src/apothem/harnesses/gemini_cli/uninstall.py +25 -0
  361. package/src/apothem/harnesses/gemini_cli/update.py +10 -0
  362. package/src/apothem/harnesses/gemini_cli/verify.py +11 -0
  363. package/src/apothem/harnesses/github_copilot/STANDARD-CONVENTION-PIN.md +84 -0
  364. package/src/apothem/harnesses/github_copilot/__init__.py +47 -0
  365. package/src/apothem/harnesses/github_copilot/capabilities.yml +42 -0
  366. package/src/apothem/harnesses/github_copilot/install.py +40 -0
  367. package/src/apothem/harnesses/github_copilot/templates/copilot-instructions.md +33 -0
  368. package/src/apothem/harnesses/github_copilot/uninstall.py +25 -0
  369. package/src/apothem/harnesses/github_copilot/update.py +10 -0
  370. package/src/apothem/harnesses/github_copilot/verify.py +11 -0
  371. package/src/apothem/harnesses/glm/STANDARD-CONVENTION-PIN.md +77 -0
  372. package/src/apothem/harnesses/glm/__init__.py +56 -0
  373. package/src/apothem/harnesses/glm/capabilities.yml +33 -0
  374. package/src/apothem/harnesses/glm/install.py +45 -0
  375. package/src/apothem/harnesses/glm/templates/glm.toml +58 -0
  376. package/src/apothem/harnesses/glm/uninstall.py +25 -0
  377. package/src/apothem/harnesses/glm/update.py +10 -0
  378. package/src/apothem/harnesses/glm/verify.py +11 -0
  379. package/src/apothem/harnesses/hermes/STANDARD-CONVENTION-PIN.md +57 -0
  380. package/src/apothem/harnesses/hermes/__init__.py +33 -0
  381. package/src/apothem/harnesses/hermes/capabilities.yml +36 -0
  382. package/src/apothem/harnesses/hermes/install.py +17 -0
  383. package/src/apothem/harnesses/hermes/materializer.py +35 -0
  384. package/src/apothem/harnesses/hermes/uninstall.py +33 -0
  385. package/src/apothem/harnesses/hermes/update.py +10 -0
  386. package/src/apothem/harnesses/hermes/verify.py +11 -0
  387. package/src/apothem/harnesses/kimi_code/STANDARD-CONVENTION-PIN.md +128 -0
  388. package/src/apothem/harnesses/kimi_code/__init__.py +59 -0
  389. package/src/apothem/harnesses/kimi_code/capabilities.yml +40 -0
  390. package/src/apothem/harnesses/kimi_code/install.py +42 -0
  391. package/src/apothem/harnesses/kimi_code/templates/AGENTS.md +43 -0
  392. package/src/apothem/harnesses/kimi_code/uninstall.py +27 -0
  393. package/src/apothem/harnesses/kimi_code/update.py +10 -0
  394. package/src/apothem/harnesses/kimi_code/verify.py +11 -0
  395. package/src/apothem/harnesses/kiro/STANDARD-CONVENTION-PIN.md +77 -0
  396. package/src/apothem/harnesses/kiro/__init__.py +49 -0
  397. package/src/apothem/harnesses/kiro/capabilities.yml +36 -0
  398. package/src/apothem/harnesses/kiro/install.py +39 -0
  399. package/src/apothem/harnesses/kiro/templates/apothem-rules.md +36 -0
  400. package/src/apothem/harnesses/kiro/uninstall.py +25 -0
  401. package/src/apothem/harnesses/kiro/update.py +10 -0
  402. package/src/apothem/harnesses/kiro/verify.py +11 -0
  403. package/src/apothem/harnesses/open_claw/STANDARD-CONVENTION-PIN.md +62 -0
  404. package/src/apothem/harnesses/open_claw/__init__.py +35 -0
  405. package/src/apothem/harnesses/open_claw/capabilities.yml +35 -0
  406. package/src/apothem/harnesses/open_claw/install.py +17 -0
  407. package/src/apothem/harnesses/open_claw/materializer.py +36 -0
  408. package/src/apothem/harnesses/open_claw/uninstall.py +32 -0
  409. package/src/apothem/harnesses/open_claw/update.py +10 -0
  410. package/src/apothem/harnesses/open_claw/verify.py +11 -0
  411. package/src/apothem/harnesses/opencode/STANDARD-CONVENTION-PIN.md +76 -0
  412. package/src/apothem/harnesses/opencode/__init__.py +35 -0
  413. package/src/apothem/harnesses/opencode/capabilities.yml +43 -0
  414. package/src/apothem/harnesses/opencode/install.py +17 -0
  415. package/src/apothem/harnesses/opencode/materializer.py +31 -0
  416. package/src/apothem/harnesses/opencode/uninstall.py +34 -0
  417. package/src/apothem/harnesses/opencode/update.py +10 -0
  418. package/src/apothem/harnesses/opencode/verify.py +11 -0
  419. package/src/apothem/harnesses/qwen_code/STANDARD-CONVENTION-PIN.md +87 -0
  420. package/src/apothem/harnesses/qwen_code/__init__.py +37 -0
  421. package/src/apothem/harnesses/qwen_code/capabilities.yml +43 -0
  422. package/src/apothem/harnesses/qwen_code/install.py +19 -0
  423. package/src/apothem/harnesses/qwen_code/materializer.py +174 -0
  424. package/src/apothem/harnesses/qwen_code/templates/QWEN.md +30 -0
  425. package/src/apothem/harnesses/qwen_code/uninstall.py +34 -0
  426. package/src/apothem/harnesses/qwen_code/update.py +10 -0
  427. package/src/apothem/harnesses/qwen_code/verify.py +11 -0
  428. package/src/apothem/harnesses/trae/STANDARD-CONVENTION-PIN.md +70 -0
  429. package/src/apothem/harnesses/trae/__init__.py +49 -0
  430. package/src/apothem/harnesses/trae/capabilities.yml +34 -0
  431. package/src/apothem/harnesses/trae/install.py +38 -0
  432. package/src/apothem/harnesses/trae/templates/apothem-rules.md +37 -0
  433. package/src/apothem/harnesses/trae/uninstall.py +25 -0
  434. package/src/apothem/harnesses/trae/update.py +10 -0
  435. package/src/apothem/harnesses/trae/verify.py +11 -0
  436. package/src/apothem/harnesses/windsurf/STANDARD-CONVENTION-PIN.md +91 -0
  437. package/src/apothem/harnesses/windsurf/__init__.py +52 -0
  438. package/src/apothem/harnesses/windsurf/capabilities.yml +40 -0
  439. package/src/apothem/harnesses/windsurf/install.py +41 -0
  440. package/src/apothem/harnesses/windsurf/templates/apothem-rules.md +37 -0
  441. package/src/apothem/harnesses/windsurf/uninstall.py +25 -0
  442. package/src/apothem/harnesses/windsurf/update.py +10 -0
  443. package/src/apothem/harnesses/windsurf/verify.py +11 -0
  444. package/src/apothem/harnesses/zed/STANDARD-CONVENTION-PIN.md +92 -0
  445. package/src/apothem/harnesses/zed/__init__.py +57 -0
  446. package/src/apothem/harnesses/zed/capabilities.yml +38 -0
  447. package/src/apothem/harnesses/zed/install.py +41 -0
  448. package/src/apothem/harnesses/zed/templates/apothem-rules.md +32 -0
  449. package/src/apothem/harnesses/zed/uninstall.py +28 -0
  450. package/src/apothem/harnesses/zed/update.py +10 -0
  451. package/src/apothem/harnesses/zed/verify.py +11 -0
  452. package/src/apothem/hooks/README.md +81 -0
  453. package/src/apothem/hooks/__init__.py +24 -0
  454. package/src/apothem/hooks/askuserquestion_validator.py +380 -0
  455. package/src/apothem/hooks/dispatch.py +296 -0
  456. package/src/apothem/hooks/emit_hook_context.py +444 -0
  457. package/src/apothem/hooks/hooks.json +318 -0
  458. package/src/apothem/hooks/lib/README.md +39 -0
  459. package/src/apothem/hooks/lib/__init__.py +18 -0
  460. package/src/apothem/hooks/lib/bootstrap.ps1 +129 -0
  461. package/src/apothem/hooks/lib/bootstrap.sh +103 -0
  462. package/src/apothem/hooks/lib/events.py +51 -0
  463. package/src/apothem/hooks/lib/find-pwsh.ps1 +78 -0
  464. package/src/apothem/hooks/lib/find-pwsh.sh +76 -0
  465. package/src/apothem/hooks/lib/find-python.ps1 +63 -0
  466. package/src/apothem/hooks/lib/find-python.sh +97 -0
  467. package/src/apothem/hooks/lib/log.py +43 -0
  468. package/src/apothem/hooks/lib/resolve_root.py +264 -0
  469. package/src/apothem/hooks/messages/postcompact.md +14 -0
  470. package/src/apothem/hooks/messages/posttooluse-proactive-compaction.md +46 -0
  471. package/src/apothem/hooks/messages/precompact.md +14 -0
  472. package/src/apothem/hooks/messages/pretooluse-askuserquestion-recommended.md +65 -0
  473. package/src/apothem/hooks/messages/pretooluse-bash-plan-guard.md +97 -0
  474. package/src/apothem/hooks/messages/pretooluse-bash.md +39 -0
  475. package/src/apothem/hooks/messages/pretooluse-conformity.md +70 -0
  476. package/src/apothem/hooks/messages/pretooluse-dependency-guard.md +21 -0
  477. package/src/apothem/hooks/messages/pretooluse-edit-header-guard.md +61 -0
  478. package/src/apothem/hooks/messages/pretooluse-edit.md +21 -0
  479. package/src/apothem/hooks/messages/pretooluse-eval-guard.md +39 -0
  480. package/src/apothem/hooks/messages/pretooluse-notebookedit.md +11 -0
  481. package/src/apothem/hooks/messages/pretooluse-write-header-guard.md +45 -0
  482. package/src/apothem/hooks/messages/pretooluse-write-plan-guard.md +72 -0
  483. package/src/apothem/hooks/messages/pretooluse-write.md +21 -0
  484. package/src/apothem/hooks/messages/sessionstart.md +15 -0
  485. package/src/apothem/hooks/messages/stop.md +27 -0
  486. package/src/apothem/hooks/proactive_compaction_tracker.py +327 -0
  487. package/src/apothem/hooks/session_start_bootstrap.py +472 -0
  488. package/src/apothem/lib/README.md +42 -0
  489. package/src/apothem/lib/__init__.py +13 -0
  490. package/src/apothem/lib/atomic_io.py +189 -0
  491. package/src/apothem/lib/auditor.py +687 -0
  492. package/src/apothem/lib/clean_slate.py +396 -0
  493. package/src/apothem/lib/contexts.py +352 -0
  494. package/src/apothem/lib/data_home.py +255 -0
  495. package/src/apothem/lib/frontmatter.py +101 -0
  496. package/src/apothem/lib/harness_materializer.py +213 -0
  497. package/src/apothem/lib/harness_protocol.py +59 -0
  498. package/src/apothem/lib/harness_registry.py +282 -0
  499. package/src/apothem/lib/harness_registry_data.py +843 -0
  500. package/src/apothem/lib/install_ledger.py +347 -0
  501. package/src/apothem/lib/learning.py +540 -0
  502. package/src/apothem/lib/memory.py +347 -0
  503. package/src/apothem/lib/parallel_sweep.py +234 -0
  504. package/src/apothem/lib/plan_tiers.py +200 -0
  505. package/src/apothem/lib/plugin_bootstrap.py +132 -0
  506. package/src/apothem/lib/plugin_tree.py +599 -0
  507. package/src/apothem/lib/profile.py +755 -0
  508. package/src/apothem/lib/profile_projection.py +198 -0
  509. package/src/apothem/lib/propagation-manifest.yaml +878 -0
  510. package/src/apothem/lib/propagation.py +220 -0
  511. package/src/apothem/lib/python_resolver.py +189 -0
  512. package/src/apothem/lib/reporter.py +62 -0
  513. package/src/apothem/lib/workspace_migration.py +323 -0
  514. package/src/apothem/output-styles/README.md +41 -0
  515. package/src/apothem/output-styles/concise-engineer.md +49 -0
  516. package/src/apothem/output-styles/default-architect.md +52 -0
  517. package/src/apothem/output-styles/default.md +113 -0
  518. package/src/apothem/output-styles/forensic-auditor.md +63 -0
  519. package/src/apothem/py.typed +0 -0
  520. package/src/apothem/rules/README.md +121 -0
  521. package/src/apothem/rules/agent-capability-discipline-matrix.md +89 -0
  522. package/src/apothem/rules/agent-capability-discipline.md +78 -0
  523. package/src/apothem/rules/agent-orchestration-patterns.md +144 -0
  524. package/src/apothem/rules/agent-orchestration.md +65 -0
  525. package/src/apothem/rules/agents-md-convention.md +86 -0
  526. package/src/apothem/rules/agile-sprints-elements.md +135 -0
  527. package/src/apothem/rules/agile-sprints.md +64 -0
  528. package/src/apothem/rules/agnostic-posture-checklist.md +47 -0
  529. package/src/apothem/rules/agnostic-posture.md +48 -0
  530. package/src/apothem/rules/authoritative-referencing-quotation.md +50 -0
  531. package/src/apothem/rules/authoritative-referencing.md +66 -0
  532. package/src/apothem/rules/authority-inquiry-categories.md +58 -0
  533. package/src/apothem/rules/authority-inquiry.md +54 -0
  534. package/src/apothem/rules/auto-memory-topic-files.md +86 -0
  535. package/src/apothem/rules/auto-memory.md +67 -0
  536. package/src/apothem/rules/bidirectional-binding.md +123 -0
  537. package/src/apothem/rules/canonical-layout-reporting-tiers.md +212 -0
  538. package/src/apothem/rules/canonical-layout.md +60 -0
  539. package/src/apothem/rules/clean-architecture-layers.md +186 -0
  540. package/src/apothem/rules/clean-room-generation-protocols.md +124 -0
  541. package/src/apothem/rules/clean-room-generation.md +59 -0
  542. package/src/apothem/rules/code-craft-conventions.md +101 -0
  543. package/src/apothem/rules/code-craft-markdown.md +138 -0
  544. package/src/apothem/rules/code-craft-python.md +154 -0
  545. package/src/apothem/rules/code-craft-shell.md +192 -0
  546. package/src/apothem/rules/cognitive-identity-techniques.md +180 -0
  547. package/src/apothem/rules/cognitive-identity.md +81 -0
  548. package/src/apothem/rules/context-management-budget.md +46 -0
  549. package/src/apothem/rules/context-management-protocol.md +161 -0
  550. package/src/apothem/rules/context-management-scratch.md +128 -0
  551. package/src/apothem/rules/context-management.md +85 -0
  552. package/src/apothem/rules/definitiveness-virtues.md +67 -0
  553. package/src/apothem/rules/definitiveness.md +58 -0
  554. package/src/apothem/rules/determinism.md +81 -0
  555. package/src/apothem/rules/disclosure-ledger-markers.md +58 -0
  556. package/src/apothem/rules/disclosure-ledger.md +52 -0
  557. package/src/apothem/rules/dynamism.md +38 -0
  558. package/src/apothem/rules/etc-extension.md +57 -0
  559. package/src/apothem/rules/expertise-posture-elements.md +68 -0
  560. package/src/apothem/rules/expertise-posture.md +54 -0
  561. package/src/apothem/rules/freshness-facade.md +64 -0
  562. package/src/apothem/rules/harness-adapter-shape-schemas.md +162 -0
  563. package/src/apothem/rules/harness-adapter-shape.md +42 -0
  564. package/src/apothem/rules/host-discovery-manifests.md +50 -0
  565. package/src/apothem/rules/host-discovery.md +56 -0
  566. package/src/apothem/rules/i18n-discipline-locale-cohorts.md +120 -0
  567. package/src/apothem/rules/i18n-discipline.md +70 -0
  568. package/src/apothem/rules/interactive-questions-canonical-shapes.md +590 -0
  569. package/src/apothem/rules/interactive-questions-detail.md +41 -0
  570. package/src/apothem/rules/interactive-questions-sweep-matchers.md +184 -0
  571. package/src/apothem/rules/interactive-questions.md +89 -0
  572. package/src/apothem/rules/large-file-generation.md +112 -0
  573. package/src/apothem/rules/large-file-reading.md +59 -0
  574. package/src/apothem/rules/living-docs.md +85 -0
  575. package/src/apothem/rules/multi-agent-workflow.md +57 -0
  576. package/src/apothem/rules/operational-mandates-expanded.md +78 -0
  577. package/src/apothem/rules/operational-mandates.md +88 -0
  578. package/src/apothem/rules/option-annotation-form.md +60 -0
  579. package/src/apothem/rules/option-annotation.md +45 -0
  580. package/src/apothem/rules/own-voice-reimplementation.md +86 -0
  581. package/src/apothem/rules/performance-discipline.md +91 -0
  582. package/src/apothem/rules/persistent-conventions-vigilance-checklist.md +54 -0
  583. package/src/apothem/rules/persistent-conventions-vigilance.md +61 -0
  584. package/src/apothem/rules/plain-language.md +56 -0
  585. package/src/apothem/rules/planning-techniques.md +130 -0
  586. package/src/apothem/rules/pre-emission-gate-bars.md +86 -0
  587. package/src/apothem/rules/pre-emission-gate.md +54 -0
  588. package/src/apothem/rules/production-ready-prs-surfaces.md +162 -0
  589. package/src/apothem/rules/production-ready-prs.md +83 -0
  590. package/src/apothem/rules/propagation.md +63 -0
  591. package/src/apothem/rules/recommend-next-step.md +106 -0
  592. package/src/apothem/rules/refactoring-discipline.md +76 -0
  593. package/src/apothem/rules/session-closure.md +44 -0
  594. package/src/apothem/rules/sota-elevation-exemplars.md +76 -0
  595. package/src/apothem/rules/sota-elevation.md +52 -0
  596. package/src/apothem/rules/source-accessibility.md +58 -0
  597. package/src/apothem/rules/surgical-manipulation.md +48 -0
  598. package/src/apothem/rules/systemic-participation-relations.md +108 -0
  599. package/src/apothem/rules/systemic-participation.md +70 -0
  600. package/src/apothem/rules/ten-dimension-check-dimensions.md +52 -0
  601. package/src/apothem/rules/ten-dimension-check.md +59 -0
  602. package/src/apothem/rules/token-budget-discipline.md +81 -0
  603. package/src/apothem/rules/token-efficiency-rewrite-protocol.md +79 -0
  604. package/src/apothem/rules/token-efficiency-rewrite.md +77 -0
  605. package/src/apothem/rules/tool-use-discipline.md +48 -0
  606. package/src/apothem/rules/visual-leverage.md +102 -0
  607. package/src/apothem/schemas/NOTICE.md +9 -0
  608. package/src/apothem/schemas/README.md +104 -0
  609. package/src/apothem/schemas/__init__.py +176 -0
  610. package/src/apothem/schemas/advisory-finding.schema.json +111 -0
  611. package/src/apothem/schemas/agent.schema.json +106 -0
  612. package/src/apothem/schemas/authorship-header.txt +1 -0
  613. package/src/apothem/schemas/cohort-manifest.yaml +248 -0
  614. package/src/apothem/schemas/cohort-metadata-vocabulary.yaml +168 -0
  615. package/src/apothem/schemas/cohort.schema.json +113 -0
  616. package/src/apothem/schemas/command.schema.json +68 -0
  617. package/src/apothem/schemas/compatibility-matrix.yaml +432 -0
  618. package/src/apothem/schemas/context-fragment.schema.json +64 -0
  619. package/src/apothem/schemas/freshness-token-denylist.txt +51 -0
  620. package/src/apothem/schemas/handoff-manifest.yaml +353 -0
  621. package/src/apothem/schemas/header-exceptions.txt +141 -0
  622. package/src/apothem/schemas/header-visibility.yaml +39 -0
  623. package/src/apothem/schemas/learning-signal.schema.json +46 -0
  624. package/src/apothem/schemas/memory-record.schema.json +61 -0
  625. package/src/apothem/schemas/output-style.schema.json +40 -0
  626. package/src/apothem/schemas/plan.schema.json +51 -0
  627. package/src/apothem/schemas/plugin.schema.json +83 -0
  628. package/src/apothem/schemas/profile.example.yaml +70 -0
  629. package/src/apothem/schemas/profile.minimal.yaml +6 -0
  630. package/src/apothem/schemas/profile.schema.json +396 -0
  631. package/src/apothem/schemas/reference-token-denylist.txt +25 -0
  632. package/src/apothem/schemas/skill.schema.json +75 -0
  633. package/src/apothem/skills/README.md +93 -0
  634. package/src/apothem/skills/dependency-upgrade/SKILL.md +105 -0
  635. package/src/apothem/skills/dev-toolkit/SKILL.md +120 -0
  636. package/src/apothem/skills/diagram-authoring/SKILL.md +113 -0
  637. package/src/apothem/skills/document-authoring/SKILL.md +118 -0
  638. package/src/apothem/skills/ecosystem-audit/SKILL.md +108 -0
  639. package/src/apothem/skills/ecosystem-audit/references/audit-fortress.md +85 -0
  640. package/src/apothem/skills/ecosystem-audit/references/procedure.md +162 -0
  641. package/src/apothem/skills/eval-harness/SKILL.md +88 -0
  642. package/src/apothem/skills/incident-runbook/SKILL.md +92 -0
  643. package/src/apothem/skills/multi-source-research/SKILL.md +90 -0
  644. package/src/apothem/skills/plan-suite/SKILL.md +118 -0
  645. package/src/apothem/skills/plan-suite/master_template.md +1324 -0
  646. package/src/apothem/skills/projectify/SKILL.md +117 -0
  647. package/src/apothem/skills/prompt-engineering/SKILL.md +122 -0
  648. package/src/apothem/skills/refactor-extract/SKILL.md +85 -0
  649. package/src/apothem/skills/research-suite/SKILL.md +170 -0
  650. package/src/apothem/skills/research-suite/references/directory-structure.md +47 -0
  651. package/src/apothem/skills/research-suite/references/lifecycle.md +67 -0
  652. package/src/apothem/skills/research-suite/references/principal-investigator-framework.md +37 -0
  653. package/src/apothem/skills/research-suite/references/rigor-mandates.md +30 -0
  654. package/src/apothem/skills/research-suite/research_template.md +476 -0
  655. package/src/apothem/skills/secret-rotation/SKILL.md +87 -0
  656. package/src/apothem/skills/source-synthesis/SKILL.md +92 -0
  657. package/src/apothem/skills/surgical-guard/SKILL.md +118 -0
  658. package/src/apothem/skills/test-authoring/SKILL.md +85 -0
  659. package/src/apothem/skills/vuln-triage/SKILL.md +91 -0
  660. package/src/apothem/skills/workflow/SKILL.md +139 -0
  661. package/src/apothem/statuslines/README.md +26 -0
  662. package/src/apothem/statuslines/__init__.py +20 -0
  663. package/src/apothem/statuslines/conformity.json +5 -0
  664. package/src/apothem/statuslines/render.py +334 -0
  665. package/src/apothem/statuslines/statusline.md +50 -0
  666. package/src/apothem/templates/README.md +43 -0
  667. package/src/apothem/templates/agents-md-template.md +80 -0
  668. package/src/apothem/templates/consideration-log.md +39 -0
  669. package/src/apothem/templates/expertise-gap-log.md +56 -0
  670. package/src/apothem/templates/master-index-template.md +93 -0
  671. package/src/apothem/templates/potency-map.md +53 -0
  672. package/src/apothem/templates/preservation-audit.md +60 -0
  673. package/src/apothem/templates/question-resolution-audit.md +52 -0
  674. package/src/apothem/templates/trace-matrix-template.md +77 -0
@@ -0,0 +1,238 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Verify the repo-root ``.gitattributes`` carries the canonical contract.
4
+
5
+ Why this validator exists. The supply-chain release contract requires
6
+ the repository to ship a ``.gitattributes`` file at its root that pins
7
+ line-ending normalisation (``* text=auto eol=lf``), declares binary
8
+ asset classes (``*.png binary``, ``*.jpg`` or ``*.jpeg`` ``binary``),
9
+ preserves SVG as text for diffability (``*.svg text``), and applies
10
+ ``export-ignore`` to ephemeral / developer-only trees (``.plans/``,
11
+ ``tests/``, ``.github/``) so source-archive consumers receive only
12
+ shippable content. Absence of any one of these contract elements is a
13
+ supply-chain drift class that this validator surfaces at the
14
+ pre-emission gate.
15
+
16
+ Detection strategy. Open ``<root>/.gitattributes``; tokenise on lines
17
+ (comments and blanks ignored); look for each required pattern via a
18
+ permissive whitespace-tolerant regex. Each missing element produces a
19
+ finding whose ``drift_class`` field names the violation. The validator
20
+ does NOT attempt to enforce ordering, repetition, or stylistic
21
+ consistency — only presence.
22
+
23
+ Exit semantics. Exits 0 (PASS) when every required contract element is
24
+ present; exits 2 (FAIL) on any drift class. The exit-2 convention
25
+ matches the conformity-gate orchestrator's ``EXIT_FAIL`` constant.
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ import json
31
+ import re
32
+ import sys
33
+ from dataclasses import asdict, dataclass, field
34
+ from pathlib import Path
35
+ from typing import Final
36
+
37
+ GREP_NAME: Final[str] = "gitattributes-presence-grep"
38
+ RULE_ANCHOR: Final[str] = "supply-chain .gitattributes contract"
39
+
40
+ EXIT_PASS: Final[int] = 0
41
+ EXIT_FAIL: Final[int] = 2
42
+
43
+ GITATTRIBUTES_FILENAME: Final[str] = ".gitattributes"
44
+
45
+ # Drift class identifiers — surface in findings so the operator can
46
+ # route each failure to its remediation path without re-parsing prose.
47
+ DRIFT_FILE_ABSENT: Final[str] = "file-absent"
48
+ DRIFT_MISSING_TEXT_EOL_BASELINE: Final[str] = "missing-text-eol-baseline"
49
+ DRIFT_MISSING_BINARY_DECLARATION: Final[str] = "missing-binary-declaration"
50
+ DRIFT_MISSING_EXPORT_IGNORE: Final[str] = "missing-export-ignore"
51
+
52
+ # Required-element regexes. Each pattern is whitespace-tolerant so the
53
+ # operator may freely align columns; none require a specific order.
54
+ # ``re.MULTILINE`` lets ``^`` match at line starts so commented-out
55
+ # variants (lines beginning with ``#``) are not matched.
56
+ _TEXT_EOL_RE: Final[re.Pattern[str]] = re.compile(
57
+ r"^\s*\*\s+text=auto\s+eol=lf\b",
58
+ re.MULTILINE,
59
+ )
60
+ _PNG_BINARY_RE: Final[re.Pattern[str]] = re.compile(
61
+ r"^\s*\*\.png\s+binary\b",
62
+ re.MULTILINE,
63
+ )
64
+ # Accept either ``*.jpg`` or ``*.jpeg`` as the JPEG declaration.
65
+ _JPG_BINARY_RE: Final[re.Pattern[str]] = re.compile(
66
+ r"^\s*\*\.jpe?g\s+binary\b",
67
+ re.MULTILINE,
68
+ )
69
+ _SVG_TEXT_RE: Final[re.Pattern[str]] = re.compile(
70
+ r"^\s*\*\.svg\s+text\b",
71
+ re.MULTILINE,
72
+ )
73
+
74
+ # Export-ignore patterns. Accept both trailing-slash (``.plans/``) and
75
+ # bare-name (``.plans``) glob forms — git honours both.
76
+ _EXPORT_IGNORE_PATHS: Final[tuple[str, ...]] = (".plans", "tests", ".github")
77
+
78
+
79
+ def _export_ignore_re(path: str) -> re.Pattern[str]:
80
+ """Compile a permissive export-ignore matcher for *path*.
81
+
82
+ Accepts ``.plans/ export-ignore`` and ``.plans export-ignore`` forms,
83
+ plus glob variants like ``.plans/** export-ignore``. The leading
84
+ anchor is ``^`` under MULTILINE so commented lines do not match.
85
+ """
86
+ escaped = re.escape(path)
87
+ return re.compile(
88
+ rf"^\s*{escaped}(?:/\S*|/?)\s+export-ignore\b",
89
+ re.MULTILINE,
90
+ )
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class Finding:
95
+ """One missing-contract-element occurrence."""
96
+
97
+ drift_class: str
98
+ detail: str
99
+ rule: str = RULE_ANCHOR
100
+
101
+
102
+ @dataclass(frozen=True)
103
+ class GrepResult:
104
+ grep: str
105
+ root: str
106
+ path: str | None
107
+ passed: bool
108
+ findings: list[Finding] = field(default_factory=list)
109
+
110
+ def to_json(self) -> str:
111
+ payload = {
112
+ "grep": self.grep,
113
+ "root": self.root,
114
+ "path": self.path,
115
+ "passed": self.passed,
116
+ "findings": [asdict(f) for f in self.findings],
117
+ }
118
+ return json.dumps(payload, indent=2)
119
+
120
+
121
+ def check(root: Path) -> GrepResult:
122
+ """Inspect ``<root>/.gitattributes`` and return a structured verdict.
123
+
124
+ Pre-conditions: *root* names a directory the operator wishes to
125
+ audit (typically the repository root).
126
+ Post-conditions: ``result.passed`` is True iff the file exists and
127
+ contains every required contract element; otherwise ``findings``
128
+ enumerates each drift class.
129
+ """
130
+ path = root / GITATTRIBUTES_FILENAME
131
+ if not path.is_file():
132
+ finding = Finding(
133
+ drift_class=DRIFT_FILE_ABSENT,
134
+ detail=(
135
+ f"{GITATTRIBUTES_FILENAME} is absent at repository root "
136
+ f"({path}); the supply-chain contract requires it"
137
+ ),
138
+ )
139
+ return GrepResult(
140
+ grep=GREP_NAME,
141
+ root=str(root),
142
+ path=str(path),
143
+ passed=False,
144
+ findings=[finding],
145
+ )
146
+
147
+ try:
148
+ content = path.read_text(encoding="utf-8")
149
+ except OSError as exc:
150
+ finding = Finding(
151
+ drift_class=DRIFT_FILE_ABSENT,
152
+ detail=f"{GITATTRIBUTES_FILENAME} unreadable at {path}: {exc}",
153
+ )
154
+ return GrepResult(
155
+ grep=GREP_NAME,
156
+ root=str(root),
157
+ path=str(path),
158
+ passed=False,
159
+ findings=[finding],
160
+ )
161
+
162
+ findings: list[Finding] = []
163
+
164
+ if not _TEXT_EOL_RE.search(content):
165
+ findings.append(
166
+ Finding(
167
+ drift_class=DRIFT_MISSING_TEXT_EOL_BASELINE,
168
+ detail=(
169
+ "baseline `* text=auto eol=lf` declaration missing; "
170
+ "line-ending normalisation is unpinned"
171
+ ),
172
+ )
173
+ )
174
+
175
+ if not _PNG_BINARY_RE.search(content):
176
+ findings.append(
177
+ Finding(
178
+ drift_class=DRIFT_MISSING_BINARY_DECLARATION,
179
+ detail="`*.png binary` declaration missing",
180
+ )
181
+ )
182
+
183
+ if not _JPG_BINARY_RE.search(content):
184
+ findings.append(
185
+ Finding(
186
+ drift_class=DRIFT_MISSING_BINARY_DECLARATION,
187
+ detail="`*.jpg binary` (or `*.jpeg binary`) declaration missing",
188
+ )
189
+ )
190
+
191
+ if not _SVG_TEXT_RE.search(content):
192
+ findings.append(
193
+ Finding(
194
+ drift_class=DRIFT_MISSING_BINARY_DECLARATION,
195
+ detail=(
196
+ "`*.svg text` declaration missing; SVG must be kept "
197
+ "diffable as text rather than treated as binary"
198
+ ),
199
+ )
200
+ )
201
+
202
+ for ignore_path in _EXPORT_IGNORE_PATHS:
203
+ if not _export_ignore_re(ignore_path).search(content):
204
+ findings.append(
205
+ Finding(
206
+ drift_class=DRIFT_MISSING_EXPORT_IGNORE,
207
+ detail=(
208
+ f"`{ignore_path}/ export-ignore` declaration "
209
+ "missing; ephemeral / developer-only tree would "
210
+ "leak into source archives"
211
+ ),
212
+ )
213
+ )
214
+
215
+ return GrepResult(
216
+ grep=GREP_NAME,
217
+ root=str(root),
218
+ path=str(path),
219
+ passed=not findings,
220
+ findings=findings,
221
+ )
222
+
223
+
224
+ def _read_input(argv: list[str]) -> Path:
225
+ if len(argv) >= 2:
226
+ return Path(argv[1])
227
+ return Path.cwd()
228
+
229
+
230
+ def _main(argv: list[str]) -> int:
231
+ root = _read_input(argv)
232
+ result = check(root)
233
+ print(result.to_json())
234
+ return EXIT_PASS if result.passed else EXIT_FAIL
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(_main(sys.argv))
@@ -0,0 +1,320 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Verify every workflow job opens with a conformant harden-runner step.
4
+
5
+ Why this enforcement exists. The workflow hardening contract requires
6
+ every GitHub Actions workflow at ``.github/workflows/*.yml`` to invoke
7
+ ``step-security/harden-runner@v2`` as the FIRST step of every job and
8
+ configure ``egress-policy: block`` (steady-state) or ``egress-policy:
9
+ audit`` (initial deployment phase). The action installs network-egress
10
+ controls before any user-supplied step runs; placing it second leaves a
11
+ window where checkout / dependency-fetch / arbitrary action steps can
12
+ exfiltrate before the egress policy engages.
13
+
14
+ Detection strategy. The validator walks ``<root>/.github/workflows/`` for
15
+ ``*.yml`` and ``*.yaml`` files, parses each via ``yaml.safe_load``, and
16
+ for every job in ``jobs:`` inspects the ``steps[]`` sequence. Three drift
17
+ classes surface:
18
+
19
+ - ``harden-runner-action-absent`` — no step in the job uses
20
+ ``step-security/harden-runner``.
21
+ - ``harden-runner-not-first-step`` — the action is present but not
22
+ ``steps[0]``.
23
+ - ``egress-policy-too-permissive`` — the step's ``with:`` block omits
24
+ ``egress-policy`` or sets it to a value other than ``block`` / ``audit``
25
+ (``log`` and any other free-form value are rejected; the canonical
26
+ taxonomy is closed).
27
+
28
+ Workflows-absent tolerance. When ``<root>/.github/workflows/`` does not
29
+ exist (a repository in its earliest scaffold state), the validator exits
30
+ 0 with ``not-yet-materialised: true`` so the gate does not block bootstrap
31
+ work that hasn't yet authored CI.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import json
37
+ import re
38
+ import sys
39
+ from dataclasses import asdict, dataclass, field
40
+ from pathlib import Path
41
+ from typing import Final
42
+
43
+ import yaml
44
+
45
+ # A bare ``${{ inputs.<name> }}`` expression — the reusable-workflow shape
46
+ # where egress-policy is parameterized by a workflow_call input.
47
+ _INPUTS_EXPR: Final[re.Pattern[str]] = re.compile(
48
+ r"^\$\{\{\s*inputs\.([A-Za-z_][\w-]*)\s*\}\}$"
49
+ )
50
+
51
+ GREP_NAME: Final[str] = "harden-runner-grep"
52
+ RULE_ANCHOR: Final[str] = "harden-runner egress baseline"
53
+
54
+ EXIT_PASS: Final[int] = 0
55
+ EXIT_FAIL: Final[int] = 2
56
+
57
+ WORKFLOWS_SUBPATH: Final[str] = ".github/workflows"
58
+ ACTION_NAME: Final[str] = "step-security/harden-runner"
59
+ CONFORMANT_EGRESS_POLICIES: Final[frozenset[str]] = frozenset({"block", "audit"})
60
+
61
+ DRIFT_ABSENT: Final[str] = "harden-runner-action-absent"
62
+ DRIFT_NOT_FIRST: Final[str] = "harden-runner-not-first-step"
63
+ DRIFT_PERMISSIVE: Final[str] = "egress-policy-too-permissive"
64
+
65
+
66
+ @dataclass(frozen=True)
67
+ class Finding:
68
+ """One job's harden-runner drift."""
69
+
70
+ workflow: str
71
+ job: str
72
+ drift: str
73
+ detail: str
74
+ rule: str = RULE_ANCHOR
75
+
76
+
77
+ @dataclass(frozen=True)
78
+ class GrepResult:
79
+ grep: str
80
+ root: str
81
+ passed: bool
82
+ not_yet_materialised: bool = False
83
+ findings: list[Finding] = field(default_factory=list)
84
+
85
+ def to_json(self) -> str:
86
+ payload = {
87
+ "grep": self.grep,
88
+ "root": self.root,
89
+ "passed": self.passed,
90
+ "not-yet-materialised": self.not_yet_materialised,
91
+ "findings": [asdict(f) for f in self.findings],
92
+ }
93
+ return json.dumps(payload, indent=2)
94
+
95
+
96
+ def _uses_harden_runner(uses_value: object) -> bool:
97
+ """Return True iff a ``uses:`` value references step-security/harden-runner."""
98
+ if not isinstance(uses_value, str):
99
+ return False
100
+ # Strip any ``@<ref>`` suffix so ``@v2``, ``@v2.10``, and a 40-char SHA
101
+ # pin all match identically.
102
+ name = uses_value.split("@", 1)[0].strip()
103
+ return name == ACTION_NAME
104
+
105
+
106
+ def _workflow_call_inputs(document: dict[object, object]) -> dict[object, object]:
107
+ """Return the ``on.workflow_call.inputs`` mapping, or empty when absent.
108
+
109
+ GitHub Actions ``on:`` is coerced by PyYAML (YAML 1.1) to the boolean
110
+ key ``True``; handle both the string ``"on"`` and boolean ``True`` keys.
111
+ """
112
+ on_block = document.get("on", document.get(True))
113
+ if not isinstance(on_block, dict):
114
+ return {}
115
+ workflow_call = on_block.get("workflow_call")
116
+ if not isinstance(workflow_call, dict):
117
+ return {}
118
+ inputs = workflow_call.get("inputs")
119
+ return inputs if isinstance(inputs, dict) else {}
120
+
121
+
122
+ def _resolve_egress(egress: object, inputs: dict[object, object]) -> object:
123
+ """Resolve a ``${{ inputs.<name> }}`` egress-policy to the input default.
124
+
125
+ A reusable workflow may parameterize egress-policy via a ``workflow_call``
126
+ input; the conformant value is then the input's declared ``default``. A
127
+ literal egress value (not an inputs expression) is returned unchanged.
128
+ """
129
+ if not isinstance(egress, str):
130
+ return egress
131
+ match = _INPUTS_EXPR.match(egress.strip())
132
+ if match is None:
133
+ return egress
134
+ spec = inputs.get(match.group(1))
135
+ if isinstance(spec, dict) and "default" in spec:
136
+ return spec["default"]
137
+ return egress
138
+
139
+
140
+ def _inspect_job(
141
+ workflow: str,
142
+ job_name: str,
143
+ job: object,
144
+ workflow_inputs: dict[object, object] | None = None,
145
+ ) -> list[Finding]:
146
+ """Return drift findings for one job; empty list when conformant."""
147
+ if not isinstance(job, dict):
148
+ return []
149
+ # Reusable-workflow callers have no inline steps; harden-runner is
150
+ # step-level and cannot apply. A job declaring a top-level ``uses:``
151
+ # field and lacking a ``steps:`` field is structurally exempt.
152
+ if "uses" in job and "steps" not in job:
153
+ return []
154
+ steps = job.get("steps")
155
+ if not isinstance(steps, list) or not steps:
156
+ return [
157
+ Finding(
158
+ workflow=workflow,
159
+ job=job_name,
160
+ drift=DRIFT_ABSENT,
161
+ detail="job has no steps[] sequence",
162
+ )
163
+ ]
164
+ # Locate harden-runner step(s).
165
+ hr_indices = [
166
+ i
167
+ for i, step in enumerate(steps)
168
+ if isinstance(step, dict) and _uses_harden_runner(step.get("uses"))
169
+ ]
170
+ if not hr_indices:
171
+ return [
172
+ Finding(
173
+ workflow=workflow,
174
+ job=job_name,
175
+ drift=DRIFT_ABSENT,
176
+ detail=f"no step uses {ACTION_NAME}",
177
+ )
178
+ ]
179
+ first_idx = hr_indices[0]
180
+ findings: list[Finding] = []
181
+ if first_idx != 0:
182
+ first_step = steps[0]
183
+ first_uses = first_step.get("uses") if isinstance(first_step, dict) else None
184
+ findings.append(
185
+ Finding(
186
+ workflow=workflow,
187
+ job=job_name,
188
+ drift=DRIFT_NOT_FIRST,
189
+ detail=(
190
+ f"{ACTION_NAME} appears at steps[{first_idx}]; "
191
+ f"steps[0] uses={first_uses!r}"
192
+ ),
193
+ )
194
+ )
195
+ # Inspect the egress-policy on the first harden-runner step.
196
+ hr_step = steps[first_idx]
197
+ with_block = hr_step.get("with") if isinstance(hr_step, dict) else None
198
+ egress = with_block.get("egress-policy") if isinstance(with_block, dict) else None
199
+ # A reusable workflow may parameterize egress-policy via a workflow_call
200
+ # input (``${{ inputs.<name> }}``); resolve it to the input's declared
201
+ # default so a conformant default is not flagged as too-permissive.
202
+ egress = _resolve_egress(egress, workflow_inputs or {})
203
+ if egress is None:
204
+ findings.append(
205
+ Finding(
206
+ workflow=workflow,
207
+ job=job_name,
208
+ drift=DRIFT_PERMISSIVE,
209
+ detail="egress-policy key absent from with: block",
210
+ )
211
+ )
212
+ elif not isinstance(egress, str) or egress not in CONFORMANT_EGRESS_POLICIES:
213
+ findings.append(
214
+ Finding(
215
+ workflow=workflow,
216
+ job=job_name,
217
+ drift=DRIFT_PERMISSIVE,
218
+ detail=(
219
+ f"egress-policy={egress!r}; expected one of "
220
+ f"{sorted(CONFORMANT_EGRESS_POLICIES)}"
221
+ ),
222
+ )
223
+ )
224
+ return findings
225
+
226
+
227
+ def _inspect_workflow(path: Path, root: Path) -> list[Finding]:
228
+ """Parse a workflow file; return drift findings across all its jobs."""
229
+ rel = str(path.relative_to(root)).replace("\\", "/")
230
+ try:
231
+ text = path.read_text(encoding="utf-8")
232
+ except (OSError, UnicodeDecodeError) as exc:
233
+ return [
234
+ Finding(
235
+ workflow=rel,
236
+ job="<workflow>",
237
+ drift=DRIFT_ABSENT,
238
+ detail=f"unreadable workflow: {exc}",
239
+ )
240
+ ]
241
+ try:
242
+ document = yaml.safe_load(text)
243
+ except yaml.YAMLError as exc:
244
+ return [
245
+ Finding(
246
+ workflow=rel,
247
+ job="<workflow>",
248
+ drift=DRIFT_ABSENT,
249
+ detail=f"YAML parse error: {exc}",
250
+ )
251
+ ]
252
+ if not isinstance(document, dict):
253
+ return [
254
+ Finding(
255
+ workflow=rel,
256
+ job="<workflow>",
257
+ drift=DRIFT_ABSENT,
258
+ detail="workflow root is not a mapping",
259
+ )
260
+ ]
261
+ jobs = document.get("jobs")
262
+ if not isinstance(jobs, dict) or not jobs:
263
+ # A workflow with no jobs has nothing to harden; skip silently.
264
+ return []
265
+ workflow_inputs = _workflow_call_inputs(document)
266
+ findings: list[Finding] = []
267
+ for job_name, job in jobs.items():
268
+ findings.extend(_inspect_workflow_job(rel, job_name, job, workflow_inputs))
269
+ return findings
270
+
271
+
272
+ def _inspect_workflow_job(
273
+ workflow: str,
274
+ job_name: object,
275
+ job: object,
276
+ workflow_inputs: dict[object, object] | None = None,
277
+ ) -> list[Finding]:
278
+ name = str(job_name) if not isinstance(job_name, str) else job_name
279
+ return _inspect_job(workflow, name, job, workflow_inputs)
280
+
281
+
282
+ def check_root(root: Path) -> GrepResult:
283
+ """Walk ``<root>/.github/workflows/`` and audit every workflow.
284
+
285
+ Pre-conditions: ``root`` is the repository root.
286
+ Post-conditions: ``result.passed`` is True iff every job in every
287
+ discovered workflow opens with ``step-security/harden-runner@v2`` (or
288
+ a pinned variant) and declares ``egress-policy: block`` / ``audit``.
289
+ When the workflows directory is absent, returns passed=True with
290
+ ``not_yet_materialised=True``.
291
+ """
292
+ workflows_dir = root / WORKFLOWS_SUBPATH
293
+ if not workflows_dir.is_dir():
294
+ return GrepResult(
295
+ grep=GREP_NAME,
296
+ root=str(root),
297
+ passed=True,
298
+ not_yet_materialised=True,
299
+ )
300
+ findings: list[Finding] = []
301
+ candidates = sorted([*workflows_dir.glob("*.yml"), *workflows_dir.glob("*.yaml")])
302
+ for workflow_path in candidates:
303
+ findings.extend(_inspect_workflow(workflow_path, root))
304
+ return GrepResult(
305
+ grep=GREP_NAME,
306
+ root=str(root),
307
+ passed=not findings,
308
+ findings=findings,
309
+ )
310
+
311
+
312
+ def _main(argv: list[str]) -> int:
313
+ root = Path(argv[1]) if len(argv) >= 2 else Path.cwd()
314
+ result = check_root(root)
315
+ print(result.to_json())
316
+ return EXIT_PASS if result.passed else EXIT_FAIL
317
+
318
+
319
+ if __name__ == "__main__":
320
+ sys.exit(_main(sys.argv))
@@ -0,0 +1,129 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Flag hedging vocabulary in prescriptive contexts.
4
+
5
+ Why this enforcement exists. The definitiveness rule M8 requires every
6
+ prescription to be unconditional or to carry the conditions that branch
7
+ its outcomes — silent hedges ("usually", "typically", "should probably")
8
+ soften prescriptions before they land, leaving the reader to guess at the
9
+ binding form. The mechanical row in the pre-emission gate scans for the
10
+ closed vocabulary and surfaces every occurrence. The operator triages
11
+ each hit on one of the three paths declared in the definitiveness rule:
12
+ promote to unconditional form with conditions named, demote to an
13
+ explicit conditional with branches enumerated, or remove the prescription
14
+ entirely (option set is underdetermined; route to inquiry).
15
+
16
+ Scope. Hits inside fenced code blocks (between triple-backtick fences) and
17
+ inside inline-code spans (single-backtick delimited) are excluded. Fenced
18
+ blocks typically quote external material, sample inputs, or commands;
19
+ inline-code spans typically cite a forbidden token meta-linguistically (a
20
+ doc that names the hedging vocabulary it forbids) rather than hedge a
21
+ prescription. Hits in prose are reported with line numbers; the operator
22
+ decides whether each hit is a genuine hedge or a quotation.
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ import re
28
+ import sys
29
+ from dataclasses import dataclass
30
+ from pathlib import Path
31
+ from typing import Final
32
+
33
+ from apothem.conformity._grep_base import GrepResult, run_grep
34
+
35
+ # The closed hedging vocabulary per the definitiveness rule M8 §Hedging
36
+ # vocabulary — eliminate or qualify. Word-boundary regex prevents partial
37
+ # matches inside larger words ("usually" matches; "unusually" does not).
38
+ HEDGING_VOCABULARY: Final[tuple[str, ...]] = (
39
+ "maybe",
40
+ "might",
41
+ "could",
42
+ "should probably",
43
+ "usually",
44
+ "generally",
45
+ "typically",
46
+ "mostly",
47
+ "often",
48
+ "perhaps",
49
+ "possibly",
50
+ "somewhat",
51
+ "fairly",
52
+ "roughly",
53
+ "broadly",
54
+ )
55
+
56
+ # Compile a single combined regex to scan once per line. The `(?i)` makes the
57
+ # match case-insensitive — `Usually` at sentence start hedges identically.
58
+ HEDGING_RE: Final[re.Pattern[str]] = re.compile(
59
+ r"(?i)\b(?:" + "|".join(re.escape(w) for w in HEDGING_VOCABULARY) + r")\b"
60
+ )
61
+
62
+ # Fenced code block delimiter. The standard Markdown form opens and closes
63
+ # with a triple backtick at column 0 (or with whitespace prefix in some
64
+ # dialects); we use the column-0 form per the host's discovered Markdown
65
+ # convention.
66
+ CODE_FENCE_RE: Final[re.Pattern[str]] = re.compile(r"^```")
67
+
68
+ # Inline-code span: a single-backtick-delimited run on one line. A hedge word
69
+ # quoted inside backticks is a meta-linguistic citation (a doc naming the
70
+ # vocabulary it forbids), not a prescription that hedges, so these spans are
71
+ # blanked before the prose scan. The column-preserving blank mirrors the
72
+ # inline-code exclusion in binding_reciprocity_grep.
73
+ INLINE_CODE_RE: Final[re.Pattern[str]] = re.compile(r"`[^`]*`")
74
+
75
+ GREP_NAME: Final[str] = "hedging-grep"
76
+ RULE_ANCHOR: Final[str] = "M8 definitiveness §Hedging"
77
+ EXIT_PASS: Final[int] = 0
78
+ EXIT_FAIL: Final[int] = 2
79
+ STDIN_FLAG: Final[str] = "--stdin"
80
+
81
+
82
+ @dataclass(frozen=True)
83
+ class Finding:
84
+ """One hedge occurrence outside a fenced code block."""
85
+
86
+ line: int
87
+ match: str
88
+ context: str
89
+ rule: str = RULE_ANCHOR
90
+
91
+
92
+ def check(content: str, path: Path | None = None) -> GrepResult:
93
+ """Scan content; return a structured result.
94
+
95
+ Pre-conditions: `content` is the artifact body about to be emitted.
96
+ Post-conditions: `result.passed` is True iff zero hedges were found
97
+ outside fenced code blocks and inline-code spans.
98
+ """
99
+ findings: list[Finding] = []
100
+ inside_fence = False
101
+ for line_index, line in enumerate(content.splitlines(), start=1):
102
+ if CODE_FENCE_RE.match(line):
103
+ # Toggle fence state — opening and closing fences both match.
104
+ inside_fence = not inside_fence
105
+ continue
106
+ if inside_fence:
107
+ continue
108
+ # Blank inline-code spans (same-length spaces preserve columns) so a
109
+ # hedge word cited inside backticks — a doc that forbids the `usually`
110
+ # vocabulary — is read as a citation, not a prescription that hedges.
111
+ scanned = INLINE_CODE_RE.sub(lambda m: " " * len(m.group(0)), line)
112
+ for match in HEDGING_RE.finditer(scanned):
113
+ findings.append(
114
+ Finding(
115
+ line=line_index,
116
+ match=match.group(),
117
+ context=line.strip(),
118
+ )
119
+ )
120
+ return GrepResult(
121
+ grep=GREP_NAME,
122
+ path=str(path) if path is not None else None,
123
+ passed=not findings,
124
+ findings=findings,
125
+ )
126
+
127
+
128
+ if __name__ == "__main__":
129
+ sys.exit(run_grep(check, sys.argv))