@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,261 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Coarse map of AI-conventions surfaces present in the working tree.
4
+
5
+ Why this scan exists. The ecosystem ships an AI-conventions surface
6
+ that spans multiple files, each authoritative within its own scope:
7
+ ``AGENTS.md`` for the canonical project voice, ``CLAUDE.md`` for the
8
+ Claude Code mirror, ``.github/copilot-instructions.md`` for GitHub Copilot, optional surfaces
9
+ (``site/content/docs/architecture/agents.mdx``, ``.cursorrules``,
10
+ ``.windsurfrules``) per the operator's opt-in. The detailed
11
+ section-presence map (``ai-surfaces.json``) is produced by a deeper
12
+ follow-up scan; this coarse pre-scan tells the deeper pass which
13
+ surfaces exist before it begins its content walk.
14
+
15
+ What this scan covers. Existence-and-presence checks for the
16
+ candidate surface paths plus the four mandatory-behavior blocks
17
+ that should appear inside ``AGENTS.md`` and mirror into ``CLAUDE.md``:
18
+
19
+ - ``AGENTS.md`` / ``CLAUDE.md`` mandatory blocks: `structured inquiry` discipline,
20
+ Plans Discipline, Authorship Header, Multi-Surface AI Conventions.
21
+ Each block's presence is detected by header / phrase heuristics.
22
+ - ``.github/copilot-instructions.md``: present / absent.
23
+ - Optional surfaces: presence flags only (no content scan).
24
+
25
+ What this scan reports. A single hit per detected absence or per
26
+ detected partial-presence. The output drives the deeper section-
27
+ presence pass and the optional-surface opt-in inquiry.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import argparse
33
+ import json
34
+ import sys
35
+ from dataclasses import asdict
36
+ from datetime import datetime, timezone
37
+ from pathlib import Path
38
+ from typing import Final
39
+
40
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
41
+
42
+ from _scan_lib import (
43
+ SEVERITY_HIGH,
44
+ SEVERITY_LOW,
45
+ SEVERITY_MEDIUM,
46
+ Hit,
47
+ load_inventory,
48
+ read_text_safely,
49
+ )
50
+
51
+ # Mandatory instruction-surface behavior blocks. Each is detected by a header
52
+ # phrase or canonical paragraph that signals the block's presence.
53
+ # Heuristic-driven; the deeper section-presence scan confirms
54
+ # structural conformance.
55
+ _MANDATORY_BLOCKS: Final[dict[str, list[str]]] = {
56
+ "structured-inquiry discipline": [
57
+ "structured inquiry",
58
+ "interactive-questions",
59
+ "structured-inquiry",
60
+ ],
61
+ "plans-discipline": [
62
+ "plans-discipline",
63
+ "Plans Discipline",
64
+ ".plans/ at",
65
+ "no-global-plans",
66
+ ],
67
+ "authorship-header": [
68
+ "authorship-header",
69
+ "Authorship Header",
70
+ "authorship banner",
71
+ "five canonical lines",
72
+ ],
73
+ "multi-surface-AI-conventions": [
74
+ "multi-surface",
75
+ "Multi-Surface",
76
+ "AI conventions",
77
+ "copilot-instructions",
78
+ ],
79
+ }
80
+
81
+ # Optional opt-in AI-conventions surfaces. Each is a path probed for
82
+ # existence; absence is informational, not a finding. The operator
83
+ # ratifies which subset to materialize.
84
+ _OPTIONAL_SURFACES: Final[list[str]] = [
85
+ "site/content/docs/architecture/agents.mdx",
86
+ ".cursorrules",
87
+ ".windsurfrules",
88
+ ]
89
+
90
+ # The Copilot instructions surface is authored by the dedicated
91
+ # AI-conventions author pass.
92
+ _COPILOT_PATH: Final[str] = ".github/copilot-instructions.md"
93
+
94
+ # AGENTS.md is the canonical project voice surface; CLAUDE.md is the
95
+ # Claude Code mirror. Absence of either mandatory surface is fatal.
96
+ _AGENTS_PATH: Final[str] = "AGENTS.md"
97
+ _CLAUDE_PATH: Final[str] = "CLAUDE.md"
98
+
99
+
100
+ def _scan_instruction_surface(path: str, content: str) -> list[Hit]:
101
+ """Detect missing mandatory blocks inside an instruction surface."""
102
+ hits: list[Hit] = []
103
+ for block_name, signals in _MANDATORY_BLOCKS.items():
104
+ if not any(signal in content for signal in signals):
105
+ hits.append(
106
+ Hit(
107
+ file=path,
108
+ line=1,
109
+ signal=f"instruction-surface-missing-block: {block_name}",
110
+ severity=SEVERITY_HIGH,
111
+ remediation=(
112
+ f"Author the '{block_name}' block in {path} per"
113
+ " the spec's mandatory-behavior catalog; the"
114
+ " block carries the canonical project directive"
115
+ " for that surface."
116
+ ),
117
+ )
118
+ )
119
+ return hits
120
+
121
+
122
+ def _scan_optional_surfaces(root: Path) -> list[Hit]:
123
+ """Emit informational hits for opt-in surfaces present on disk."""
124
+ hits: list[Hit] = []
125
+ for path in _OPTIONAL_SURFACES:
126
+ if (root / path).exists():
127
+ hits.append(
128
+ Hit(
129
+ file=path,
130
+ line=0,
131
+ signal=f"optional-surface-present: {path}",
132
+ severity=SEVERITY_LOW,
133
+ remediation=(
134
+ "The deeper section-presence scan confirms"
135
+ " structure; the opt-in inquiry recovers the"
136
+ " operator's ratification for keep / refit /"
137
+ " remove."
138
+ ),
139
+ )
140
+ )
141
+ return hits
142
+
143
+
144
+ def main(argv: list[str] | None = None) -> int:
145
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
146
+ parser.add_argument(
147
+ "--inventory",
148
+ type=Path,
149
+ default=Path(".audit/inventory.json"),
150
+ )
151
+ parser.add_argument("--root", type=Path, default=Path())
152
+ parser.add_argument(
153
+ "--output",
154
+ type=Path,
155
+ default=Path(".audit/drift-ai-surfaces-coarse.json"),
156
+ )
157
+ args = parser.parse_args(argv)
158
+
159
+ if not args.inventory.exists():
160
+ print(
161
+ f"error: inventory not found at {args.inventory}",
162
+ file=sys.stderr,
163
+ )
164
+ return 1
165
+
166
+ _, sha = load_inventory(args.inventory)
167
+ hits: list[Hit] = []
168
+
169
+ # Mandatory instruction-surface presence and block coverage.
170
+ agents_path = args.root / _AGENTS_PATH
171
+ claude_path = args.root / _CLAUDE_PATH
172
+ if not agents_path.exists():
173
+ hits.append(
174
+ Hit(
175
+ file=_AGENTS_PATH,
176
+ line=0,
177
+ signal="agents-md-absent",
178
+ severity=SEVERITY_HIGH,
179
+ remediation=(
180
+ "Author AGENTS.md at the repository root; this is the"
181
+ " canonical project instruction surface and its absence"
182
+ " disables downstream AI-conventions discipline."
183
+ ),
184
+ )
185
+ )
186
+ else:
187
+ hits.extend(
188
+ _scan_instruction_surface(_AGENTS_PATH, read_text_safely(agents_path))
189
+ )
190
+
191
+ if not claude_path.exists():
192
+ hits.append(
193
+ Hit(
194
+ file=_CLAUDE_PATH,
195
+ line=0,
196
+ signal="claude-md-absent",
197
+ severity=SEVERITY_HIGH,
198
+ remediation=(
199
+ "Author CLAUDE.md as the Claude Code mirror of AGENTS.md;"
200
+ " its absence leaves that harness without the shared"
201
+ " instruction discipline."
202
+ ),
203
+ )
204
+ )
205
+ else:
206
+ hits.extend(
207
+ _scan_instruction_surface(_CLAUDE_PATH, read_text_safely(claude_path))
208
+ )
209
+
210
+ # Copilot instructions presence.
211
+ copilot_path = args.root / _COPILOT_PATH
212
+ if not copilot_path.exists():
213
+ hits.append(
214
+ Hit(
215
+ file=_COPILOT_PATH,
216
+ line=0,
217
+ signal="copilot-instructions-absent",
218
+ severity=SEVERITY_MEDIUM,
219
+ remediation=(
220
+ "The AI-conventions author pass produces"
221
+ " .github/copilot-instructions.md per the canonical"
222
+ " nine-section structure. Until then the Copilot"
223
+ " surface defaults to its built-in heuristics without"
224
+ " the ecosystem's directive overlay."
225
+ ),
226
+ )
227
+ )
228
+
229
+ # Optional opt-in surfaces.
230
+ hits.extend(_scan_optional_surfaces(args.root))
231
+
232
+ payload = {
233
+ "generated": datetime.now(timezone.utc).isoformat(),
234
+ "scanner": "scan_ai_surfaces_coarse",
235
+ "inventory-source-sha256": sha,
236
+ "agents-md-present": agents_path.exists(),
237
+ "claude-md-present": claude_path.exists(),
238
+ "copilot-instructions-present": copilot_path.exists(),
239
+ "optional-surfaces-present": [
240
+ p for p in _OPTIONAL_SURFACES if (args.root / p).exists()
241
+ ],
242
+ "hit-count": len(hits),
243
+ "hits": [asdict(h) for h in hits],
244
+ }
245
+ args.output.parent.mkdir(parents=True, exist_ok=True)
246
+ args.output.write_text(
247
+ json.dumps(payload, indent=2) + "\n",
248
+ encoding="utf-8",
249
+ )
250
+ print(
251
+ f"scan_ai_surfaces_coarse: AGENTS.md={agents_path.exists()},"
252
+ f" CLAUDE.md={claude_path.exists()},"
253
+ f" copilot-instructions={copilot_path.exists()},"
254
+ f" optional={len(payload['optional-surfaces-present'])} of"
255
+ f" {len(_OPTIONAL_SURFACES)}; total hits={len(hits)}"
256
+ )
257
+ return 0
258
+
259
+
260
+ if __name__ == "__main__":
261
+ raise SystemExit(main())
@@ -0,0 +1,143 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Detect references to deprecated or renamed features.
4
+
5
+ Why this scan exists. Narrative artifacts (rules, agents, commands,
6
+ hook context, docs) accumulate references to vendor features as the
7
+ upstream surface evolves: model identifiers retire, tool names change,
8
+ flags are renamed, capabilities are deprecated. A reference to a
9
+ retired feature is functionally a broken pointer — it leads readers
10
+ toward behavior the runtime no longer supports. The scan walks every
11
+ narrative surface for tokens drawn from a curated deprecated list and
12
+ emits a finding per occurrence so the refit phases can replace them.
13
+
14
+ What this scan covers. Deprecated tokens live in
15
+ ``src/apothem/audit/deprecated-tokens.txt`` (one token per line; comments
16
+ prefixed with ``#``; case-sensitive substring match). The list is
17
+ intentionally seeded empty: at first run there is no proof any specific
18
+ token is deprecated, and a populated allow-list with no evidence would
19
+ fabricate findings. Tokens are added when the operator confirms a
20
+ specific feature has retired; the file then becomes a living deprecation
21
+ ledger.
22
+
23
+ What this scan excludes. Source code (``*.py``, ``*.sh``, ``*.ps1``)
24
+ is also walked because deprecated identifiers can appear as string
25
+ literals in tooling scaffolds. Memory and plan-artifact files are
26
+ excluded per the shared narrative-surface filter.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import argparse
32
+ import sys
33
+ from pathlib import Path
34
+ from typing import Any
35
+
36
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
37
+
38
+ from _scan_lib import (
39
+ CONTENT_ROOT,
40
+ NARRATIVE_CLASSES,
41
+ SEVERITY_MEDIUM,
42
+ Hit,
43
+ WalkCallback,
44
+ emit_json,
45
+ load_inventory,
46
+ walk_narrative_surfaces,
47
+ )
48
+
49
+
50
+ def _load_deprecated_tokens(token_file: Path) -> list[str]:
51
+ """Parse the deprecated-token catalog.
52
+
53
+ Lines beginning with ``#`` or empty lines are comments. Returns an
54
+ empty list when the file is absent — the scan then emits zero hits,
55
+ which is correct (no deprecated tokens declared, no findings).
56
+ """
57
+ if not token_file.exists():
58
+ return []
59
+ tokens: list[str] = []
60
+ for raw in token_file.read_text(encoding="utf-8").splitlines():
61
+ line = raw.strip()
62
+ if not line or line.startswith("#"):
63
+ continue
64
+ tokens.append(line)
65
+ return tokens
66
+
67
+
68
+ def _scan_file(tokens: list[str]) -> WalkCallback:
69
+ """Build a callback that records every line containing any token."""
70
+
71
+ def _walk(path: Path, record: dict[str, Any], content: str) -> list[Hit]:
72
+ if not tokens:
73
+ return []
74
+ hits: list[Hit] = []
75
+ for lineno, line in enumerate(content.splitlines(), start=1):
76
+ for token in tokens:
77
+ if token in line:
78
+ hits.append(
79
+ Hit(
80
+ file=record["path"],
81
+ line=lineno,
82
+ signal=f"deprecated-feature-reference: {token}",
83
+ severity=SEVERITY_MEDIUM,
84
+ remediation=(
85
+ f"Replace '{token}' with the current vendor identifier;"
86
+ " consult the deprecated-tokens.txt catalog for the"
87
+ " replacement guidance."
88
+ ),
89
+ )
90
+ )
91
+ return hits
92
+
93
+ return _walk
94
+
95
+
96
+ def main(argv: list[str] | None = None) -> int:
97
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
98
+ parser.add_argument(
99
+ "--inventory",
100
+ type=Path,
101
+ default=Path(".audit/inventory.json"),
102
+ help="Path to inventory.json (default: ./.audit/inventory.json)",
103
+ )
104
+ parser.add_argument(
105
+ "--root",
106
+ type=Path,
107
+ default=CONTENT_ROOT,
108
+ help="Content root the inventory paths resolve against "
109
+ "(default: the src/apothem package).",
110
+ )
111
+ parser.add_argument(
112
+ "--tokens",
113
+ type=Path,
114
+ default=Path("src/apothem/audit/deprecated-tokens.txt"),
115
+ help="Deprecated-token catalog (default: src/apothem/audit/deprecated-tokens.txt)",
116
+ )
117
+ parser.add_argument(
118
+ "--output",
119
+ type=Path,
120
+ default=Path(".audit/drift-feature-refs.json"),
121
+ help="Output JSON path (default: ./.audit/drift-feature-refs.json)",
122
+ )
123
+ args = parser.parse_args(argv)
124
+
125
+ if not args.inventory.exists():
126
+ print(f"error: inventory not found at {args.inventory}", file=sys.stderr)
127
+ return 1
128
+
129
+ records, sha = load_inventory(args.inventory)
130
+ tokens = _load_deprecated_tokens(args.tokens)
131
+ hits = walk_narrative_surfaces(records, args.root, _scan_file(tokens))
132
+ emit_json(args.output, "scan_drift_features", hits, sha)
133
+ narrative_count = sum(1 for r in records if r.get("class") in NARRATIVE_CLASSES)
134
+ print(
135
+ f"scan_drift_features: {len(hits)} hit(s) across "
136
+ f"{narrative_count} narrative files "
137
+ f"(deprecated-token catalog: {len(tokens)} entries)"
138
+ )
139
+ return 0
140
+
141
+
142
+ if __name__ == "__main__":
143
+ raise SystemExit(main())
@@ -0,0 +1,293 @@
1
+ # SPDX-License-Identifier: MIT
2
+
3
+ """Scan frontmatter consistency across artifact classes that carry it.
4
+
5
+ Why this scan exists. Agents, commands, skills, output-styles, and
6
+ several plan-artifact classes carry YAML frontmatter at the head of
7
+ each file. Per-class schemas have not yet been ratified at the time of
8
+ this scan, so the check is heuristic-driven: walk every artifact in a
9
+ class, sample the dominant key set and key order, and flag any sibling
10
+ that diverges. The scan also flags any TOML-style frontmatter
11
+ (``+++...+++``) because the discovered convention across every existing
12
+ file is YAML (``---...---``).
13
+
14
+ What this scan covers per class.
15
+
16
+ - **Required-key drift.** Keys present in at least 80% of the class's
17
+ files are treated as the de-facto required set; siblings missing any
18
+ of those keys produce a finding.
19
+ - **Key-order drift.** The dominant key sequence is the de-facto order;
20
+ siblings whose key sequence diverges produce a finding (rearranging a
21
+ frontmatter block is a near-zero-cost fix that compounds readability).
22
+ - **Format drift.** Any non-YAML frontmatter delimiter (``+++``,
23
+ ``;;;``, JSON object as first content) is flagged irrespective of
24
+ class.
25
+ - **Empty required field.** A required key with an empty string /
26
+ ``null`` value is flagged separately because the absence of value is
27
+ often more dangerous than the absence of key.
28
+
29
+ What this scan excludes. Memory and plan-artifact classes are excluded
30
+ from the dominant-pattern computation because they are not refit
31
+ targets at this phase. Files without any frontmatter that belong to a
32
+ class where frontmatter is universally present are flagged as
33
+ ``frontmatter-absent``.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import argparse
39
+ import sys
40
+ from collections import Counter
41
+ from collections.abc import Iterable
42
+ from pathlib import Path
43
+ from typing import Any, Final
44
+
45
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
46
+
47
+ from _scan_lib import (
48
+ CONTENT_ROOT,
49
+ SEVERITY_MEDIUM,
50
+ Hit,
51
+ emit_json,
52
+ load_inventory,
53
+ read_text_safely,
54
+ )
55
+
56
+ # Artifact classes that carry frontmatter as a convention. Settings
57
+ # files are JSON; scaffolding files vary; only the listed classes are
58
+ # walked for frontmatter consistency.
59
+ FRONTMATTER_CLASSES: Final[frozenset[str]] = frozenset(
60
+ {"agent", "command", "skill", "output-style"}
61
+ )
62
+
63
+ # Frontmatter requires presence in at least this fraction of a class's
64
+ # files to be treated as the de-facto required set.
65
+ REQUIRED_THRESHOLD: Final[float] = 0.8
66
+
67
+
68
+ def _extract_frontmatter(content: str) -> tuple[list[str], str | None]:
69
+ """Return ``(ordered-keys, delimiter)`` for the document's
70
+ frontmatter, or ``([], None)`` if no frontmatter is present.
71
+
72
+ Recognizes YAML (``---``), TOML (``+++``), and a permissive
73
+ semicolon delimiter (``;;;``). Inside a YAML block, only top-level
74
+ keys (un-indented ``key:`` lines) are captured; nested keys are not
75
+ part of the frontmatter contract.
76
+ """
77
+ lines = content.splitlines()
78
+ if not lines:
79
+ return [], None
80
+ first = lines[0].strip()
81
+ if first not in ("---", "+++", ";;;"):
82
+ return [], None
83
+ delimiter = first
84
+ keys: list[str] = []
85
+ for line in lines[1:]:
86
+ stripped = line.strip()
87
+ if stripped == delimiter:
88
+ return keys, delimiter
89
+ # Top-level YAML key: un-indented `key:` (no leading whitespace).
90
+ if line and not line[0].isspace() and ":" in line:
91
+ key = line.split(":", 1)[0].strip()
92
+ if key and not key.startswith("#"):
93
+ keys.append(key)
94
+ # Closing delimiter not found; treat as malformed frontmatter.
95
+ return keys, "malformed"
96
+
97
+
98
+ def _empty_value_keys(content: str, keys: list[str]) -> list[str]:
99
+ """Return keys whose value is empty / null / quoted-empty."""
100
+ if not keys:
101
+ return []
102
+ empty: list[str] = []
103
+ in_block = False
104
+ for line in content.splitlines():
105
+ stripped = line.strip()
106
+ if stripped == "---":
107
+ if in_block:
108
+ break
109
+ in_block = True
110
+ continue
111
+ if not in_block or line[:1].isspace() or ":" not in line:
112
+ continue
113
+ key, _, value = line.partition(":")
114
+ key = key.strip()
115
+ value = value.strip()
116
+ if key in keys and value in ("", "''", '""', "null", "~"):
117
+ empty.append(key)
118
+ return empty
119
+
120
+
121
+ def _gather_class_records(
122
+ records: Iterable[dict[str, Any]], cls: str
123
+ ) -> list[dict[str, Any]]:
124
+ return [r for r in records if r.get("class") == cls]
125
+
126
+
127
+ def _compute_class_baseline(
128
+ class_records: list[dict[str, Any]], root: Path
129
+ ) -> tuple[set[str], list[str]]:
130
+ """Return ``(required-keys, dominant-order)`` for a class."""
131
+ if not class_records:
132
+ return set(), []
133
+ key_presence: Counter[str] = Counter()
134
+ order_counter: Counter[tuple[str, ...]] = Counter()
135
+ n = 0
136
+ for record in class_records:
137
+ path = root / record["path"]
138
+ content = read_text_safely(path)
139
+ if not content:
140
+ continue
141
+ keys, delimiter = _extract_frontmatter(content)
142
+ if not keys or delimiter != "---":
143
+ continue
144
+ n += 1
145
+ for k in keys:
146
+ key_presence[k] += 1
147
+ order_counter[tuple(keys)] += 1
148
+ if n == 0:
149
+ return set(), []
150
+ threshold = max(1, int(REQUIRED_THRESHOLD * n))
151
+ required = {k for k, count in key_presence.items() if count >= threshold}
152
+ dominant_order = list(order_counter.most_common(1)[0][0]) if order_counter else []
153
+ return required, dominant_order
154
+
155
+
156
+ def _scan_class(cls: str, records: list[dict[str, Any]], root: Path) -> list[Hit]:
157
+ """Walk a single class and emit hits for divergent siblings."""
158
+ required, dominant_order = _compute_class_baseline(records, root)
159
+ hits: list[Hit] = []
160
+ for record in records:
161
+ rel = record["path"]
162
+ path = root / rel
163
+ content = read_text_safely(path)
164
+ if not content:
165
+ continue
166
+ keys, delimiter = _extract_frontmatter(content)
167
+ hits.extend(
168
+ _diagnose_record(rel, content, keys, delimiter, required, dominant_order)
169
+ )
170
+ return hits
171
+
172
+
173
+ def _diagnose_record(
174
+ rel: str,
175
+ content: str,
176
+ keys: list[str],
177
+ delimiter: str | None,
178
+ required: set[str],
179
+ dominant_order: list[str],
180
+ ) -> list[Hit]:
181
+ """Return all findings for a single file's frontmatter state."""
182
+ hits: list[Hit] = []
183
+ if delimiter is None:
184
+ if required:
185
+ hits.append(
186
+ Hit(
187
+ file=rel,
188
+ line=1,
189
+ signal="frontmatter-absent",
190
+ severity=SEVERITY_MEDIUM,
191
+ remediation=(
192
+ "Add the class's required frontmatter keys at the"
193
+ " head of the file enclosed by '---' delimiters."
194
+ ),
195
+ )
196
+ )
197
+ return hits
198
+ if delimiter == "+++" or delimiter == ";;;" or delimiter == "malformed":
199
+ hits.append(
200
+ Hit(
201
+ file=rel,
202
+ line=1,
203
+ signal=f"frontmatter-non-yaml-or-malformed: {delimiter}",
204
+ severity=SEVERITY_MEDIUM,
205
+ remediation=(
206
+ "Convert the frontmatter to the YAML form delimited"
207
+ " by '---'; non-YAML delimiters break tooling that"
208
+ " expects the convention."
209
+ ),
210
+ )
211
+ )
212
+ missing = required - set(keys)
213
+ for key in sorted(missing):
214
+ hits.append(
215
+ Hit(
216
+ file=rel,
217
+ line=1,
218
+ signal=f"frontmatter-missing-required: {key}",
219
+ severity=SEVERITY_MEDIUM,
220
+ remediation=(
221
+ f"Add '{key}: <value>' to the frontmatter; the key is"
222
+ " present in at least 80% of the class's files."
223
+ ),
224
+ )
225
+ )
226
+ if dominant_order and keys != dominant_order and not missing:
227
+ # Only flag order drift when the file has the full required set;
228
+ # missing-key cases dominate and ordering is secondary.
229
+ hits.append(
230
+ Hit(
231
+ file=rel,
232
+ line=1,
233
+ signal="frontmatter-key-order-drift",
234
+ severity=SEVERITY_MEDIUM,
235
+ remediation=(
236
+ "Reorder the frontmatter keys to match the class's"
237
+ f" dominant sequence: {dominant_order}."
238
+ ),
239
+ )
240
+ )
241
+ for key in _empty_value_keys(content, list(required & set(keys))):
242
+ hits.append(
243
+ Hit(
244
+ file=rel,
245
+ line=1,
246
+ signal=f"frontmatter-empty-required-value: {key}",
247
+ severity=SEVERITY_MEDIUM,
248
+ remediation=(
249
+ f"Populate '{key}' with a non-empty value; the key is"
250
+ " required but its value is blank or null."
251
+ ),
252
+ )
253
+ )
254
+ return hits
255
+
256
+
257
+ def main(argv: list[str] | None = None) -> int:
258
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
259
+ parser.add_argument(
260
+ "--inventory",
261
+ type=Path,
262
+ default=Path(".audit/inventory.json"),
263
+ )
264
+ parser.add_argument("--root", type=Path, default=CONTENT_ROOT)
265
+ parser.add_argument(
266
+ "--output",
267
+ type=Path,
268
+ default=Path(".audit/drift-frontmatter.json"),
269
+ )
270
+ args = parser.parse_args(argv)
271
+
272
+ if not args.inventory.exists():
273
+ print(
274
+ f"error: inventory not found at {args.inventory}",
275
+ file=sys.stderr,
276
+ )
277
+ return 1
278
+
279
+ records, sha = load_inventory(args.inventory)
280
+ hits: list[Hit] = []
281
+ per_class_counts: dict[str, int] = {}
282
+ for cls in sorted(FRONTMATTER_CLASSES):
283
+ cls_records = _gather_class_records(records, cls)
284
+ per_class_counts[cls] = len(cls_records)
285
+ hits.extend(_scan_class(cls, cls_records, args.root))
286
+ emit_json(args.output, "scan_frontmatter", hits, sha)
287
+ summary = ", ".join(f"{c}={n}" for c, n in per_class_counts.items())
288
+ print(f"scan_frontmatter: {len(hits)} hit(s) across classes [{summary}]")
289
+ return 0
290
+
291
+
292
+ if __name__ == "__main__":
293
+ raise SystemExit(main())