@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,291 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Verify release workflows use OIDC trusted publishing.
4
+
5
+ Why this validator exists. The release-engineering contract retires
6
+ long-lived registry tokens (PYPI_API_TOKEN, NPM_TOKEN) in favour of
7
+ OIDC trusted publishing. Trusted publishing
8
+ eliminates an entire class of credential-exfiltration risk by binding
9
+ the publish step to a short-lived GitHub-issued OIDC token whose
10
+ audience is the registry. The mechanical floor: every release workflow
11
+ under ``.github/workflows/`` that publishes to PyPI or npm declares
12
+ ``id-token: write`` permission, references no legacy secret token, and
13
+ (for npm) runs on the npm Trusted Publishing runtime floor: Node 24 and
14
+ npm CLI 11.5.1 or newer. npm now emits provenance automatically for
15
+ public packages published from public GitHub Actions workflows.
16
+
17
+ Scope. The validator walks ``<root>/.github/workflows/`` and inspects
18
+ filenames matching ``*publish*py*.yml`` (PyPI release workflows) and
19
+ ``*publish*npm*.yml`` or ``*packaging*npm*.yml`` (npm release
20
+ workflows). Each detected workflow is parsed via ``yaml.safe_load``
21
+ when PyYAML is available; otherwise the validator falls back to
22
+ permissive line-oriented string parsing so it remains operable
23
+ without extra packages on the conformity-gate execution path.
24
+
25
+ Workflow-absent tolerance. When neither workflow class exists under
26
+ ``.github/workflows/``, the validator exits 0 with the informational
27
+ field ``not-yet-materialised: true``. Before publishing workflows are
28
+ materialized, the matcher is advisory and never blocks the gate.
29
+
30
+ Drift classes.
31
+
32
+ - ``missing-id-token-permission`` — neither the top-level nor any
33
+ job-level ``permissions:`` block declares ``id-token: write``.
34
+ - ``action-version-too-old`` — ``pypa/gh-action-pypi-publish`` is
35
+ pinned below v1.10; OIDC trusted publishing support landed at v1.10.
36
+ - ``legacy-token-secret-reference`` — a reference to
37
+ ``secrets.PYPI_API_TOKEN`` or ``secrets.NPM_TOKEN`` (or the bare
38
+ ``PYPI_API_TOKEN`` / ``NPM_TOKEN`` env-var name in a workflow env
39
+ block) survives in the workflow.
40
+ - ``npm-runtime-too-old`` — the npm workflow does not select Node 24 or
41
+ a newer major Node line.
42
+ - ``npm-cli-floor-absent`` — the npm workflow does not pin npm CLI
43
+ 11.5.1+ before publishing.
44
+
45
+ Exit semantics. Exits 0 when no drift is detected (including the
46
+ workflow-absent informational case). Exits 2 when any drift class
47
+ fires.
48
+ """
49
+
50
+ from __future__ import annotations
51
+
52
+ import json
53
+ import re
54
+ import sys
55
+ from dataclasses import asdict, dataclass, field
56
+ from pathlib import Path
57
+ from typing import Final
58
+
59
+ GREP_NAME: Final[str] = "oidc-trusted-publishing-grep"
60
+ RULE_ANCHOR: Final[str] = "OIDC trusted publishing"
61
+
62
+ EXIT_PASS: Final[int] = 0
63
+ EXIT_FAIL: Final[int] = 2
64
+
65
+ # Minimum pypa/gh-action-pypi-publish version with OIDC trusted-publishing
66
+ # support per the upstream changelog.
67
+ MIN_PYPI_ACTION_MAJOR: Final[int] = 1
68
+ MIN_PYPI_ACTION_MINOR: Final[int] = 10
69
+
70
+ # Workflow filename predicates. We match permissively so renamed but
71
+ # semantically equivalent filenames remain in scope.
72
+ _PYPI_WORKFLOW_RE: Final[re.Pattern[str]] = re.compile(r"(?i)publish.*py.*\.ya?ml$")
73
+ _NPM_WORKFLOW_RE: Final[re.Pattern[str]] = re.compile(
74
+ r"(?i)(?:publish|packaging).*npm.*\.ya?ml$"
75
+ )
76
+
77
+ # Detection patterns operating on raw workflow text. These are
78
+ # deliberately substring-based so the validator works without PyYAML.
79
+ _ID_TOKEN_WRITE_RE: Final[re.Pattern[str]] = re.compile(r"id-token\s*:\s*write")
80
+ _PYPI_ACTION_USES_RE: Final[re.Pattern[str]] = re.compile(
81
+ r"pypa/gh-action-pypi-publish@v?(\d+)\.(\d+)(?:\.\d+)?"
82
+ )
83
+ _PYPI_TOKEN_REF_RE: Final[re.Pattern[str]] = re.compile(r"PYPI_API_TOKEN")
84
+ _NPM_TOKEN_REF_RE: Final[re.Pattern[str]] = re.compile(r"NPM_TOKEN")
85
+ _NPM_PUBLISH_RE: Final[re.Pattern[str]] = re.compile(r"npm\s+publish")
86
+ _NPM_NODE_BASELINE_RE: Final[re.Pattern[str]] = re.compile(
87
+ r"node-version\s*:\s*[\"']?(?:24|2[5-9]|[3-9]\d)(?:[\"']|\b)"
88
+ )
89
+ _NPM_CLI_BASELINE_RE: Final[re.Pattern[str]] = re.compile(
90
+ r"npm@(?:11\.(?:[5-9]|\d{2,})(?:\.\d+)?|1[2-9](?:\.\d+){0,2}|[2-9]\d(?:\.\d+){0,2})"
91
+ )
92
+
93
+
94
+ @dataclass(frozen=True)
95
+ class Finding:
96
+ """One drift occurrence in a release workflow."""
97
+
98
+ workflow: str
99
+ drift_class: str
100
+ detail: str
101
+ rule: str = RULE_ANCHOR
102
+
103
+
104
+ @dataclass(frozen=True)
105
+ class GrepResult:
106
+ """Aggregated walk result over the release workflows under root."""
107
+
108
+ grep: str
109
+ root: str
110
+ workflows_inspected: list[str]
111
+ not_yet_materialised: bool
112
+ passed: bool
113
+ findings: list[Finding] = field(default_factory=list)
114
+
115
+ def to_json(self) -> str:
116
+ payload = {
117
+ "grep": self.grep,
118
+ "root": self.root,
119
+ "workflows-inspected": self.workflows_inspected,
120
+ "not-yet-materialised": self.not_yet_materialised,
121
+ "passed": self.passed,
122
+ "findings": [asdict(f) for f in self.findings],
123
+ }
124
+ return json.dumps(payload, indent=2)
125
+
126
+
127
+ def _check_pypi_workflow(workflow: Path, text: str) -> list[Finding]:
128
+ """Inspect a PyPI release workflow for trusted-publishing drift."""
129
+ findings: list[Finding] = []
130
+ name = workflow.name
131
+ if not _ID_TOKEN_WRITE_RE.search(text):
132
+ findings.append(
133
+ Finding(
134
+ workflow=name,
135
+ drift_class="missing-id-token-permission",
136
+ detail=(
137
+ "no `id-token: write` permission block; OIDC trusted "
138
+ "publishing requires the workflow (or its publish job) "
139
+ "to request the id-token scope"
140
+ ),
141
+ )
142
+ )
143
+ for match in _PYPI_ACTION_USES_RE.finditer(text):
144
+ major = int(match.group(1))
145
+ minor = int(match.group(2))
146
+ too_old = (major, minor) < (MIN_PYPI_ACTION_MAJOR, MIN_PYPI_ACTION_MINOR)
147
+ if too_old:
148
+ findings.append(
149
+ Finding(
150
+ workflow=name,
151
+ drift_class="action-version-too-old",
152
+ detail=(
153
+ f"pypa/gh-action-pypi-publish pinned at v{major}."
154
+ f"{minor}; OIDC trusted publishing requires v"
155
+ f"{MIN_PYPI_ACTION_MAJOR}."
156
+ f"{MIN_PYPI_ACTION_MINOR} or higher"
157
+ ),
158
+ )
159
+ )
160
+ if _PYPI_TOKEN_REF_RE.search(text):
161
+ findings.append(
162
+ Finding(
163
+ workflow=name,
164
+ drift_class="legacy-token-secret-reference",
165
+ detail=(
166
+ "PYPI_API_TOKEN reference survives; OIDC trusted "
167
+ "publishing retires the long-lived token"
168
+ ),
169
+ )
170
+ )
171
+ return findings
172
+
173
+
174
+ def _check_npm_workflow(workflow: Path, text: str) -> list[Finding]:
175
+ """Inspect an npm release workflow for trusted-publishing drift."""
176
+ findings: list[Finding] = []
177
+ name = workflow.name
178
+ if not _ID_TOKEN_WRITE_RE.search(text):
179
+ findings.append(
180
+ Finding(
181
+ workflow=name,
182
+ drift_class="missing-id-token-permission",
183
+ detail=(
184
+ "no `id-token: write` permission block; OIDC trusted "
185
+ "publishing requires the workflow (or its publish job) "
186
+ "to request the id-token scope"
187
+ ),
188
+ )
189
+ )
190
+ if _NPM_TOKEN_REF_RE.search(text):
191
+ findings.append(
192
+ Finding(
193
+ workflow=name,
194
+ drift_class="legacy-token-secret-reference",
195
+ detail=(
196
+ "NPM_TOKEN reference survives; OIDC trusted publishing "
197
+ "retires the long-lived token"
198
+ ),
199
+ )
200
+ )
201
+ if _NPM_PUBLISH_RE.search(text) and not _NPM_NODE_BASELINE_RE.search(text):
202
+ findings.append(
203
+ Finding(
204
+ workflow=name,
205
+ drift_class="npm-runtime-too-old",
206
+ detail=(
207
+ "npm Trusted Publishing requires Node 22.14.0+; the "
208
+ "canonical workflow floor is Node 24 for a stable OIDC "
209
+ "runtime"
210
+ ),
211
+ )
212
+ )
213
+ if _NPM_PUBLISH_RE.search(text) and not _NPM_CLI_BASELINE_RE.search(text):
214
+ findings.append(
215
+ Finding(
216
+ workflow=name,
217
+ drift_class="npm-cli-floor-absent",
218
+ detail=(
219
+ "npm Trusted Publishing requires npm CLI 11.5.1+; pin "
220
+ "npm@11.5.1 or newer before running `npm publish`"
221
+ ),
222
+ )
223
+ )
224
+ return findings
225
+
226
+
227
+ def check(root: Path) -> GrepResult:
228
+ """Walk release workflows under root; aggregate trusted-publishing drift."""
229
+ workflows_dir = root / ".github" / "workflows"
230
+ findings: list[Finding] = []
231
+ inspected: list[str] = []
232
+ if not workflows_dir.is_dir():
233
+ return GrepResult(
234
+ grep=GREP_NAME,
235
+ root=str(root),
236
+ workflows_inspected=[],
237
+ not_yet_materialised=True,
238
+ passed=True,
239
+ findings=[],
240
+ )
241
+ pypi_workflows: list[Path] = []
242
+ npm_workflows: list[Path] = []
243
+ for candidate in sorted(workflows_dir.iterdir()):
244
+ if not candidate.is_file():
245
+ continue
246
+ if _PYPI_WORKFLOW_RE.search(candidate.name):
247
+ pypi_workflows.append(candidate)
248
+ elif _NPM_WORKFLOW_RE.search(candidate.name):
249
+ npm_workflows.append(candidate)
250
+ if not pypi_workflows and not npm_workflows:
251
+ return GrepResult(
252
+ grep=GREP_NAME,
253
+ root=str(root),
254
+ workflows_inspected=[],
255
+ not_yet_materialised=True,
256
+ passed=True,
257
+ findings=[],
258
+ )
259
+ for workflow in pypi_workflows:
260
+ text = workflow.read_text(encoding="utf-8")
261
+ inspected.append(workflow.name)
262
+ findings.extend(_check_pypi_workflow(workflow, text))
263
+ for workflow in npm_workflows:
264
+ text = workflow.read_text(encoding="utf-8")
265
+ inspected.append(workflow.name)
266
+ findings.extend(_check_npm_workflow(workflow, text))
267
+ return GrepResult(
268
+ grep=GREP_NAME,
269
+ root=str(root),
270
+ workflows_inspected=inspected,
271
+ not_yet_materialised=False,
272
+ passed=not findings,
273
+ findings=findings,
274
+ )
275
+
276
+
277
+ def _read_input(argv: list[str]) -> Path:
278
+ if len(argv) >= 2:
279
+ return Path(argv[1])
280
+ return Path.cwd()
281
+
282
+
283
+ def _main(argv: list[str]) -> int:
284
+ root = _read_input(argv)
285
+ result = check(root)
286
+ print(result.to_json())
287
+ return EXIT_PASS if result.passed else EXIT_FAIL
288
+
289
+
290
+ if __name__ == "__main__":
291
+ sys.exit(_main(sys.argv))
@@ -0,0 +1,352 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Enforce the per-option Recommended label-to-body bind (H6).
4
+
5
+ Why this enforcement exists. Option sets surfaced for operator decision
6
+ annotate the recommended path so the operator sees the agent's share of
7
+ evaluation. The canonical-channel rule §3 binds each option's label to its
8
+ body: an option whose body `recommendation:` value is exactly `recommended`
9
+ carries the canonical `(Recommended)` postfix on its own label; no option
10
+ carries the postfix without the matching body value; and a single-select
11
+ question recommends at most one option. The prior file-wide "any one marker
12
+ anywhere" check accepted a body-only or rationale-only recommendation — this
13
+ matcher tightens that to a per-option bind so the marker lands on the right
14
+ label.
15
+
16
+ Canonical postfix. The canonical postfix string is capital `(Recommended)`
17
+ per `_spec/spec.md` §4.2 and `rules/interactive-questions-canonical-shapes.md`
18
+ §2.1; it is recognized case-correctly. The lowercase `(recommended)` form is a
19
+ banned variant and is itself a finding when it appears as a label postfix.
20
+
21
+ Detection strategy. An option is a label line — backtick form
22
+ ``- `Label`:`` or YAML form ``label: Label`` — whose body (the lines up to the
23
+ next label) carries a `recommendation:` taxonomy value. For each option the
24
+ matcher computes two booleans (label-carries-canonical-postfix,
25
+ body-is-recommended) and flags every direction of the bind violation.
26
+ Cardinality is checked per invocation block (options grouped by the nearest
27
+ preceding invocation head — a prose channel mention or a canonical YAML
28
+ `question:` field); a non-`multiSelect: true` block with more than one
29
+ recommended option is a finding.
30
+
31
+ Narrative-marker leak. The `(Recommended)` (and the prose-and-document
32
+ `**Recommended**`) marker lives SOLELY in the option label per
33
+ `rules/interactive-questions-canonical-shapes.md` §2.1; the body carries
34
+ verifiable concrete-driver evidence instead. A marker surfacing on a body
35
+ segment line (``rationale:`` / ``recommendation:`` / ``default-pointer:``) is a
36
+ `narrative-marker-leak` finding. The label's own legitimate postfix is never a
37
+ leak — a label line carries no body-segment lead-in token.
38
+
39
+ Definitional exclusion. The rule files that *define* the option-annotation
40
+ convention quote non-canonical and deliberately-bound-violating examples as
41
+ specification material; sweeping them would flag their definitional examples.
42
+ Those files are excluded by path, mirroring the §3 exclusion zones of
43
+ `rules/interactive-questions-sweep-matchers.md`.
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ import re
49
+ import sys
50
+ from dataclasses import dataclass
51
+ from pathlib import Path
52
+ from typing import Final
53
+
54
+ from apothem.conformity._grep_base import GrepResult, run_grep
55
+
56
+ # Recommendation taxonomy value on a body segment, per the canonical channel §4.
57
+ _RECOMMENDATION_VALUE_RE: Final[re.Pattern[str]] = re.compile(
58
+ r"recommendation:\s*(?P<value>recommended|acceptable|discouraged|destructive-no-default)\b"
59
+ )
60
+
61
+ # Option label shapes. Backtick form ``- `Label`:`` (command / fallback prose)
62
+ # and YAML form ``label: Label`` / ``- label: Label`` (fenced worked examples).
63
+ _BACKTICK_LABEL_RE: Final[re.Pattern[str]] = re.compile(
64
+ r"^\s*>?\s*-\s*`(?P<label>[^`]+)`\s*:"
65
+ )
66
+ _YAML_LABEL_RE: Final[re.Pattern[str]] = re.compile(
67
+ r"^\s*-?\s*label:\s*(?P<label>\S.*?)\s*$"
68
+ )
69
+ _BOLD_LABEL_RE: Final[re.Pattern[str]] = re.compile(
70
+ r"^\s*>?\s*-\s*\*\*(?P<label>[^*]+)\*\*\s*:"
71
+ )
72
+
73
+ # Canonical postfix is capital `(Recommended)`, case-correct. The lowercase
74
+ # form is the banned variant.
75
+ _CANONICAL_POSTFIX_RE: Final[re.Pattern[str]] = re.compile(r"\(Recommended\)\s*$")
76
+ _LOWERCASE_POSTFIX_RE: Final[re.Pattern[str]] = re.compile(r"\(recommended\)\s*$")
77
+
78
+ # The canonical recommended marker in either of its two surface forms: the
79
+ # structured-inquiry label postfix `(Recommended)` and the prose-and-document
80
+ # inline marker `**Recommended**`. The marker lives SOLELY in the option label
81
+ # per `rules/interactive-questions-canonical-shapes.md` §2.1; a marker surfacing
82
+ # inside a body/narrative segment is a `narrative-marker-leak`.
83
+ _BODY_MARKER_RE: Final[re.Pattern[str]] = re.compile(
84
+ r"\(Recommended\)|\*\*Recommended\*\*"
85
+ )
86
+
87
+ # Body-segment lead-in tokens. A leak is flagged only when the marker rides on
88
+ # one of the three canonical body segments, so a stray prose line near an option
89
+ # is not mistaken for a narrative-embedded marker.
90
+ _BODY_SEGMENT_RE: Final[re.Pattern[str]] = re.compile(
91
+ r"(?:rationale|recommendation|default-pointer)\s*:"
92
+ )
93
+
94
+ # Invocation-head markers used only to bound cardinality blocks. Over-
95
+ # segmentation (a prose mention of the channel) is conservative — it only
96
+ # shrinks a block, which can never manufacture a false cardinality finding.
97
+ _INVOCATION_HEAD_RE: Final[re.Pattern[str]] = re.compile(
98
+ r"structured[- ]inquiry\s*:\s*question"
99
+ r"|Invoke the structured-inquiry channel"
100
+ r"|structured inquiry:"
101
+ # Canonical YAML invocation head: a `question:` field at line start opens a
102
+ # structured-inquiry block, so each YAML question bounds its own cardinality
103
+ # block. This stops two independent single-select YAML option sets from
104
+ # being read as one block, and a multiSelect:true block from masking a
105
+ # sibling single-select block's cardinality.
106
+ r"|^\s*question:\s*\S",
107
+ re.IGNORECASE,
108
+ )
109
+ _MULTISELECT_TRUE_RE: Final[re.Pattern[str]] = re.compile(r"multiSelect:\s*true\b")
110
+
111
+ # Body window cap: an option's body is the lines from its label up to the next
112
+ # label line, bounded by this many lines so a runaway scan cannot bind an
113
+ # option to a distant unrelated recommendation value.
114
+ _BODY_WINDOW_LINES: Final[int] = 14
115
+
116
+ # Rule / skill files that DEFINE the option-annotation convention. Their
117
+ # worked examples are specification material, not live invocations.
118
+ _EXCLUDED_BASENAMES: Final[frozenset[str]] = frozenset(
119
+ {
120
+ "interactive-questions.md",
121
+ "interactive-questions-canonical-shapes.md",
122
+ "interactive-questions-sweep-matchers.md",
123
+ "option-annotation.md",
124
+ "option-annotation-form.md",
125
+ "operational-mandates.md",
126
+ "master_template.md",
127
+ }
128
+ )
129
+ # Full relative-tail exclusions for files whose basename is generic.
130
+ _EXCLUDED_TAILS: Final[tuple[str, ...]] = (
131
+ "skills/ecosystem-audit/SKILL.md",
132
+ "skills/plan-suite/SKILL.md",
133
+ )
134
+
135
+ GREP_NAME: Final[str] = "option-annotation-grep"
136
+ RULE_ANCHOR: Final[str] = "M7 option-annotation (H6 per-option bind)"
137
+ EXIT_PASS: Final[int] = 0
138
+ EXIT_FAIL: Final[int] = 2
139
+ STDIN_FLAG: Final[str] = "--stdin"
140
+
141
+
142
+ @dataclass(frozen=True)
143
+ class Finding:
144
+ """One per-option (or per-block) bind violation."""
145
+
146
+ line: int
147
+ kind: str
148
+ label: str
149
+ detail: str
150
+ rule: str = RULE_ANCHOR
151
+
152
+
153
+ @dataclass(frozen=True)
154
+ class _Option:
155
+ line: int # 1-indexed label line
156
+ label: str
157
+ has_canonical_postfix: bool
158
+ has_lowercase_postfix: bool
159
+ body_recommended: bool
160
+ # 1-indexed line of a marker leak inside a body segment, or None.
161
+ narrative_marker_leak_line: int | None = None
162
+
163
+
164
+ def _is_excluded(path: Path | None) -> bool:
165
+ if path is None:
166
+ return False
167
+ if path.name in _EXCLUDED_BASENAMES:
168
+ return True
169
+ posix = path.as_posix()
170
+ return any(posix.endswith(tail) for tail in _EXCLUDED_TAILS)
171
+
172
+
173
+ def _label_line(line: str) -> str | None:
174
+ """Return the option label text if *line* is an option label, else None."""
175
+ for pattern in (_BACKTICK_LABEL_RE, _BOLD_LABEL_RE, _YAML_LABEL_RE):
176
+ m = pattern.match(line)
177
+ if m:
178
+ return m.group("label").strip()
179
+ return None
180
+
181
+
182
+ def _parse_options(lines: list[str]) -> list[_Option]:
183
+ """Extract every annotated option (a label whose body carries a value)."""
184
+ # Pre-compute the line index of every label so a body scan can stop at the
185
+ # next label rather than bleeding into the following option.
186
+ label_indices = [i for i, ln in enumerate(lines) if _label_line(ln) is not None]
187
+ label_index_set = set(label_indices)
188
+ options: list[_Option] = []
189
+ for i in label_indices:
190
+ label = _label_line(lines[i])
191
+ if label is None: # pragma: no cover - guarded by label_indices
192
+ continue
193
+ window_end = min(i + 1 + _BODY_WINDOW_LINES, len(lines))
194
+ body_value: str | None = None
195
+ leak_line: int | None = None
196
+ for j in range(i + 1, window_end):
197
+ if j in label_index_set:
198
+ break # body ends at the next option label
199
+ if body_value is None:
200
+ value_match = _RECOMMENDATION_VALUE_RE.search(lines[j])
201
+ if value_match:
202
+ body_value = value_match.group("value")
203
+ # Marker-leak detection runs across the WHOLE body window, not just
204
+ # up to the recommendation value, so a marker riding a later
205
+ # `default-pointer:` segment is caught too. A leak is a marker on a
206
+ # canonical body segment line.
207
+ if (
208
+ leak_line is None
209
+ and _BODY_SEGMENT_RE.search(lines[j])
210
+ and _BODY_MARKER_RE.search(lines[j])
211
+ ):
212
+ leak_line = j + 1
213
+ if body_value is None:
214
+ # No recommendation value in this label's body: not an annotated
215
+ # option subject to the bind (e.g., a prose list item). Skip.
216
+ continue
217
+ options.append(
218
+ _Option(
219
+ line=i + 1,
220
+ label=label,
221
+ has_canonical_postfix=_CANONICAL_POSTFIX_RE.search(label) is not None,
222
+ has_lowercase_postfix=_LOWERCASE_POSTFIX_RE.search(label) is not None,
223
+ body_recommended=body_value == "recommended",
224
+ narrative_marker_leak_line=leak_line,
225
+ )
226
+ )
227
+ return options
228
+
229
+
230
+ def _block_of(line_idx: int, head_indices: list[int]) -> int:
231
+ """Return the index of the nearest preceding invocation head (or -1)."""
232
+ block = -1
233
+ for k, head in enumerate(head_indices):
234
+ if head <= line_idx:
235
+ block = k
236
+ else:
237
+ break
238
+ return block
239
+
240
+
241
+ def check(content: str, path: Path | None = None) -> GrepResult:
242
+ """Scan *content* for per-option Recommended-bind violations.
243
+
244
+ Pre-conditions: *content* is the artifact body about to be emitted; *path*
245
+ is its destination (used only for definitional-file exclusion).
246
+ Post-conditions: ``result.passed`` is True when every annotated option
247
+ satisfies the bidirectional bind (capital canonical postfix iff body value
248
+ is ``recommended``), no label carries the lowercase variant, and no
249
+ single-select block recommends more than one option.
250
+ """
251
+ if _is_excluded(path):
252
+ return GrepResult(grep=GREP_NAME, path=_path_str(path), passed=True)
253
+
254
+ lines = content.splitlines()
255
+ options = _parse_options(lines)
256
+ findings: list[Finding] = []
257
+
258
+ for opt in options:
259
+ if opt.has_lowercase_postfix:
260
+ findings.append(
261
+ Finding(
262
+ line=opt.line,
263
+ kind="non-canonical-postfix-case",
264
+ label=opt.label,
265
+ detail="label uses lowercase (recommended); canonical form is (Recommended)",
266
+ )
267
+ )
268
+ if (
269
+ opt.body_recommended
270
+ and not opt.has_canonical_postfix
271
+ and not opt.has_lowercase_postfix
272
+ ):
273
+ findings.append(
274
+ Finding(
275
+ line=opt.line,
276
+ kind="missing-canonical-postfix",
277
+ label=opt.label,
278
+ detail="body recommendation: recommended but label lacks the (Recommended) postfix",
279
+ )
280
+ )
281
+ if opt.has_canonical_postfix and not opt.body_recommended:
282
+ findings.append(
283
+ Finding(
284
+ line=opt.line,
285
+ kind="spurious-postfix",
286
+ label=opt.label,
287
+ detail="label carries (Recommended) but body recommendation is not recommended",
288
+ )
289
+ )
290
+ if opt.narrative_marker_leak_line is not None:
291
+ findings.append(
292
+ Finding(
293
+ line=opt.narrative_marker_leak_line,
294
+ kind="narrative-marker-leak",
295
+ label=opt.label,
296
+ detail=(
297
+ "the (Recommended) / **Recommended** marker appears in a "
298
+ "body segment (rationale:/recommendation:/default-pointer:); "
299
+ "it lives solely in the option label"
300
+ ),
301
+ )
302
+ )
303
+
304
+ # Cardinality: per invocation block, a non-multiSelect-true question carries
305
+ # at most one recommended option.
306
+ head_indices = [i for i, ln in enumerate(lines) if _INVOCATION_HEAD_RE.search(ln)]
307
+ multiselect_blocks: set[int] = set()
308
+ if head_indices:
309
+ for i, ln in enumerate(lines):
310
+ if _MULTISELECT_TRUE_RE.search(ln):
311
+ multiselect_blocks.add(_block_of(i, head_indices))
312
+ else:
313
+ # No invocation head: treat the whole file as one implicit block.
314
+ if any(_MULTISELECT_TRUE_RE.search(ln) for ln in lines):
315
+ multiselect_blocks.add(0)
316
+ block_recommended: dict[int, list[_Option]] = {}
317
+ for opt in options:
318
+ if not opt.body_recommended:
319
+ continue
320
+ block = _block_of(opt.line - 1, head_indices) if head_indices else 0
321
+ block_recommended.setdefault(block, []).append(opt)
322
+ for block, recs in block_recommended.items():
323
+ if block in multiselect_blocks:
324
+ continue
325
+ if len(recs) > 1:
326
+ findings.append(
327
+ Finding(
328
+ line=recs[1].line,
329
+ kind="single-select-multi-recommended",
330
+ label=recs[1].label,
331
+ detail=(
332
+ f"single-select block recommends {len(recs)} options; "
333
+ "at most one is permitted"
334
+ ),
335
+ )
336
+ )
337
+
338
+ findings.sort(key=lambda f: (f.line, f.kind))
339
+ return GrepResult(
340
+ grep=GREP_NAME,
341
+ path=_path_str(path),
342
+ passed=not findings,
343
+ findings=findings,
344
+ )
345
+
346
+
347
+ def _path_str(path: Path | None) -> str | None:
348
+ return str(path) if path is not None else None
349
+
350
+
351
+ if __name__ == "__main__":
352
+ sys.exit(run_grep(check, sys.argv))