@r1z4x/tezgah 1.0.0

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 (582) hide show
  1. package/CHANGELOG.md +738 -0
  2. package/LICENSE +21 -0
  3. package/MANIFEST +753 -0
  4. package/README.de.md +169 -0
  5. package/README.es.md +168 -0
  6. package/README.fr.md +166 -0
  7. package/README.md +161 -0
  8. package/README.tr.md +163 -0
  9. package/agents/tezgah-reviewer.md +70 -0
  10. package/bin/codegen +337 -0
  11. package/bin/consult +797 -0
  12. package/bin/tezgah-adhd +50 -0
  13. package/bin/tezgah-agents +45 -0
  14. package/bin/tezgah-capture +66 -0
  15. package/bin/tezgah-context +96 -0
  16. package/bin/tezgah-decisions +213 -0
  17. package/bin/tezgah-design +953 -0
  18. package/bin/tezgah-docs +1199 -0
  19. package/bin/tezgah-doctor +497 -0
  20. package/bin/tezgah-dsh +37 -0
  21. package/bin/tezgah-dsh.cmd +70 -0
  22. package/bin/tezgah-gate +75 -0
  23. package/bin/tezgah-import-ai-research +439 -0
  24. package/bin/tezgah-index +32 -0
  25. package/bin/tezgah-lessons +148 -0
  26. package/bin/tezgah-mcp +322 -0
  27. package/bin/tezgah-migrate +324 -0
  28. package/bin/tezgah-pony +50 -0
  29. package/bin/tezgah-research +772 -0
  30. package/bin/tezgah-rollback +93 -0
  31. package/bin/tezgah-route +124 -0
  32. package/bin/tezgah-setup +5798 -0
  33. package/bin/tezgah-status +383 -0
  34. package/bin/tezgah-task +607 -0
  35. package/bin/tezgah-taste +423 -0
  36. package/bin/tezgah-triage +485 -0
  37. package/bin/tezgah.js +36 -0
  38. package/commands/adhd.md +21 -0
  39. package/commands/ponytail.md +21 -0
  40. package/docs/README.md +112 -0
  41. package/docs/architecture.md +226 -0
  42. package/docs/citations-baseline.json +59 -0
  43. package/docs/clarity-baseline.json +116 -0
  44. package/docs/contract.md +416 -0
  45. package/docs/environment.md +141 -0
  46. package/docs/evidence.md +831 -0
  47. package/docs/feature-audit.md +139 -0
  48. package/docs/gate.md +407 -0
  49. package/docs/glossary.md +201 -0
  50. package/docs/hosts.md +400 -0
  51. package/docs/index.json +471 -0
  52. package/docs/judge.md +193 -0
  53. package/docs/layers.md +189 -0
  54. package/docs/models.md +160 -0
  55. package/docs/operations.md +666 -0
  56. package/docs/research.md +809 -0
  57. package/docs/results/paired-outcome-2026-10.jsonl +254 -0
  58. package/docs/results/paired-outcome-2026-10.md +204 -0
  59. package/docs/skills.md +236 -0
  60. package/docs/status-line.md +315 -0
  61. package/docs/testing.md +287 -0
  62. package/docs/vision.md +117 -0
  63. package/hooks/hooks.json +33 -0
  64. package/hooks/projects-auto-init.py +98 -0
  65. package/hooks/projects-posttooluse.py +149 -0
  66. package/hooks/projects-pretooluse.py +40 -0
  67. package/hooks/projects-stop.py +60 -0
  68. package/hooks/tezgah_agents.py +817 -0
  69. package/hooks/tezgah_apps.py +266 -0
  70. package/hooks/tezgah_attest.py +255 -0
  71. package/hooks/tezgah_clarity.py +171 -0
  72. package/hooks/tezgah_components.py +140 -0
  73. package/hooks/tezgah_context.py +2953 -0
  74. package/hooks/tezgah_embed.py +765 -0
  75. package/hooks/tezgah_gate.py +2736 -0
  76. package/hooks/tezgah_guard.py +177 -0
  77. package/hooks/tezgah_index.py +124 -0
  78. package/hooks/tezgah_integrity.py +5141 -0
  79. package/hooks/tezgah_judge.py +487 -0
  80. package/hooks/tezgah_lang.py +230 -0
  81. package/hooks/tezgah_lessons.py +210 -0
  82. package/hooks/tezgah_models.py +855 -0
  83. package/hooks/tezgah_paths.py +1132 -0
  84. package/hooks/tezgah_policy.py +1039 -0
  85. package/hooks/tezgah_rank.py +77 -0
  86. package/hooks/tezgah_replay.py +1109 -0
  87. package/hooks/tezgah_research.py +5176 -0
  88. package/hooks/tezgah_shapes.py +305 -0
  89. package/hooks/tezgah_skill_pick.py +227 -0
  90. package/hooks/tezgah_snapshot.py +490 -0
  91. package/hooks/tezgah_stopspec.py +320 -0
  92. package/hooks/tezgah_task.py +458 -0
  93. package/hooks/tezgah_taste.py +201 -0
  94. package/hooks/tezgah_untrusted.py +112 -0
  95. package/hooks/tezgah_update.py +430 -0
  96. package/hosts/codex/hook.py +268 -0
  97. package/hosts/codex/hooks.json +26 -0
  98. package/hosts/cursor/hook.py +461 -0
  99. package/hosts/cursor/hooks.json +44 -0
  100. package/hosts/dsh/hooks.json +21 -0
  101. package/hosts/dsh/statusline/lib/client.js +138 -0
  102. package/hosts/dsh/statusline/lib/index.js +104 -0
  103. package/hosts/dsh/statusline/package.json +28 -0
  104. package/hosts/omp/hook.py +277 -0
  105. package/hosts/omp/tezgah-hook.ts.in +701 -0
  106. package/hosts/opencode/plugins/tezgah.js +2742 -0
  107. package/hosts/opencode/tui/tezgah-tui.tsx +151 -0
  108. package/output-styles/tezgah.md +142 -0
  109. package/package.json +32 -0
  110. package/packaging/build.sh +342 -0
  111. package/packaging/install.ps1 +166 -0
  112. package/packaging/install.sh +56 -0
  113. package/packaging/upgrade.sh +142 -0
  114. package/skills/ai-research/0-autoresearch-skill/SKILL.md +411 -0
  115. package/skills/ai-research/0-autoresearch-skill/references/agent-continuity.md +95 -0
  116. package/skills/ai-research/0-autoresearch-skill/references/progress-reporting.md +166 -0
  117. package/skills/ai-research/0-autoresearch-skill/references/skill-routing.md +219 -0
  118. package/skills/ai-research/0-autoresearch-skill/templates/findings.md +44 -0
  119. package/skills/ai-research/0-autoresearch-skill/templates/research-log.md +41 -0
  120. package/skills/ai-research/01-model-architecture/litgpt/SKILL.md +469 -0
  121. package/skills/ai-research/01-model-architecture/litgpt/references/custom-models.md +569 -0
  122. package/skills/ai-research/01-model-architecture/litgpt/references/distributed-training.md +452 -0
  123. package/skills/ai-research/01-model-architecture/litgpt/references/supported-models.md +337 -0
  124. package/skills/ai-research/01-model-architecture/litgpt/references/training-recipes.md +620 -0
  125. package/skills/ai-research/01-model-architecture/mamba/SKILL.md +260 -0
  126. package/skills/ai-research/01-model-architecture/mamba/references/architecture-details.md +207 -0
  127. package/skills/ai-research/01-model-architecture/mamba/references/benchmarks.md +256 -0
  128. package/skills/ai-research/01-model-architecture/mamba/references/training-guide.md +389 -0
  129. package/skills/ai-research/01-model-architecture/nanogpt/SKILL.md +290 -0
  130. package/skills/ai-research/01-model-architecture/nanogpt/references/architecture.md +383 -0
  131. package/skills/ai-research/01-model-architecture/nanogpt/references/data.md +477 -0
  132. package/skills/ai-research/01-model-architecture/nanogpt/references/training.md +565 -0
  133. package/skills/ai-research/01-model-architecture/rwkv/SKILL.md +260 -0
  134. package/skills/ai-research/01-model-architecture/rwkv/references/architecture-details.md +345 -0
  135. package/skills/ai-research/01-model-architecture/rwkv/references/rwkv7.md +387 -0
  136. package/skills/ai-research/01-model-architecture/rwkv/references/state-management.md +370 -0
  137. package/skills/ai-research/01-model-architecture/torchtitan/SKILL.md +358 -0
  138. package/skills/ai-research/01-model-architecture/torchtitan/references/checkpoint.md +182 -0
  139. package/skills/ai-research/01-model-architecture/torchtitan/references/custom-models.md +259 -0
  140. package/skills/ai-research/01-model-architecture/torchtitan/references/float8.md +134 -0
  141. package/skills/ai-research/01-model-architecture/torchtitan/references/fsdp.md +127 -0
  142. package/skills/ai-research/02-tokenization/huggingface-tokenizers/SKILL.md +516 -0
  143. package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/algorithms.md +654 -0
  144. package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/integration.md +638 -0
  145. package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/pipeline.md +724 -0
  146. package/skills/ai-research/02-tokenization/huggingface-tokenizers/references/training.md +566 -0
  147. package/skills/ai-research/02-tokenization/sentencepiece/SKILL.md +235 -0
  148. package/skills/ai-research/02-tokenization/sentencepiece/references/algorithms.md +201 -0
  149. package/skills/ai-research/02-tokenization/sentencepiece/references/training.md +305 -0
  150. package/skills/ai-research/03-fine-tuning/axolotl/SKILL.md +158 -0
  151. package/skills/ai-research/03-fine-tuning/axolotl/references/api.md +5549 -0
  152. package/skills/ai-research/03-fine-tuning/axolotl/references/dataset-formats.md +1030 -0
  153. package/skills/ai-research/03-fine-tuning/axolotl/references/index.md +16 -0
  154. package/skills/ai-research/03-fine-tuning/axolotl/references/other.md +3564 -0
  155. package/skills/ai-research/03-fine-tuning/llama-factory/SKILL.md +80 -0
  156. package/skills/ai-research/03-fine-tuning/llama-factory/references/_images.md +24 -0
  157. package/skills/ai-research/03-fine-tuning/llama-factory/references/advanced.md +1056 -0
  158. package/skills/ai-research/03-fine-tuning/llama-factory/references/getting_started.md +350 -0
  159. package/skills/ai-research/03-fine-tuning/llama-factory/references/index.md +20 -0
  160. package/skills/ai-research/03-fine-tuning/llama-factory/references/other.md +32 -0
  161. package/skills/ai-research/03-fine-tuning/peft/SKILL.md +431 -0
  162. package/skills/ai-research/03-fine-tuning/peft/references/advanced-usage.md +515 -0
  163. package/skills/ai-research/03-fine-tuning/peft/references/troubleshooting.md +481 -0
  164. package/skills/ai-research/03-fine-tuning/unsloth/SKILL.md +80 -0
  165. package/skills/ai-research/03-fine-tuning/unsloth/references/index.md +8 -0
  166. package/skills/ai-research/03-fine-tuning/unsloth/references/llms.md +83 -0
  167. package/skills/ai-research/04-mechanistic-interpretability/nnsight/SKILL.md +436 -0
  168. package/skills/ai-research/04-mechanistic-interpretability/nnsight/references/README.md +79 -0
  169. package/skills/ai-research/04-mechanistic-interpretability/nnsight/references/api.md +345 -0
  170. package/skills/ai-research/04-mechanistic-interpretability/nnsight/references/tutorials.md +301 -0
  171. package/skills/ai-research/04-mechanistic-interpretability/pyvene/SKILL.md +473 -0
  172. package/skills/ai-research/04-mechanistic-interpretability/pyvene/references/README.md +74 -0
  173. package/skills/ai-research/04-mechanistic-interpretability/pyvene/references/api.md +384 -0
  174. package/skills/ai-research/04-mechanistic-interpretability/pyvene/references/tutorials.md +377 -0
  175. package/skills/ai-research/04-mechanistic-interpretability/saelens/SKILL.md +386 -0
  176. package/skills/ai-research/04-mechanistic-interpretability/saelens/references/README.md +71 -0
  177. package/skills/ai-research/04-mechanistic-interpretability/saelens/references/api.md +334 -0
  178. package/skills/ai-research/04-mechanistic-interpretability/saelens/references/tutorials.md +319 -0
  179. package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/SKILL.md +346 -0
  180. package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/references/README.md +55 -0
  181. package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/references/api.md +363 -0
  182. package/skills/ai-research/04-mechanistic-interpretability/transformer-lens/references/tutorials.md +340 -0
  183. package/skills/ai-research/05-data-processing/nemo-curator/SKILL.md +383 -0
  184. package/skills/ai-research/05-data-processing/nemo-curator/references/deduplication.md +88 -0
  185. package/skills/ai-research/05-data-processing/nemo-curator/references/filtering.md +103 -0
  186. package/skills/ai-research/05-data-processing/ray-data/SKILL.md +326 -0
  187. package/skills/ai-research/05-data-processing/ray-data/references/integration.md +83 -0
  188. package/skills/ai-research/05-data-processing/ray-data/references/transformations.md +84 -0
  189. package/skills/ai-research/06-post-training/grpo-rl-training/README.md +98 -0
  190. package/skills/ai-research/06-post-training/grpo-rl-training/SKILL.md +572 -0
  191. package/skills/ai-research/06-post-training/miles/SKILL.md +315 -0
  192. package/skills/ai-research/06-post-training/miles/references/api-reference.md +142 -0
  193. package/skills/ai-research/06-post-training/miles/references/troubleshooting.md +353 -0
  194. package/skills/ai-research/06-post-training/openrlhf/SKILL.md +249 -0
  195. package/skills/ai-research/06-post-training/openrlhf/references/algorithm-comparison.md +405 -0
  196. package/skills/ai-research/06-post-training/openrlhf/references/custom-rewards.md +531 -0
  197. package/skills/ai-research/06-post-training/openrlhf/references/hybrid-engine.md +288 -0
  198. package/skills/ai-research/06-post-training/openrlhf/references/multi-node-training.md +455 -0
  199. package/skills/ai-research/06-post-training/simpo/SKILL.md +219 -0
  200. package/skills/ai-research/06-post-training/simpo/references/datasets.md +479 -0
  201. package/skills/ai-research/06-post-training/simpo/references/hyperparameters.md +453 -0
  202. package/skills/ai-research/06-post-training/simpo/references/loss-functions.md +351 -0
  203. package/skills/ai-research/06-post-training/slime/SKILL.md +464 -0
  204. package/skills/ai-research/06-post-training/slime/references/api-reference.md +393 -0
  205. package/skills/ai-research/06-post-training/slime/references/troubleshooting.md +387 -0
  206. package/skills/ai-research/06-post-training/torchforge/SKILL.md +433 -0
  207. package/skills/ai-research/06-post-training/torchforge/references/api-reference.md +328 -0
  208. package/skills/ai-research/06-post-training/torchforge/references/troubleshooting.md +410 -0
  209. package/skills/ai-research/06-post-training/trl-fine-tuning/SKILL.md +455 -0
  210. package/skills/ai-research/06-post-training/trl-fine-tuning/references/dpo-variants.md +228 -0
  211. package/skills/ai-research/06-post-training/trl-fine-tuning/references/online-rl.md +83 -0
  212. package/skills/ai-research/06-post-training/trl-fine-tuning/references/reward-modeling.md +123 -0
  213. package/skills/ai-research/06-post-training/trl-fine-tuning/references/sft-training.md +169 -0
  214. package/skills/ai-research/06-post-training/verl/SKILL.md +391 -0
  215. package/skills/ai-research/06-post-training/verl/references/api-reference.md +302 -0
  216. package/skills/ai-research/06-post-training/verl/references/troubleshooting.md +392 -0
  217. package/skills/ai-research/07-safety-alignment/constitutional-ai/SKILL.md +290 -0
  218. package/skills/ai-research/07-safety-alignment/llamaguard/SKILL.md +337 -0
  219. package/skills/ai-research/07-safety-alignment/nemo-guardrails/SKILL.md +297 -0
  220. package/skills/ai-research/07-safety-alignment/prompt-guard/SKILL.md +312 -0
  221. package/skills/ai-research/08-distributed-training/accelerate/SKILL.md +332 -0
  222. package/skills/ai-research/08-distributed-training/accelerate/references/custom-plugins.md +454 -0
  223. package/skills/ai-research/08-distributed-training/accelerate/references/megatron-integration.md +490 -0
  224. package/skills/ai-research/08-distributed-training/accelerate/references/performance.md +526 -0
  225. package/skills/ai-research/08-distributed-training/deepspeed/SKILL.md +141 -0
  226. package/skills/ai-research/08-distributed-training/deepspeed/references/08.md +18 -0
  227. package/skills/ai-research/08-distributed-training/deepspeed/references/09.md +174 -0
  228. package/skills/ai-research/08-distributed-training/deepspeed/references/2020.md +379 -0
  229. package/skills/ai-research/08-distributed-training/deepspeed/references/2023.md +280 -0
  230. package/skills/ai-research/08-distributed-training/deepspeed/references/assets.md +180 -0
  231. package/skills/ai-research/08-distributed-training/deepspeed/references/index.md +36 -0
  232. package/skills/ai-research/08-distributed-training/deepspeed/references/mii.md +119 -0
  233. package/skills/ai-research/08-distributed-training/deepspeed/references/other.md +1192 -0
  234. package/skills/ai-research/08-distributed-training/megatron-core/SKILL.md +366 -0
  235. package/skills/ai-research/08-distributed-training/megatron-core/references/benchmarks.md +250 -0
  236. package/skills/ai-research/08-distributed-training/megatron-core/references/parallelism-guide.md +405 -0
  237. package/skills/ai-research/08-distributed-training/megatron-core/references/production-examples.md +474 -0
  238. package/skills/ai-research/08-distributed-training/megatron-core/references/training-recipes.md +548 -0
  239. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/SKILL.md +231 -0
  240. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_dcp_async_recipe.md +14 -0
  241. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_dcp_overview.md +19 -0
  242. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_dcp_recipe.md +18 -0
  243. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_ddp_notes.md +10 -0
  244. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_device_mesh_tutorial.md +22 -0
  245. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_examples_fsdp2.md +15 -0
  246. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_fsdp1_api.md +9 -0
  247. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_fsdp2_tutorial.md +45 -0
  248. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_fully_shard_api.md +49 -0
  249. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/pytorch_tp_tutorial.md +22 -0
  250. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/ray_train_fsdp2_example.md +13 -0
  251. package/skills/ai-research/08-distributed-training/pytorch-fsdp2/references/torchtitan_fsdp_notes.md +14 -0
  252. package/skills/ai-research/08-distributed-training/pytorch-lightning/SKILL.md +346 -0
  253. package/skills/ai-research/08-distributed-training/pytorch-lightning/references/callbacks.md +437 -0
  254. package/skills/ai-research/08-distributed-training/pytorch-lightning/references/distributed.md +491 -0
  255. package/skills/ai-research/08-distributed-training/pytorch-lightning/references/hyperparameter-tuning.md +557 -0
  256. package/skills/ai-research/08-distributed-training/ray-train/SKILL.md +406 -0
  257. package/skills/ai-research/08-distributed-training/ray-train/references/multi-node.md +629 -0
  258. package/skills/ai-research/09-infrastructure/lambda-labs/SKILL.md +545 -0
  259. package/skills/ai-research/09-infrastructure/lambda-labs/references/advanced-usage.md +612 -0
  260. package/skills/ai-research/09-infrastructure/lambda-labs/references/troubleshooting.md +531 -0
  261. package/skills/ai-research/09-infrastructure/modal/SKILL.md +341 -0
  262. package/skills/ai-research/09-infrastructure/modal/references/advanced-usage.md +504 -0
  263. package/skills/ai-research/09-infrastructure/modal/references/troubleshooting.md +495 -0
  264. package/skills/ai-research/09-infrastructure/skypilot/SKILL.md +509 -0
  265. package/skills/ai-research/09-infrastructure/skypilot/references/advanced-usage.md +492 -0
  266. package/skills/ai-research/09-infrastructure/skypilot/references/troubleshooting.md +571 -0
  267. package/skills/ai-research/10-optimization/awq/SKILL.md +310 -0
  268. package/skills/ai-research/10-optimization/awq/references/advanced-usage.md +325 -0
  269. package/skills/ai-research/10-optimization/awq/references/troubleshooting.md +345 -0
  270. package/skills/ai-research/10-optimization/bitsandbytes/SKILL.md +411 -0
  271. package/skills/ai-research/10-optimization/bitsandbytes/references/memory-optimization.md +522 -0
  272. package/skills/ai-research/10-optimization/bitsandbytes/references/qlora-training.md +522 -0
  273. package/skills/ai-research/10-optimization/bitsandbytes/references/quantization-formats.md +448 -0
  274. package/skills/ai-research/10-optimization/flash-attention/SKILL.md +367 -0
  275. package/skills/ai-research/10-optimization/flash-attention/references/benchmarks.md +216 -0
  276. package/skills/ai-research/10-optimization/flash-attention/references/transformers-integration.md +294 -0
  277. package/skills/ai-research/10-optimization/gguf/SKILL.md +427 -0
  278. package/skills/ai-research/10-optimization/gguf/references/advanced-usage.md +505 -0
  279. package/skills/ai-research/10-optimization/gguf/references/troubleshooting.md +443 -0
  280. package/skills/ai-research/10-optimization/gptq/SKILL.md +450 -0
  281. package/skills/ai-research/10-optimization/gptq/references/calibration.md +338 -0
  282. package/skills/ai-research/10-optimization/gptq/references/integration.md +130 -0
  283. package/skills/ai-research/10-optimization/gptq/references/troubleshooting.md +96 -0
  284. package/skills/ai-research/10-optimization/hqq/SKILL.md +445 -0
  285. package/skills/ai-research/10-optimization/hqq/references/advanced-usage.md +529 -0
  286. package/skills/ai-research/10-optimization/hqq/references/troubleshooting.md +504 -0
  287. package/skills/ai-research/10-optimization/ml-training-recipes/SKILL.md +319 -0
  288. package/skills/ai-research/10-optimization/ml-training-recipes/references/architecture.md +320 -0
  289. package/skills/ai-research/10-optimization/ml-training-recipes/references/biomedical.md +498 -0
  290. package/skills/ai-research/10-optimization/ml-training-recipes/references/domain-specific.md +626 -0
  291. package/skills/ai-research/10-optimization/ml-training-recipes/references/experiment-loop.md +129 -0
  292. package/skills/ai-research/10-optimization/ml-training-recipes/references/optimizers.md +330 -0
  293. package/skills/ai-research/10-optimization/ml-training-recipes/references/scaling-and-selection.md +428 -0
  294. package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/SKILL.md +405 -0
  295. package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/references/benchmarks.md +394 -0
  296. package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/references/custom-tasks.md +425 -0
  297. package/skills/ai-research/11-evaluation/bigcode-evaluation-harness/references/issues.md +395 -0
  298. package/skills/ai-research/11-evaluation/lm-evaluation-harness/SKILL.md +490 -0
  299. package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/api-evaluation.md +491 -0
  300. package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/benchmark-guide.md +489 -0
  301. package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/custom-tasks.md +603 -0
  302. package/skills/ai-research/11-evaluation/lm-evaluation-harness/references/distributed-eval.md +520 -0
  303. package/skills/ai-research/11-evaluation/nemo-evaluator/SKILL.md +494 -0
  304. package/skills/ai-research/11-evaluation/nemo-evaluator/references/adapter-system.md +341 -0
  305. package/skills/ai-research/11-evaluation/nemo-evaluator/references/configuration.md +448 -0
  306. package/skills/ai-research/11-evaluation/nemo-evaluator/references/custom-benchmarks.md +316 -0
  307. package/skills/ai-research/11-evaluation/nemo-evaluator/references/execution-backends.md +362 -0
  308. package/skills/ai-research/12-inference-serving/llama-cpp/SKILL.md +258 -0
  309. package/skills/ai-research/12-inference-serving/llama-cpp/references/optimization.md +90 -0
  310. package/skills/ai-research/12-inference-serving/llama-cpp/references/quantization.md +214 -0
  311. package/skills/ai-research/12-inference-serving/llama-cpp/references/server.md +126 -0
  312. package/skills/ai-research/12-inference-serving/sglang/SKILL.md +442 -0
  313. package/skills/ai-research/12-inference-serving/sglang/references/deployment.md +491 -0
  314. package/skills/ai-research/12-inference-serving/sglang/references/radix-attention.md +414 -0
  315. package/skills/ai-research/12-inference-serving/sglang/references/structured-generation.md +542 -0
  316. package/skills/ai-research/12-inference-serving/tensorrt-llm/SKILL.md +187 -0
  317. package/skills/ai-research/12-inference-serving/tensorrt-llm/references/multi-gpu.md +299 -0
  318. package/skills/ai-research/12-inference-serving/tensorrt-llm/references/optimization.md +243 -0
  319. package/skills/ai-research/12-inference-serving/tensorrt-llm/references/serving.md +471 -0
  320. package/skills/ai-research/12-inference-serving/vllm/SKILL.md +364 -0
  321. package/skills/ai-research/12-inference-serving/vllm/references/optimization.md +227 -0
  322. package/skills/ai-research/12-inference-serving/vllm/references/quantization.md +285 -0
  323. package/skills/ai-research/12-inference-serving/vllm/references/server-deployment.md +256 -0
  324. package/skills/ai-research/12-inference-serving/vllm/references/troubleshooting.md +448 -0
  325. package/skills/ai-research/13-mlops/mlflow/SKILL.md +704 -0
  326. package/skills/ai-research/13-mlops/mlflow/references/deployment.md +745 -0
  327. package/skills/ai-research/13-mlops/mlflow/references/model-registry.md +771 -0
  328. package/skills/ai-research/13-mlops/mlflow/references/tracking.md +681 -0
  329. package/skills/ai-research/13-mlops/swanlab/SKILL.md +406 -0
  330. package/skills/ai-research/13-mlops/swanlab/references/integrations.md +305 -0
  331. package/skills/ai-research/13-mlops/swanlab/references/visualization.md +234 -0
  332. package/skills/ai-research/13-mlops/tensorboard/SKILL.md +629 -0
  333. package/skills/ai-research/13-mlops/tensorboard/references/integrations.md +639 -0
  334. package/skills/ai-research/13-mlops/tensorboard/references/profiling.md +546 -0
  335. package/skills/ai-research/13-mlops/tensorboard/references/visualization.md +621 -0
  336. package/skills/ai-research/13-mlops/weights-and-biases/SKILL.md +590 -0
  337. package/skills/ai-research/13-mlops/weights-and-biases/references/artifacts.md +585 -0
  338. package/skills/ai-research/13-mlops/weights-and-biases/references/integrations.md +701 -0
  339. package/skills/ai-research/13-mlops/weights-and-biases/references/sweeps.md +848 -0
  340. package/skills/ai-research/14-agents/a-evolve/SKILL.md +383 -0
  341. package/skills/ai-research/14-agents/a-evolve/references/README.md +831 -0
  342. package/skills/ai-research/14-agents/a-evolve/references/api.md +594 -0
  343. package/skills/ai-research/14-agents/a-evolve/references/architecture.md +417 -0
  344. package/skills/ai-research/14-agents/a-evolve/references/design-patterns.md +462 -0
  345. package/skills/ai-research/14-agents/a-evolve/references/examples.md +352 -0
  346. package/skills/ai-research/14-agents/a-evolve/references/issues.md +591 -0
  347. package/skills/ai-research/14-agents/a-evolve/references/releases.md +112 -0
  348. package/skills/ai-research/14-agents/a-evolve/references/tutorials.md +819 -0
  349. package/skills/ai-research/14-agents/autogpt/SKILL.md +403 -0
  350. package/skills/ai-research/14-agents/autogpt/references/advanced-usage.md +536 -0
  351. package/skills/ai-research/14-agents/autogpt/references/troubleshooting.md +421 -0
  352. package/skills/ai-research/14-agents/crewai/SKILL.md +498 -0
  353. package/skills/ai-research/14-agents/crewai/references/flows.md +439 -0
  354. package/skills/ai-research/14-agents/crewai/references/tools.md +430 -0
  355. package/skills/ai-research/14-agents/crewai/references/troubleshooting.md +481 -0
  356. package/skills/ai-research/14-agents/langchain/SKILL.md +480 -0
  357. package/skills/ai-research/14-agents/langchain/references/agents.md +500 -0
  358. package/skills/ai-research/14-agents/langchain/references/integration.md +563 -0
  359. package/skills/ai-research/14-agents/langchain/references/rag.md +601 -0
  360. package/skills/ai-research/14-agents/llamaindex/SKILL.md +569 -0
  361. package/skills/ai-research/14-agents/llamaindex/references/agents.md +84 -0
  362. package/skills/ai-research/14-agents/llamaindex/references/data_connectors.md +109 -0
  363. package/skills/ai-research/14-agents/llamaindex/references/query_engines.md +407 -0
  364. package/skills/ai-research/15-rag/chroma/SKILL.md +406 -0
  365. package/skills/ai-research/15-rag/chroma/references/integration.md +39 -0
  366. package/skills/ai-research/15-rag/faiss/SKILL.md +221 -0
  367. package/skills/ai-research/15-rag/faiss/references/index_types.md +281 -0
  368. package/skills/ai-research/15-rag/pinecone/SKILL.md +358 -0
  369. package/skills/ai-research/15-rag/pinecone/references/deployment.md +182 -0
  370. package/skills/ai-research/15-rag/qdrant/SKILL.md +493 -0
  371. package/skills/ai-research/15-rag/qdrant/references/advanced-usage.md +649 -0
  372. package/skills/ai-research/15-rag/qdrant/references/troubleshooting.md +632 -0
  373. package/skills/ai-research/15-rag/sentence-transformers/SKILL.md +255 -0
  374. package/skills/ai-research/15-rag/sentence-transformers/references/models.md +124 -0
  375. package/skills/ai-research/16-prompt-engineering/dspy/SKILL.md +590 -0
  376. package/skills/ai-research/16-prompt-engineering/dspy/references/examples.md +664 -0
  377. package/skills/ai-research/16-prompt-engineering/dspy/references/modules.md +476 -0
  378. package/skills/ai-research/16-prompt-engineering/dspy/references/optimizers.md +567 -0
  379. package/skills/ai-research/16-prompt-engineering/guidance/SKILL.md +572 -0
  380. package/skills/ai-research/16-prompt-engineering/guidance/references/backends.md +555 -0
  381. package/skills/ai-research/16-prompt-engineering/guidance/references/constraints.md +675 -0
  382. package/skills/ai-research/16-prompt-engineering/guidance/references/examples.md +768 -0
  383. package/skills/ai-research/16-prompt-engineering/instructor/SKILL.md +740 -0
  384. package/skills/ai-research/16-prompt-engineering/instructor/references/examples.md +108 -0
  385. package/skills/ai-research/16-prompt-engineering/instructor/references/providers.md +71 -0
  386. package/skills/ai-research/16-prompt-engineering/instructor/references/validation.md +607 -0
  387. package/skills/ai-research/16-prompt-engineering/outlines/SKILL.md +652 -0
  388. package/skills/ai-research/16-prompt-engineering/outlines/references/backends.md +616 -0
  389. package/skills/ai-research/16-prompt-engineering/outlines/references/examples.md +774 -0
  390. package/skills/ai-research/16-prompt-engineering/outlines/references/json_generation.md +653 -0
  391. package/skills/ai-research/17-observability/langsmith/SKILL.md +422 -0
  392. package/skills/ai-research/17-observability/langsmith/references/advanced-usage.md +549 -0
  393. package/skills/ai-research/17-observability/langsmith/references/troubleshooting.md +538 -0
  394. package/skills/ai-research/17-observability/phoenix/SKILL.md +475 -0
  395. package/skills/ai-research/17-observability/phoenix/references/advanced-usage.md +620 -0
  396. package/skills/ai-research/17-observability/phoenix/references/troubleshooting.md +539 -0
  397. package/skills/ai-research/18-multimodal/audiocraft/SKILL.md +564 -0
  398. package/skills/ai-research/18-multimodal/audiocraft/references/advanced-usage.md +667 -0
  399. package/skills/ai-research/18-multimodal/audiocraft/references/troubleshooting.md +505 -0
  400. package/skills/ai-research/18-multimodal/blip-2/SKILL.md +564 -0
  401. package/skills/ai-research/18-multimodal/blip-2/references/advanced-usage.md +681 -0
  402. package/skills/ai-research/18-multimodal/blip-2/references/troubleshooting.md +527 -0
  403. package/skills/ai-research/18-multimodal/clip/SKILL.md +253 -0
  404. package/skills/ai-research/18-multimodal/clip/references/applications.md +208 -0
  405. package/skills/ai-research/18-multimodal/cosmos-policy/SKILL.md +402 -0
  406. package/skills/ai-research/18-multimodal/cosmos-policy/references/libero-commands.md +175 -0
  407. package/skills/ai-research/18-multimodal/cosmos-policy/references/robocasa-commands.md +147 -0
  408. package/skills/ai-research/18-multimodal/llava/SKILL.md +304 -0
  409. package/skills/ai-research/18-multimodal/llava/references/training.md +198 -0
  410. package/skills/ai-research/18-multimodal/openpi/SKILL.md +480 -0
  411. package/skills/ai-research/18-multimodal/openpi/references/checkpoints-and-env-map.md +86 -0
  412. package/skills/ai-research/18-multimodal/openpi/references/config-recipes.md +50 -0
  413. package/skills/ai-research/18-multimodal/openpi/references/pytorch-gotchas.md +49 -0
  414. package/skills/ai-research/18-multimodal/openpi/references/remote-client-pattern.md +48 -0
  415. package/skills/ai-research/18-multimodal/openpi/references/training-debugging.md +46 -0
  416. package/skills/ai-research/18-multimodal/openvla-oft/SKILL.md +441 -0
  417. package/skills/ai-research/18-multimodal/openvla-oft/references/aloha-workflow.md +141 -0
  418. package/skills/ai-research/18-multimodal/openvla-oft/references/config-troubleshooting.md +97 -0
  419. package/skills/ai-research/18-multimodal/openvla-oft/references/libero-workflow.md +115 -0
  420. package/skills/ai-research/18-multimodal/openvla-oft/references/paper-and-checkpoints.md +84 -0
  421. package/skills/ai-research/18-multimodal/segment-anything/SKILL.md +500 -0
  422. package/skills/ai-research/18-multimodal/segment-anything/references/advanced-usage.md +590 -0
  423. package/skills/ai-research/18-multimodal/segment-anything/references/troubleshooting.md +485 -0
  424. package/skills/ai-research/18-multimodal/stable-diffusion/SKILL.md +519 -0
  425. package/skills/ai-research/18-multimodal/stable-diffusion/references/advanced-usage.md +717 -0
  426. package/skills/ai-research/18-multimodal/stable-diffusion/references/troubleshooting.md +556 -0
  427. package/skills/ai-research/18-multimodal/whisper/SKILL.md +317 -0
  428. package/skills/ai-research/18-multimodal/whisper/references/languages.md +190 -0
  429. package/skills/ai-research/19-emerging-techniques/knowledge-distillation/SKILL.md +458 -0
  430. package/skills/ai-research/19-emerging-techniques/knowledge-distillation/references/minillm.md +335 -0
  431. package/skills/ai-research/19-emerging-techniques/long-context/SKILL.md +536 -0
  432. package/skills/ai-research/19-emerging-techniques/long-context/references/extension_methods.md +469 -0
  433. package/skills/ai-research/19-emerging-techniques/long-context/references/fine_tuning.md +612 -0
  434. package/skills/ai-research/19-emerging-techniques/long-context/references/rope.md +403 -0
  435. package/skills/ai-research/19-emerging-techniques/model-merging/SKILL.md +497 -0
  436. package/skills/ai-research/19-emerging-techniques/model-merging/references/coefficient-tuning.md +415 -0
  437. package/skills/ai-research/19-emerging-techniques/model-merging/references/evaluation.md +463 -0
  438. package/skills/ai-research/19-emerging-techniques/model-merging/references/examples.md +429 -0
  439. package/skills/ai-research/19-emerging-techniques/model-merging/references/methods.md +353 -0
  440. package/skills/ai-research/19-emerging-techniques/model-pruning/SKILL.md +495 -0
  441. package/skills/ai-research/19-emerging-techniques/model-pruning/references/wanda.md +348 -0
  442. package/skills/ai-research/19-emerging-techniques/moe-training/SKILL.md +526 -0
  443. package/skills/ai-research/19-emerging-techniques/moe-training/references/architectures.md +433 -0
  444. package/skills/ai-research/19-emerging-techniques/moe-training/references/inference.md +349 -0
  445. package/skills/ai-research/19-emerging-techniques/moe-training/references/training.md +426 -0
  446. package/skills/ai-research/19-emerging-techniques/speculative-decoding/SKILL.md +467 -0
  447. package/skills/ai-research/19-emerging-techniques/speculative-decoding/references/lookahead.md +310 -0
  448. package/skills/ai-research/19-emerging-techniques/speculative-decoding/references/medusa.md +351 -0
  449. package/skills/ai-research/20-ml-paper-writing/academic-plotting/SKILL.md +479 -0
  450. package/skills/ai-research/20-ml-paper-writing/academic-plotting/references/data-visualization.md +509 -0
  451. package/skills/ai-research/20-ml-paper-writing/academic-plotting/references/diagram-generation.md +395 -0
  452. package/skills/ai-research/20-ml-paper-writing/academic-plotting/references/style-guide.md +305 -0
  453. package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/SKILL.md +982 -0
  454. package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/checklists.md +348 -0
  455. package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/citation-workflow.md +563 -0
  456. package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/reviewer-guidelines.md +349 -0
  457. package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/sources.md +165 -0
  458. package/skills/ai-research/20-ml-paper-writing/ml-paper-writing/references/writing-guide.md +477 -0
  459. package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/SKILL.md +298 -0
  460. package/skills/ai-research/20-ml-paper-writing/presenting-conference-talks/references/slide-templates.md +505 -0
  461. package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/SKILL.md +270 -0
  462. package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/checklist.md +279 -0
  463. package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/reviewer-guidelines.md +116 -0
  464. package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/section-blueprints.md +400 -0
  465. package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/systems-conferences.md +261 -0
  466. package/skills/ai-research/20-ml-paper-writing/systems-paper-writing/references/writing-patterns.md +231 -0
  467. package/skills/ai-research/21-research-ideation/brainstorming-research-ideas/SKILL.md +384 -0
  468. package/skills/ai-research/21-research-ideation/creative-thinking-for-research/SKILL.md +366 -0
  469. package/skills/ai-research/22-agent-native-research-artifact/compiler/SKILL.md +245 -0
  470. package/skills/ai-research/22-agent-native-research-artifact/compiler/references/ara-schema.md +439 -0
  471. package/skills/ai-research/22-agent-native-research-artifact/compiler/references/exploration-tree-spec.md +125 -0
  472. package/skills/ai-research/22-agent-native-research-artifact/compiler/references/validation-checklist.md +149 -0
  473. package/skills/ai-research/22-agent-native-research-artifact/research-manager/SKILL.md +324 -0
  474. package/skills/ai-research/22-agent-native-research-artifact/research-manager/references/event-taxonomy.md +124 -0
  475. package/skills/ai-research/22-agent-native-research-artifact/research-manager/references/provenance-tags.md +190 -0
  476. package/skills/ai-research/22-agent-native-research-artifact/research-manager/references/session-protocol.md +162 -0
  477. package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/SKILL.md +322 -0
  478. package/skills/ai-research/22-agent-native-research-artifact/rigor-reviewer/references/review-dimensions.md +182 -0
  479. package/skills/ai-research/SKILL.md +101 -0
  480. package/skills/ai-research/SOURCE +38 -0
  481. package/skills/ai-research/index/1-frame.md +18 -0
  482. package/skills/ai-research/index/2-data.md +21 -0
  483. package/skills/ai-research/index/3-train.md +48 -0
  484. package/skills/ai-research/index/4-measure.md +25 -0
  485. package/skills/ai-research/index/5-run.md +23 -0
  486. package/skills/ai-research/index/6-write.md +35 -0
  487. package/skills/ai-research/library.json +3456 -0
  488. package/skills/analyze-app/EVALS.md +82 -0
  489. package/skills/analyze-app/SKILL.md +188 -0
  490. package/skills/design-contract/EVALS.md +80 -0
  491. package/skills/design-contract/SKILL.md +171 -0
  492. package/skills/design-library/EVALS.md +85 -0
  493. package/skills/design-library/INDEX.md +59 -0
  494. package/skills/design-library/SKILL.md +58 -0
  495. package/skills/design-library/SOURCE +131 -0
  496. package/skills/design-library/adaptive-interfaces/skills/colour-independence/SKILL.md +90 -0
  497. package/skills/design-library/adaptive-interfaces/skills/flexible-typography/SKILL.md +83 -0
  498. package/skills/design-library/adaptive-interfaces/skills/information-density/SKILL.md +87 -0
  499. package/skills/design-library/adaptive-interfaces/skills/preference-audit/SKILL.md +70 -0
  500. package/skills/design-library/adaptive-interfaces/skills/responsive-accessibility/SKILL.md +86 -0
  501. package/skills/design-library/adaptive-interfaces/skills/responsive-review/SKILL.md +76 -0
  502. package/skills/design-library/adaptive-interfaces/skills/simplified-views/SKILL.md +97 -0
  503. package/skills/design-library/adaptive-interfaces/skills/specify/SKILL.md +63 -0
  504. package/skills/design-library/adaptive-interfaces/skills/user-preference-respect/SKILL.md +100 -0
  505. package/skills/design-library/cognitive-accessibility/skills/adaptive-personalisation/SKILL.md +61 -0
  506. package/skills/design-library/cognitive-accessibility/skills/assess-load/SKILL.md +48 -0
  507. package/skills/design-library/cognitive-accessibility/skills/cognitive-load-assessment/SKILL.md +60 -0
  508. package/skills/design-library/cognitive-accessibility/skills/contextual-help-design/SKILL.md +61 -0
  509. package/skills/design-library/cognitive-accessibility/skills/error-prevention-recovery/SKILL.md +71 -0
  510. package/skills/design-library/cognitive-accessibility/skills/focus-attention-design/SKILL.md +67 -0
  511. package/skills/design-library/cognitive-accessibility/skills/memory-load-reduction/SKILL.md +60 -0
  512. package/skills/design-library/cognitive-accessibility/skills/plain-language-design/SKILL.md +78 -0
  513. package/skills/design-library/cognitive-accessibility/skills/review/SKILL.md +56 -0
  514. package/skills/design-library/cognitive-accessibility/skills/simplify/SKILL.md +39 -0
  515. package/skills/design-library/cognitive-accessibility/skills/wayfinding-navigation/SKILL.md +64 -0
  516. package/skills/design-library/design-research/skills/behavioural-analytics/SKILL.md +33 -0
  517. package/skills/design-library/design-research/skills/qual-quant-triangulation/SKILL.md +33 -0
  518. package/skills/design-library/inclusive-interaction/skills/audit/SKILL.md +50 -0
  519. package/skills/design-library/inclusive-interaction/skills/design-flow/SKILL.md +58 -0
  520. package/skills/design-library/inclusive-interaction/skills/feedback-and-status/SKILL.md +76 -0
  521. package/skills/design-library/inclusive-interaction/skills/gesture-alternatives/SKILL.md +70 -0
  522. package/skills/design-library/inclusive-interaction/skills/keyboard-navigation/SKILL.md +99 -0
  523. package/skills/design-library/inclusive-interaction/skills/keyboard-review/SKILL.md +54 -0
  524. package/skills/design-library/inclusive-interaction/skills/motion-sensitivity/SKILL.md +79 -0
  525. package/skills/design-library/inclusive-interaction/skills/multi-modal-input/SKILL.md +70 -0
  526. package/skills/design-library/inclusive-interaction/skills/touch-target-design/SKILL.md +71 -0
  527. package/skills/design-library/inclusive-interaction/skills/voice-interaction/SKILL.md +68 -0
  528. package/skills/design-library/visual-critique/skills/critique-affordance/SKILL.md +45 -0
  529. package/skills/design-library/visual-critique/skills/critique-brand-consistency/SKILL.md +42 -0
  530. package/skills/design-library/visual-critique/skills/critique-color/SKILL.md +44 -0
  531. package/skills/design-library/visual-critique/skills/critique-composition/SKILL.md +46 -0
  532. package/skills/design-library/visual-critique/skills/critique-information-density/SKILL.md +45 -0
  533. package/skills/design-library/visual-critique/skills/critique-typography/SKILL.md +45 -0
  534. package/skills/design-library/visual-critique/skills/critique-visual-hierarchy/SKILL.md +40 -0
  535. package/skills/feature-audit/EVALS.md +83 -0
  536. package/skills/feature-audit/SKILL.md +326 -0
  537. package/skills/harness/EVALS.md +81 -0
  538. package/skills/harness/SKILL.md +88 -0
  539. package/skills/i-have-adhd/EVALS.md +80 -0
  540. package/skills/i-have-adhd/SKILL.md +183 -0
  541. package/skills/no-ai-slop/EVALS.md +89 -0
  542. package/skills/no-ai-slop/SKILL.md +209 -0
  543. package/skills/no-ai-slop/eval.md +52 -0
  544. package/skills/plan-add/EVALS.md +79 -0
  545. package/skills/plan-add/SKILL.md +179 -0
  546. package/skills/plan-add/render_table.py +72 -0
  547. package/skills/plan-status/EVALS.md +78 -0
  548. package/skills/plan-status/SKILL.md +52 -0
  549. package/skills/plan-sync/EVALS.md +80 -0
  550. package/skills/plan-sync/SKILL.md +69 -0
  551. package/skills/pm-frameworks/SKILL.md +33 -0
  552. package/skills/pm-frameworks/SOURCE +39 -0
  553. package/skills/pm-frameworks/intended-vs-implemented/SKILL.md +42 -0
  554. package/skills/pm-frameworks/opportunity-solution-tree/SKILL.md +64 -0
  555. package/skills/ponytail/EVALS.md +80 -0
  556. package/skills/ponytail/SKILL.md +124 -0
  557. package/skills/product-analysis/EVALS.md +103 -0
  558. package/skills/product-analysis/SKILL.md +361 -0
  559. package/skills/research/EVALS.md +104 -0
  560. package/skills/research/SKILL.md +570 -0
  561. package/skills/rl-env/SKILL.md +53 -0
  562. package/skills/rl-env/SOURCE +41 -0
  563. package/skills/rl-env/generate-nemo-gym-env/SKILL.md +169 -0
  564. package/skills/rl-env/generate-nemo-gym-env/references/architecture.md +114 -0
  565. package/skills/rl-env/generate-openenv-env/SKILL.md +183 -0
  566. package/skills/rl-env/generate-openenv-env/references/architecture.md +82 -0
  567. package/skills/rl-env/generate-ors-env/SKILL.md +209 -0
  568. package/skills/rl-env/generate-ors-env/references/architecture.md +113 -0
  569. package/skills/rl-env/generate-verifiers-env/SKILL.md +188 -0
  570. package/skills/rl-env/generate-verifiers-env/references/architecture.md +119 -0
  571. package/skills/rl-env/rl-env-from-description/SKILL.md +124 -0
  572. package/skills/rl-env/rl-env-from-description/references/interview.md +75 -0
  573. package/skills/rl-env/rl-env-from-description/references/nemo_gym.md +41 -0
  574. package/skills/rl-env/rl-env-from-description/references/openenv.md +35 -0
  575. package/skills/rl-env/rl-env-from-description/references/ors.md +50 -0
  576. package/skills/rl-env/rl-env-from-description/references/verifiers.md +39 -0
  577. package/skills/tezgah-contract/EVALS.md +78 -0
  578. package/skills/tezgah-contract/SKILL.md +792 -0
  579. package/statusline.py +129 -0
  580. package/workflows/graph-impact.js +153 -0
  581. package/workflows/graph-map.js +158 -0
  582. package/workflows/graph-review.js +170 -0
@@ -0,0 +1,1199 @@
1
+ #!/usr/bin/env python3
2
+ """tezgah-docs [QUERY] - reach the docs/ page that answers a question.
3
+
4
+ Reads docs/index.json and prints the matching page paths, so an agent picks a
5
+ page instead of guessing and a script can assert the layer is intact.
6
+
7
+ tezgah-docs every page, one line each
8
+ tezgah-docs <words...> the pages matching every word of the query
9
+ tezgah-docs --json [QUERY] the matching entries as JSON
10
+ tezgah-docs --read QUERY print the first matching page in full
11
+ tezgah-docs --citations the citations that no longer show what they name,
12
+ the files with more unjudged citations than
13
+ docs/citations-baseline.json, the rule ledger's rows
14
+ and the code-derived inventories that no longer hold
15
+ (`--citations --update` rewrites the baseline)
16
+ tezgah-docs --clarity the clarity ratchet: per-file prose rule counts
17
+ against docs/clarity-baseline.json; 1 when a file
18
+ exceeds it (`--clarity --update` rewrites it)
19
+
20
+ The options above are options anywhere before `--`. An unknown `--word` is a
21
+ usage error before the first query word and a query word after it, so
22
+ `tezgah-docs why was --no-verify blocked --json` is a JSON query; after `--`
23
+ every word is part of the query.
24
+
25
+ Matching is word-based and case-insensitive against an entry's title, its
26
+ questions, its sources and its path; every word must match, so a second word
27
+ narrows. A query that matches nothing is the last resort of one TypeSafe Choice
28
+ question over the pages themselves; it answers `none` for a query no page
29
+ covers. When that question gives no judgement - unavailable (`docs-judge-off`,
30
+ the seam's `judge-off`, no credential) or a call that failed or came back
31
+ unreadable - the pages are ranked by shared words instead (BM25,
32
+ `hooks/tezgah_rank.py`) and the best few printed; a judged `none` is not
33
+ overruled. Exit codes: 0 with matches, 1 with none, 2 usage.
34
+ """
35
+ import ast
36
+ import bisect
37
+ import glob
38
+ import io
39
+ import json
40
+ import os
41
+ import re
42
+ import sys
43
+ import tokenize
44
+
45
+ HERE = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
46
+ sys.path.insert(0, os.path.join(HERE, "hooks"))
47
+ import tezgah_integrity as ti # noqa: E402
48
+ import tezgah_judge # noqa: E402
49
+ import tezgah_paths as tp # noqa: E402
50
+ import tezgah_embed # noqa: E402
51
+ import tezgah_clarity # noqa: E402
52
+
53
+ INDEX = os.path.join(HERE, "docs", "index.json")
54
+
55
+
56
+ def entries():
57
+ """The index entries, or [] when the index is missing or malformed."""
58
+ try:
59
+ with open(INDEX, encoding="utf-8") as fh:
60
+ data = json.load(fh)
61
+ except (OSError, ValueError):
62
+ return []
63
+ pages = data.get("pages")
64
+ if not isinstance(pages, list):
65
+ return []
66
+ return [p for p in pages if isinstance(p, dict) and p.get("path")]
67
+
68
+
69
+ def blob(entry):
70
+ """Everything a query is matched against, lowercased."""
71
+ parts = [str(entry.get("path") or ""), str(entry.get("title") or "")]
72
+ parts += [str(a) for a in (entry.get("answers") or [])]
73
+ parts += [str(s) for s in (entry.get("sources") or [])]
74
+ return " ".join(parts).lower()
75
+
76
+
77
+ def matches(query):
78
+ """The entries every word of `query` appears in; [] for an empty query."""
79
+ words = query.lower().split()
80
+ if not words:
81
+ return []
82
+ return [e for e in entries() if all(w in blob(e) for w in words)]
83
+
84
+
85
+ # --- the citation audit -----------------------------------------------------
86
+ # A page's `path:line` is a promise a reader can open. `tests/test_docs.py`
87
+ # checks the half a script can (the file exists, the line is inside it) and says
88
+ # why it stops there: whether the line still shows the thing the sentence names
89
+ # is a judgement about prose, and a test that guessed at it would fail on
90
+ # rewording rather than on drift. That judgement is mechanical in one case - a
91
+ # citation that names a symbol whose body the range must lie in - and that case
92
+ # is where the drift actually lands, because the numbers were written against a
93
+ # revision of the file and nothing regenerates them when lines move under them.
94
+ # A gate, not only a report: CI runs `bin/tezgah-docs --citations` and `main()`
95
+ # exits 1 on a citation outside its symbol, on a file with more unjudged
96
+ # citations than `docs/citations-baseline.json` allows, and on a ledger or
97
+ # inventory row that does not hold. It reads docs/*.md and the comments and
98
+ # docstrings of the Python code files; a string literal (a test's fixture, a
99
+ # captured output) is data, not a citation, and is not read.
100
+
101
+ CODE_FILES = ("hooks/*.py", "bin/*", "statusline.py", "hosts/*/*.py",
102
+ "hosts/*/*.ts.in", "hosts/*/plugins/*.js", "tests/*.py")
103
+ STRING = re.compile(r'"""(?:.|\n)*?"""|\'\'\'(?:.|\n)*?\'\'\'')
104
+ TOP = re.compile(r"^(?:def|class)\s+(\w+)|^([A-Za-z_][A-Za-z0-9_]*)\s*(?::[^=]+)?=")
105
+ RUN = re.compile(r"((?:`[A-Za-z_][A-Za-z0-9_]*(?:\(\))?`\s*[/,]?\s*){1,5})$")
106
+ CITE = re.compile(r"`([\w.-]+(?:/[\w.-]+)+|[\w-]+\.\w+):(\d+)(?:-(\d+))?`"
107
+ r"|`(:(\d+)(?:-(\d+))?)`")
108
+ # The symbol written after the citation instead of before it, the way a table
109
+ # cell reads: `hooks/tezgah_policy.py:634-853` (`CORE`).
110
+ AFTER = re.compile(r"\s*\(`([A-Za-z_][A-Za-z0-9_]*)(?:\(\))?`\)")
111
+ # Any `path:line`, backticked or not: what CITE does not match of these (a
112
+ # bare token, a comma list `x.py:12,40`) is counted as unjudged, so a citation
113
+ # written in a shape the audit cannot read is still a number someone sees.
114
+ # Scanned as one token run and then shape-checked: the shape alternation as
115
+ # the scanner (nested `+` under a look-behind) cost a third of a second a pass.
116
+ LOOSE = re.compile(r"(?<![\w./:-])([\w./-]*\w):(\d+)(?:-(\d+))?(?!\w)")
117
+ LOOSE_SHAPE = re.compile(r"(?:[\w.-]+/)+[\w.-]*\w|[\w-]+\.\w+")
118
+
119
+
120
+ def loose(text):
121
+ """The LOOSE matches whose path has a file's shape (a `/`, or `name.ext`)."""
122
+ return (m for m in LOOSE.finditer(text) if LOOSE_SHAPE.fullmatch(m.group(1)))
123
+
124
+
125
+ # A symbol anchor, `path::name` or `path::Class.method`: no line number, so an
126
+ # edit above or inside the symbol cannot move it. What can still go stale is the
127
+ # name, and the AST of the cited file answers that: a name it does not define
128
+ # is flagged like a line outside its symbol.
129
+ SYMCITE = re.compile(r"`([\w.-]+(?:/[\w.-]+)+|[\w-]+\.\w+)::"
130
+ r"([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)`")
131
+
132
+
133
+ def qualnames(path):
134
+ """{dotted name: (start, end, method)} for every def and class of a Python
135
+ file, nested ones as `Class.method` / `outer.inner`, and its module-level
136
+ assignments; `method` is True for a def whose direct parent is a class.
137
+ None for a file that does not parse as Python."""
138
+ try:
139
+ tree = ast.parse(source(path))
140
+ except (SyntaxError, ValueError, UnicodeDecodeError):
141
+ return None
142
+ out = {}
143
+
144
+ def walk(body, prefix, in_class=False):
145
+ for node in body:
146
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
147
+ name = prefix + node.name
148
+ start = min([node.lineno] + [d.lineno for d in node.decorator_list])
149
+ out.setdefault(name, (start, node.end_lineno,
150
+ in_class and not isinstance(node, ast.ClassDef)))
151
+ walk(node.body, name + ".", isinstance(node, ast.ClassDef))
152
+ continue
153
+ if not prefix and isinstance(node, (ast.Assign, ast.AnnAssign)):
154
+ for target in getattr(node, "targets", None) or [node.target]:
155
+ if isinstance(target, ast.Name):
156
+ out.setdefault(target.id, (node.lineno, node.end_lineno, False))
157
+ for field in ("body", "orelse", "finalbody"):
158
+ walk(getattr(node, field, None) or [], prefix, in_class)
159
+ for handler in getattr(node, "handlers", None) or []:
160
+ walk(handler.body, prefix, in_class)
161
+
162
+ walk(tree.body, "")
163
+ return out
164
+
165
+
166
+ def anchor_span(names, qual):
167
+ """The (start, end, method) `qual` names in a qualnames() table, or None. A
168
+ name without a dot may also be the one method of that name, the way a test
169
+ pin names `test_x` inside its TestCase class; a def local to a function
170
+ never stands in, or a deleted top-level symbol would pass on a helper."""
171
+ if qual in names:
172
+ return names[qual]
173
+ if "." not in qual:
174
+ hits = [span for name, span in names.items()
175
+ if span[2] and name.rsplit(".", 1)[-1] == qual]
176
+ if len(hits) == 1:
177
+ return hits[0]
178
+ return None
179
+
180
+
181
+ CITATION_BASELINE = os.path.join("docs", "citations-baseline.json")
182
+
183
+
184
+ def code_symbols(path):
185
+ """{name: [(start, end)]} for this file's top-level defs, classes and
186
+ assignments. A triple-quoted body is masked first, so a line inside a
187
+ contract string cannot read as a definition and cut another one short."""
188
+ text = open(path, encoding="utf-8", errors="replace").read()
189
+ chars = list(text)
190
+ for m in STRING.finditer(text):
191
+ for i in range(m.start(), m.end()):
192
+ if chars[i] != "\n":
193
+ chars[i] = " "
194
+ lines = "".join(chars).splitlines()
195
+ starts = [(i, m.group(1) or m.group(2))
196
+ for i, line in enumerate(lines, 1) if (m := TOP.match(line))]
197
+ out = {}
198
+ for k, (start, name) in enumerate(starts):
199
+ end = starts[k + 1][0] - 1 if k + 1 < len(starts) else len(lines)
200
+ out.setdefault(name, []).append((start, end))
201
+ return out
202
+
203
+
204
+ def code_files():
205
+ """Every file CODE_FILES globs, relative to HERE, symlinks left out: every
206
+ bin/ script has a `.py` twin pointing at it so tests can import it, and
207
+ counting both made every bin symbol "ambiguous" - the audit (M-11a)
208
+ measured 0 stale citations reported while 25 were."""
209
+ out = []
210
+ for pattern in CODE_FILES:
211
+ for path in sorted(glob.glob(os.path.join(HERE, pattern))):
212
+ if os.path.isfile(path) and not os.path.islink(path):
213
+ out.append(os.path.relpath(path, HERE))
214
+ return out
215
+
216
+
217
+ def symbols():
218
+ """{name: (path, [(start, end)])} over every code file, for the names
219
+ exactly one file defines: an ambiguous name is skipped, not guessed at."""
220
+ seen = {}
221
+ for rel in code_files():
222
+ for name, spans in code_symbols(os.path.join(HERE, rel)).items():
223
+ seen.setdefault(name, []).append((rel, spans))
224
+ return {name: only[0] for name, only in seen.items() if len(only) == 1}
225
+
226
+
227
+ def is_python(path):
228
+ """True for a `.py` file or a script whose first line names python."""
229
+ if path.endswith(".py"):
230
+ return True
231
+ with open(path, encoding="utf-8", errors="replace") as fh:
232
+ return "python" in fh.readline()
233
+
234
+
235
+ def comment_text(path):
236
+ """A Python file's comments and docstrings at their own offsets, every other
237
+ non-blank character turned into NUL, or None for a file that is not Python.
238
+
239
+ The offsets are kept so a match's line is the file's line, and the code is
240
+ NUL rather than blank so a symbol in one comment block never pairs with a
241
+ citation in the next one across the code between them. A docstring is a
242
+ string that is a statement of its own; a string inside an expression is a
243
+ value (a fixture, a captured output) and is blanked with the code. A file
244
+ holding no citation-shaped token at all is "": no comment of it could hold
245
+ one either, so it is neither tokenized nor masked."""
246
+ if not is_python(path):
247
+ return None
248
+ text = source(path)
249
+ if not (CITE.search(text) or SYMCITE.search(text) or next(loose(text), None)):
250
+ return ""
251
+ masked = re.sub(r"\S", "\0", text)
252
+ starts = [0]
253
+ for line in text.splitlines(keepends=True):
254
+ starts.append(starts[-1] + len(line))
255
+ try:
256
+ tokens = list(tokenize.generate_tokens(io.StringIO(text).readline))
257
+ except (tokenize.TokenError, SyntaxError):
258
+ return None
259
+
260
+ def span(tok, skip=0):
261
+ return (starts[tok.start[0] - 1] + tok.start[1] + skip,
262
+ starts[tok.end[0] - 1] + tok.end[1])
263
+
264
+ lead = (tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT, tokenize.ENCODING)
265
+ real = [t for t in tokens if t.type not in (tokenize.NL, tokenize.COMMENT)]
266
+ kept = [span(t, 1) for t in tokens if t.type == tokenize.COMMENT]
267
+ kept += [span(t) for k, t in enumerate(real)
268
+ if t.type == tokenize.STRING and (k == 0 or real[k - 1].type in lead)
269
+ and k + 1 < len(real)
270
+ and real[k + 1].type in (tokenize.NEWLINE, tokenize.ENDMARKER)]
271
+ parts, at = [], 0
272
+ for lo, hi in sorted(kept):
273
+ parts += [masked[at:lo], text[lo:hi]]
274
+ at = hi
275
+ return "".join(parts) + masked[at:]
276
+
277
+
278
+ def sources():
279
+ """(relpath, text) for every page under docs/ and every Python code file's
280
+ comments and docstrings (`comment_text`)."""
281
+ for path in sorted(glob.glob(os.path.join(HERE, "docs", "*.md"))):
282
+ yield os.path.relpath(path, HERE), source(path)
283
+ for rel in code_files():
284
+ text = comment_text(os.path.join(HERE, rel))
285
+ if text is not None:
286
+ yield rel, text
287
+
288
+
289
+ def citations(known):
290
+ """(flagged, judged, unjudged, invisible): the citations whose named symbol
291
+ does not contain the cited range, as (`file:line`, token, symbol file, low,
292
+ high), and the symbol anchors naming no symbol, as (`file:line`, token,
293
+ cited file, 0, 0); how many were judgeable; {file: count} of the ones that
294
+ were not; and how many of those CITE could not read at all (`LOOSE`).
295
+
296
+ A citation that writes its own path is judged against the symbol in that
297
+ file alone: a sentence naming `note_tool` beside `hooks/x.py:12` means the
298
+ `note_tool` in `hooks/x.py`, so a same-named symbol elsewhere is not the
299
+ thing being cited and the row is left unjudged rather than misjudged. A bare
300
+ file name (`tezgah_gate.py:12`) is that path when exactly one code file has
301
+ the name, and unjudged otherwise. A symbol anchor (`SYMCITE`) is always
302
+ judged: against the qualnames() of the file it names."""
303
+ names = {}
304
+ for rel in code_files():
305
+ names.setdefault(os.path.basename(rel), []).append(rel)
306
+
307
+ def resolve(path):
308
+ if "/" in path:
309
+ return path
310
+ found = names.get(path, ())
311
+ return found[0] if len(found) == 1 else None
312
+
313
+ flagged, judged, unjudged, invisible, tables = [], 0, {}, 0, {}
314
+ for rel, text in sources():
315
+ read = []
316
+ for m in CITE.finditer(text):
317
+ read.append(m.span())
318
+ # RUN is anchored at the end and five names fit in far fewer than
319
+ # 600 characters: slicing the whole page per citation was the cost.
320
+ pre = text[max(0, m.start() - 600):m.start()].rstrip().rstrip("(") \
321
+ .rstrip().rstrip(",")
322
+ run = RUN.search(pre.rstrip()) if pre.rstrip().endswith("`") else None
323
+ named = re.findall(r"`([A-Za-z_][A-Za-z0-9_]*)(?:\(\))?`",
324
+ run.group(1)) if run else []
325
+ after = AFTER.match(text, m.end())
326
+ if not named and after:
327
+ named = [after.group(1)]
328
+ cited = resolve(m.group(1)) if m.group(1) else None
329
+ spans, where = [], None
330
+ for name in named:
331
+ hit = known.get(name)
332
+ if not hit or (m.group(1) and hit[0] != cited):
333
+ continue
334
+ where, spans = hit[0], spans + hit[1]
335
+ if not spans:
336
+ unjudged[rel] = unjudged.get(rel, 0) + 1
337
+ continue
338
+ lo = int(m.group(2) or m.group(5))
339
+ hi = int(m.group(3) or m.group(6) or lo)
340
+ low, high = min(s for s, _ in spans), max(e for _, e in spans)
341
+ judged += 1
342
+ if lo >= low and hi <= high:
343
+ continue
344
+ line = text.count("\n", 0, m.start()) + 1
345
+ flagged.append(("%s:%d" % (rel, line), m.group(0), where, low, high))
346
+ for m in SYMCITE.finditer(text):
347
+ judged += 1
348
+ path = resolve(m.group(1))
349
+ full = os.path.join(HERE, path) if path else None
350
+ if full and os.path.isfile(full):
351
+ if path not in tables:
352
+ tables[path] = qualnames(full) or {}
353
+ if anchor_span(tables[path], m.group(2)):
354
+ continue
355
+ line = text.count("\n", 0, m.start()) + 1
356
+ flagged.append(("%s:%d" % (rel, line), m.group(0), path or m.group(1), 0, 0))
357
+ starts = [a for a, _ in read]
358
+ for m in loose(text):
359
+ k = bisect.bisect_right(starts, m.start()) - 1
360
+ if k >= 0 and m.end() <= read[k][1]:
361
+ continue
362
+ path = resolve(m.group(1))
363
+ if path and os.path.isfile(os.path.join(HERE, path)):
364
+ invisible += 1
365
+ unjudged[rel] = unjudged.get(rel, 0) + 1
366
+ return flagged, judged, unjudged, invisible
367
+
368
+
369
+ def ratchet_baseline():
370
+ """The committed {file: {"unjudged": n}} the ratchet compares against."""
371
+ return tezgah_clarity.load(os.path.join(HERE, CITATION_BASELINE))
372
+
373
+
374
+ def ratchet_drift(unjudged, baseline):
375
+ """[(file, count, allowed)] for each file whose unjudged count is not its
376
+ baseline (absent: 0). Over is a new citation nobody can judge; under is a
377
+ fixed one whose lower count was not written down, which would let the next
378
+ new one in silently. Read-only: CI runs `--citations` on a checkout, so the
379
+ baseline moves only by `--citations --update`, never as a side effect."""
380
+ allowed = {rel: row.get("unjudged", 0) for rel, row in baseline.items()}
381
+ return [(rel, unjudged.get(rel, 0), allowed.get(rel, 0))
382
+ for rel in sorted(set(unjudged) | set(allowed))
383
+ if unjudged.get(rel, 0) != allowed.get(rel, 0)]
384
+
385
+
386
+ # --- the rule ledger --------------------------------------------------------
387
+ # Every refusal has a rule name and an origin, and until now both lived only as
388
+ # prose spread through docs/gate.md: the rule name in the section, the incident
389
+ # in a sentence inside it. The table under that page's `## Rule provenance` is
390
+ # that record moved to where a machine reads it - one row per refused rule, with
391
+ # the incident that produced it, where that incident is written down, and the
392
+ # test that guards it. This check is the
393
+ # citation audit's sibling and rides the same `--citations` pass, because both
394
+ # answer "is what the page promises still true" and both are re-run after a
395
+ # change to the module a page documents.
396
+ #
397
+ # Two things are refused, and both are drift a reader cannot see: a rule name
398
+ # the gate can still produce that has no row (a rule landed, or was renamed,
399
+ # with no incident and no case written down), and a row whose `pin` names a test
400
+ # that does not exist (the case was deleted or renamed and the row goes on
401
+ # naming it). Like the rest of the pass this is a gate: the ledger is small and
402
+ # every row is checkable, so a wrong one is a failure.
403
+ #
404
+ # The rule names are read off the source rather than restated here, the way the
405
+ # audit reads a symbol: the gate's rules are the `_deny(session_id, "<rule>",
406
+ # ...)` calls in hooks/tezgah_gate.py, and the Stop rule's triggers are the
407
+ # classes `_stop_block` returns - including the four `_shape_block` and the six
408
+ # `_evidence_block` hand back, which is why those two functions are read too.
409
+
410
+ GATE = os.path.join(HERE, "hooks", "tezgah_gate.py")
411
+ STOP = os.path.join(HERE, "hooks", "tezgah_integrity.py")
412
+ LEDGER = os.path.join(HERE, "docs", "gate.md")
413
+ LEDGER_COLUMNS = ("rule", "incident", "evidence", "pin")
414
+
415
+
416
+ def source(path):
417
+ """A module's text, read once (closed, so the suite sees no leak warning)."""
418
+ with open(path, encoding="utf-8") as fh:
419
+ return fh.read()
420
+
421
+
422
+ def returned_classes(node):
423
+ """The string classes a decision function returns as its first element.
424
+
425
+ The class is the first element of the returned tuple (`("check failed",
426
+ "…")`), and a `(None, None)` or a name (`return shaped`) is not one."""
427
+ out = set()
428
+ for sub in ast.walk(node):
429
+ if not isinstance(sub, ast.Return) or sub.value is None:
430
+ continue
431
+ first = sub.value.elts[0] if isinstance(sub.value, ast.Tuple) \
432
+ and sub.value.elts else sub.value
433
+ if isinstance(first, ast.Constant) and isinstance(first.value, str):
434
+ out.add(first.value)
435
+ return out
436
+
437
+
438
+ def gate_rules(text=None):
439
+ """The rule names `_deny` writes into the ledger, read off the source."""
440
+ tree = ast.parse(source(GATE) if text is None else text, filename=GATE)
441
+ out = set()
442
+ for node in ast.walk(tree):
443
+ if not (isinstance(node, ast.Call)
444
+ and isinstance(node.func, ast.Name) and node.func.id == "_deny"):
445
+ continue
446
+ if len(node.args) > 1 and isinstance(node.args[1], ast.Constant) \
447
+ and isinstance(node.args[1].value, str):
448
+ out.add(node.args[1].value)
449
+ return out
450
+
451
+
452
+ def stop_triggers():
453
+ """The classes `_stop_block` can return, the shape and evidence halves
454
+ included."""
455
+ tree = ast.parse(source(STOP))
456
+ out = set()
457
+ for node in ast.walk(tree):
458
+ if isinstance(node, ast.FunctionDef) and node.name in (
459
+ "_stop_block", "_shape_block", "_evidence_block"):
460
+ out |= returned_classes(node)
461
+ return out
462
+
463
+
464
+ def ledger_rows():
465
+ """The rule-provenance rows in docs/gate.md, as {column: value} dicts."""
466
+ return table_rows(LEDGER, LEDGER_COLUMNS)
467
+
468
+
469
+ def table_rows(path, columns):
470
+ """The rows of the table in `path` whose header is `columns`, as {column:
471
+ value} dicts, or [] when the page is missing.
472
+
473
+ The header row is the table's own signature, so no other table on the page
474
+ is read; a cell written as a code span (`` `explorer` ``) loses its backticks here."""
475
+ try:
476
+ with open(path, encoding="utf-8") as fh:
477
+ lines = fh.read().splitlines()
478
+ except OSError:
479
+ return []
480
+ rows, header = [], False
481
+ for line in lines:
482
+ if not line.lstrip().startswith("|"):
483
+ header = False
484
+ continue
485
+ cells = [c.strip().strip("`") for c in line.strip().strip("|").split("|")]
486
+ if tuple(cells) == columns:
487
+ header = True
488
+ continue
489
+ if not header or all(c and set(c) <= set("-") for c in cells):
490
+ continue
491
+ rows.append(dict(zip(columns, cells)))
492
+ return rows
493
+
494
+
495
+ def pin_exists(pin):
496
+ """True when `pin` names a test that exists, as `<file>.py::<test_name>`."""
497
+ path, sep, name = pin.partition("::")
498
+ if not sep or not name:
499
+ return False
500
+ full = os.path.join(HERE, path)
501
+ if not os.path.isfile(full):
502
+ return False
503
+ try:
504
+ with open(full, encoding="utf-8", errors="replace") as fh:
505
+ text = fh.read()
506
+ except OSError:
507
+ return False
508
+ return re.search(r"^\s*def %s\(" % re.escape(name), text, re.M) is not None
509
+
510
+
511
+ def ledger_failures(rows):
512
+ """The two ways the record lies: a rule with no row, a pin that names no
513
+ test. An empty list is the record being true."""
514
+ out = []
515
+ known = {row["rule"] for row in rows}
516
+ for name in sorted(gate_rules() | stop_triggers()):
517
+ if name not in known:
518
+ out.append("rule `%s` has no provenance row in docs/gate.md" % name)
519
+ for row in rows:
520
+ if not pin_exists(row["pin"]):
521
+ out.append("row `%s` names no test that exists: %s"
522
+ % (row["rule"], row["pin"]))
523
+ return out
524
+
525
+
526
+ # --- the inventories --------------------------------------------------------
527
+ # Three lists a page keeps by hand, each read off the code by AST rather than
528
+ # restated in a literal here (one definition per concept): the gate's rules in
529
+ # the order `decision` checks them, the programs that call the judge seam, and
530
+ # the kill switches the code reads. They ride the `--citations` pass for the
531
+ # ledger's reason: each answers "is what the page promises still true".
532
+
533
+ JUDGE_PAGE = os.path.join(HERE, "docs", "judge.md")
534
+ CONTRACT_SKILL = os.path.join(HERE, "skills", "tezgah-contract", "SKILL.md")
535
+ SETUP = os.path.join(HERE, "bin", "tezgah-setup")
536
+ NUMBERS = ("zero", "one", "two", "three", "four", "five", "six", "seven",
537
+ "eight", "nine", "ten")
538
+ # ADR 007 (2026-10-05) leaves these unclassified: two `off()` names the CORE
539
+ # kill-switch paragraph does not list and two opt-in markers read with
540
+ # `armed()`. Until the owner decides whether they are rule switches the check
541
+ # pins CORE against the code and carries these as the one explicit exception.
542
+ UNCLASSIFIED_SWITCHES = frozenset((
543
+ "agents-off", "update-check-off", "taste-on", "skill-suggest-on"))
544
+
545
+
546
+ def rule_sites(text=None):
547
+ """[(rule, first line, last line, switches, tool classes)] for every
548
+ `return _deny(session_id, "<rule>", ...)` statement in `decision`, in source
549
+ order. A switch is an enclosing `if not off("<name>")` guard, a tool class
550
+ an enclosing `t in <NAME>` test; the line span is the statement's own, which
551
+ is what tests/neuter_matrix.py replaces to revert the rule (it passes HEAD's
552
+ `text`, the tree it mutates)."""
553
+ tree = ast.parse(source(GATE) if text is None else text, filename=GATE)
554
+ body = next(n for n in tree.body
555
+ if isinstance(n, ast.FunctionDef) and n.name == "decision").body
556
+ out = []
557
+
558
+ def guards(test):
559
+ switches, tools = [], []
560
+ for node in ast.walk(test):
561
+ if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.Not) \
562
+ and isinstance(node.operand, ast.Call) \
563
+ and getattr(node.operand.func, "id", "") == "off":
564
+ switches.append(node.operand.args[0].value)
565
+ if isinstance(node, ast.Compare) and getattr(node.left, "id", "") == "t" \
566
+ and isinstance(node.ops[0], ast.In):
567
+ tools += [n.id for n in ast.walk(node.comparators[0])
568
+ if isinstance(n, ast.Name)]
569
+ return switches, tools
570
+
571
+ def walk(stmts, switches, tools):
572
+ for stmt in stmts:
573
+ if isinstance(stmt, ast.If):
574
+ more, classes = guards(stmt.test)
575
+ walk(stmt.body, switches + more, tools + classes)
576
+ walk(stmt.orelse, switches, tools)
577
+ elif isinstance(stmt, ast.Return) and isinstance(stmt.value, ast.Call) \
578
+ and getattr(stmt.value.func, "id", "") == "_deny":
579
+ out.append((stmt.value.args[1].value, stmt.lineno, stmt.end_lineno,
580
+ tuple(dict.fromkeys(switches)),
581
+ tuple(dict.fromkeys(tools))))
582
+ else:
583
+ for field in ("body", "orelse", "finalbody", "handlers", "cases"):
584
+ walk(getattr(stmt, field, []), switches, tools)
585
+ walk(body, [], [])
586
+ return out
587
+
588
+
589
+ def rule_site_failures(text=None):
590
+ """A rule `_deny` names that `rule_sites` cannot place - a call outside a
591
+ `return _deny(...)` statement (`out = _deny(...); return out`) or outside
592
+ `decision` - has no line span for the neuter matrix and no place in the
593
+ rule order, so it is refused here instead of vanishing from both."""
594
+ placed = {site[0] for site in rule_sites(text)}
595
+ return ["rule `%s` is denied somewhere `rule_sites` cannot read: return "
596
+ "`_deny(...)` directly inside `decision`" % name
597
+ for name in sorted(gate_rules(text) - placed)]
598
+
599
+
600
+ def sections(text, parent, level="### "):
601
+ """[(heading, body)] of the `level` headings under the `## <parent>` one."""
602
+ out, inside = [], False
603
+ for line in text.splitlines():
604
+ if line.startswith("## "):
605
+ inside = line.startswith("## " + parent)
606
+ elif inside and line.startswith(level):
607
+ out.append((line[len(level):], []))
608
+ elif inside and out:
609
+ out[-1][1].append(line)
610
+ return [(head, "\n".join(body)) for head, body in out]
611
+
612
+
613
+ def rule_order_failures(text=None):
614
+ """The ways docs/gate.md's rule list disagrees with `decision`: a rule with
615
+ no `###` heading, headings in another order than the rules' first deny
616
+ sites, and a rule section that does not name a switch guarding it. A
617
+ heading is matched to the rule its first word starts with (`Language` is
618
+ `lang`); one that matches none (`Nudge`, no `_deny`) is not a rule."""
619
+ sites = rule_sites()
620
+ order, switches = [], {}
621
+ for rule, _lo, _hi, guard, _tools in sites:
622
+ if rule not in order:
623
+ order.append(rule)
624
+ switches.setdefault(rule, set()).update(guard)
625
+ headed = []
626
+ for head, body in sections(source(LEDGER) if text is None else text,
627
+ "The rules, in the order"):
628
+ word = head.split()[0].lower() if head.split() else ""
629
+ rule = next((r for r in order if word.startswith(r)), None)
630
+ if rule:
631
+ headed.append((rule, body))
632
+ out = ["rule `%s` has no `###` heading in docs/gate.md's rule list" % r
633
+ for r in order if r not in [h for h, _ in headed]]
634
+ if [h for h, _ in headed] != [r for r in order if r in dict(headed)]:
635
+ out.append("docs/gate.md heads the rules in the order %s; `decision` "
636
+ "checks them in the order %s"
637
+ % (", ".join(h for h, _ in headed), ", ".join(order)))
638
+ for rule, body in headed:
639
+ for name in sorted(switches[rule]):
640
+ if "`%s`" % name not in body:
641
+ out.append("docs/gate.md's `%s` section does not name its switch "
642
+ "`%s`" % (rule, name))
643
+ return out
644
+
645
+
646
+ _PROGRAMS = {}
647
+
648
+
649
+ def python_programs():
650
+ """The Python programs under hooks/ and bin/ that call `off`, `armed` or the
651
+ judge seam - the only ones the two checks below read - relative to HERE,
652
+ each with its parsed tree; parsed once per HERE."""
653
+ if HERE not in _PROGRAMS:
654
+ out = []
655
+ for rel in code_files():
656
+ path = os.path.join(HERE, rel)
657
+ if not rel.startswith(("hooks/", "bin/")) or not is_python(path):
658
+ continue
659
+ text = source(path)
660
+ if "off(" in text or "armed(" in text or "tezgah_judge" in text:
661
+ out.append((rel, ast.parse(text, filename=path)))
662
+ _PROGRAMS[HERE] = out
663
+ return _PROGRAMS[HERE]
664
+
665
+
666
+ def judge_callers():
667
+ """The programs that reach `tezgah_judge.ask` (called, or handed on as the
668
+ route's `ask`), sorted."""
669
+ return sorted(rel for rel, tree in python_programs()
670
+ if "tezgah_judge" in source(os.path.join(HERE, rel))
671
+ and any(isinstance(n, ast.Attribute) and n.attr == "ask"
672
+ and getattr(n.value, "id", "") == "tezgah_judge"
673
+ for n in ast.walk(tree)))
674
+
675
+
676
+ def judge_caller_failures(text=None):
677
+ """docs/judge.md against `judge_callers()`: the callers table names every
678
+ program, and the count in the page title and the table heading is right."""
679
+ text = source(JUDGE_PAGE) if text is None else text
680
+ callers = judge_callers()
681
+ word = NUMBERS[len(callers)] if len(callers) < len(NUMBERS) else str(len(callers))
682
+ table = re.search(r"^## The (\w+) callers\n(.*?)(?=^## )", text, re.M | re.S)
683
+ if not table:
684
+ return ["docs/judge.md has no `## The <n> callers` section"]
685
+ out = ["docs/judge.md's callers table has no row for `%s`" % rel
686
+ for rel in callers if "| `%s` |" % rel not in table.group(2)]
687
+ if table.group(1) != word:
688
+ out.append("docs/judge.md heads %s callers; %s programs call the seam"
689
+ % (table.group(1), word))
690
+ if "its %s callers" % word not in text.splitlines()[0]:
691
+ out.append("docs/judge.md's title does not say its %s callers" % word)
692
+ return out
693
+
694
+
695
+ def code_switches():
696
+ """({off names}, {armed names}): the first argument of every `off(...)` and
697
+ `armed(...)` call under hooks/ and bin/, a module-level string constant
698
+ (`armed(ARM)`) resolved to its value."""
699
+ found = {"off": set(), "armed": set()}
700
+ for _rel, tree in python_programs():
701
+ consts = {t.id: n.value.value for n in tree.body if isinstance(n, ast.Assign)
702
+ and isinstance(n.value, ast.Constant) and isinstance(n.value.value, str)
703
+ for t in n.targets if isinstance(t, ast.Name)}
704
+ for node in ast.walk(tree):
705
+ if not isinstance(node, ast.Call) or not node.args:
706
+ continue
707
+ name = getattr(node.func, "id", None) or getattr(node.func, "attr", None)
708
+ arg = node.args[0]
709
+ value = arg.value if isinstance(arg, ast.Constant) else consts.get(
710
+ getattr(arg, "id", None))
711
+ if name in found and isinstance(value, str):
712
+ found[name].add(value)
713
+ return found["off"], found["armed"]
714
+
715
+
716
+ def switch_names(text):
717
+ """The `-off`/`.off` names a kill-switch paragraph spells in backticks."""
718
+ return {n for n in re.findall(r"`([^`\s]+)`", text) if n.endswith(("-off", ".off"))}
719
+
720
+
721
+ def core_switches():
722
+ """The switches CORE's `**Kill switches:**` paragraph names."""
723
+ import tezgah_policy
724
+ core = tezgah_policy.CORE
725
+ return switch_names(core[core.index("**Kill switches:**"):].split("\n\n")[0])
726
+
727
+
728
+ def switch_failures(core=None):
729
+ """The kill-switch inventory against the code: every CORE name is read by
730
+ an `off()` call, every name the code reads is CORE or one of the
731
+ UNCLASSIFIED_SWITCHES, the contract skill lists CORE exactly, and
732
+ bin/tezgah-setup's OFF_SWITCH_NAMES (what an uninstall sweeps) is every
733
+ `off()` name."""
734
+ core = core_switches() if core is None else core
735
+ off, armed = code_switches()
736
+ out = ["CORE names `%s`, which no `off()` call reads" % n for n in sorted(core - off)]
737
+ out += ["the code reads `%s`, which neither CORE nor the unclassified set names"
738
+ % n for n in sorted((off | armed) - core - UNCLASSIFIED_SWITCHES)]
739
+ out += ["the unclassified switch `%s` is no longer read by the code" % n
740
+ for n in sorted(UNCLASSIFIED_SWITCHES - off - armed)]
741
+ skill = source(CONTRACT_SKILL)
742
+ block = skill[skill.index("## Kill switches"):].split("\n## ")[0]
743
+ if switch_names(block) != core:
744
+ out.append("skills/tezgah-contract/SKILL.md lists %s; CORE lists %s"
745
+ % (sorted(switch_names(block)), sorted(core)))
746
+ tree = ast.parse(source(SETUP), filename=SETUP)
747
+ names = next((ast.literal_eval(n.value) for n in tree.body if isinstance(n, ast.Assign)
748
+ and getattr(n.targets[0], "id", "") == "OFF_SWITCH_NAMES"), ())
749
+ if set(names) != off:
750
+ out.append("bin/tezgah-setup's OFF_SWITCH_NAMES lacks %s and adds %s"
751
+ % (sorted(off - set(names)), sorted(set(names) - off)))
752
+ return out
753
+
754
+
755
+ # --- the enforcement matrix -------------------------------------------------
756
+ # Every always-on CORE rule against what enforces it: a gate rule or a Stop
757
+ # class that refuses, a report-only shape flag that only records, or none. A
758
+ # link is read off the code wherever the code states one - an enforcer named
759
+ # like the rule's CORE_RULES key, or guarded by a switch whose `switches()`
760
+ # branch drops the rule's paragraph - and HAND_LINKS carries the few it cannot
761
+ # state. Each enforcer also carries its guards, the enclosing `if` tests that
762
+ # can switch it off, so a rule whose every refusal vanishes while its text stays
763
+ # reads `partial` rather than `enforced`. A rule with no refusing enforcer, or
764
+ # one whose enforcers cover only part of it, has a row in docs/contract.md's
765
+ # table saying why (or "no decision").
766
+
767
+ CONTEXT = os.path.join(HERE, "hooks", "tezgah_context.py")
768
+ STOP_HOOK = os.path.join(HERE, "hooks", "projects-stop.py")
769
+ CONTRACT_PAGE = os.path.join(HERE, "docs", "contract.md")
770
+ PROSE_ONLY_COLUMNS = ("rule", "decision")
771
+ # `explorer` refuses a grep-only subagent and names the code-graph tools
772
+ # (EXPLORE_DENY) under no switch; `retry` is the loop rule's "three attempts is
773
+ # the ceiling" (RETRY_CEILING) but sits under `verify-off`, which drops only the
774
+ # integrity paragraph; `placating opener` enforces the Deliver paragraph's
775
+ # "Never placate" under the output-shape rule's switch; the shape flags
776
+ # (`flag:*`) measure the shape `_shape_block` enforces for the output rule, but
777
+ # are recorded on every judged reply, under the Stop hook's switch only.
778
+ HAND_LINKS = {"gate:explorer": "graph", "gate:retry": "loop",
779
+ "stop:placating opener": "fidelity", "flag:*": "adhd"}
780
+
781
+
782
+ def _function(path, name):
783
+ """The top-level `def name` of the Python file at `path`."""
784
+ tree = ast.parse(source(path), filename=path)
785
+ return next(n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name == name)
786
+
787
+
788
+ def _switch_words(node):
789
+ """The switches a guard expression reads: `off("<name>")` calls and
790
+ `"<mark>" in marks` tests."""
791
+ out = []
792
+ for n in ast.walk(node):
793
+ if isinstance(n, ast.Call) and getattr(n.func, "id", "") == "off" and n.args \
794
+ and isinstance(n.args[0], ast.Constant):
795
+ out.append(n.args[0].value)
796
+ elif isinstance(n, ast.Compare) and isinstance(n.left, ast.Constant) \
797
+ and isinstance(n.left.value, str):
798
+ out.append(n.left.value)
799
+ return out
800
+
801
+
802
+ def switch_drops():
803
+ """{switch or repo mark: {CORE_RULES key}}, read off
804
+ `tezgah_context.switches`: each `if` branch's switches and the keys its
805
+ body `drop.add`s."""
806
+ out = {}
807
+ for node in ast.walk(_function(CONTEXT, "switches")):
808
+ if isinstance(node, ast.If):
809
+ keys = {c.args[0].value for s in node.body for c in ast.walk(s)
810
+ if isinstance(c, ast.Call) and getattr(c.func, "attr", "") == "add"}
811
+ for name in _switch_words(node.test):
812
+ out.setdefault(name, set()).update(keys)
813
+ return out
814
+
815
+
816
+ def core_rules(core=None):
817
+ """[(key, label)] for every CORE paragraph opening with a bold head, in
818
+ order, the kill-switch list left out. The label is the policy text's own;
819
+ the key is its `tezgah_context.CORE_RULES` id (None when that table lacks it)."""
820
+ import tezgah_policy
821
+ core = tezgah_policy.CORE if core is None else core
822
+ table = next(n.value for n in ast.parse(source(CONTEXT), filename=CONTEXT).body
823
+ if isinstance(n, ast.Assign) and getattr(n.targets[0], "id", "") == "CORE_RULES")
824
+ keys = {label: key for key, label in ast.literal_eval(table)}
825
+ out = []
826
+ for para in core.split("\n\n"):
827
+ m = re.match(r"\s*(\*\*(.+?)\*\*)", para)
828
+ if m and not m.group(2).startswith("Kill switches"):
829
+ out.append((keys.get(m.group(1)), m.group(2)))
830
+ return out
831
+
832
+
833
+ def _guarded_returns(func, funcs, pick):
834
+ """[(name, switches, conditions)] for every `return` in `func` that `pick`
835
+ names, with what its enclosing `if` tests guard it by. A conjunct of a test
836
+ that reads a switch (`_switch_words`, or a call to a function in `funcs`
837
+ that reads one: `_adhd_armed`) is a switch guard; a conjunct that names no
838
+ argument or local of `func` is a condition on the environment (`reply_lang()
839
+ == "tr"`); a conjunct reading the call's own data is the trigger itself and
840
+ is not a guard. The switch a whole hook checks first (`pretooluse-off`, the
841
+ Stop hook's `verify-off`) is outside these functions and is not counted."""
842
+ own = {a.arg for a in func.args.args + func.args.kwonlyargs} | {
843
+ n.id for n in ast.walk(func) if isinstance(n, ast.Name) and isinstance(n.ctx, ast.Store)}
844
+ out = []
845
+
846
+ def split(test):
847
+ parts = test.values if isinstance(test, ast.BoolOp) and isinstance(test.op, ast.And) \
848
+ else [test]
849
+ switches, conditions = [], []
850
+ for part in parts:
851
+ words = _switch_words(part) + [
852
+ w for n in ast.walk(part) if isinstance(n, ast.Call)
853
+ and getattr(n.func, "id", "") in funcs for w in _switch_words(funcs[n.func.id])]
854
+ if words:
855
+ switches += words
856
+ elif not any(isinstance(n, ast.Name) and n.id in own for n in ast.walk(part)):
857
+ conditions.append(ast.unparse(part))
858
+ return switches, conditions
859
+
860
+ def walk(stmts, switches, conditions):
861
+ for stmt in stmts:
862
+ if isinstance(stmt, ast.If):
863
+ more, extra = split(stmt.test)
864
+ walk(stmt.body, switches + more, conditions + extra)
865
+ walk(stmt.orelse, switches, conditions)
866
+ elif isinstance(stmt, ast.Return):
867
+ out.extend((name, tuple(dict.fromkeys(switches)),
868
+ tuple(dict.fromkeys(conditions))) for name in pick(stmt))
869
+ else:
870
+ for field in ("body", "orelse", "finalbody", "handlers"):
871
+ walk(getattr(stmt, field, []), switches, conditions)
872
+ walk(func.body, [], [])
873
+ return out
874
+
875
+
876
+ def stop_sites():
877
+ """[(class, switches, conditions)] for every class `_shape_block`,
878
+ `_stop_block` and `_evidence_block` return (`_guarded_returns`)."""
879
+ tree = ast.parse(source(STOP), filename=STOP)
880
+ funcs = {n.name: n for n in tree.body if isinstance(n, ast.FunctionDef)}
881
+ return [site for name in ("_shape_block", "_stop_block", "_evidence_block")
882
+ for site in _guarded_returns(funcs[name], funcs,
883
+ lambda r: sorted(returned_classes(r)))]
884
+
885
+
886
+ def gate_sites():
887
+ """[(rule, switches, conditions)] for every `return _deny(...)` in
888
+ `decision` (`_guarded_returns`)."""
889
+ def pick(ret):
890
+ call = ret.value
891
+ return [call.args[1].value] if isinstance(call, ast.Call) \
892
+ and getattr(call.func, "id", "") == "_deny" else []
893
+ return _guarded_returns(_function(GATE, "decision"), {}, pick)
894
+
895
+
896
+ def enforcer_links():
897
+ """{enforcer id: {"rules", "switches", "conditions"}} for every gate rule
898
+ (`gate:<rule>`), Stop class (`stop:<class>`) and report-only shape flag
899
+ (`flag:<flag>`), in that order: the CORE_RULES keys it is linked to, and the
900
+ guards every one of its sites shares. A Stop class with no switch of its own
901
+ is linked through the Stop hook's switch; a flag rides only that switch."""
902
+ drops = switch_drops()
903
+ keys = {key for key, _ in core_rules()}
904
+ found = {}
905
+
906
+ def link(eid, name, switches, conditions, via):
907
+ row = found.get(eid)
908
+ if row is None:
909
+ row = found[eid] = {"rules": set(), "switches": switches,
910
+ "conditions": conditions}
911
+ row["switches"] = tuple(s for s in row["switches"] if s in switches)
912
+ row["conditions"] = tuple(c for c in row["conditions"] if c in conditions)
913
+ row["rules"].update({name} & keys)
914
+ for s in via:
915
+ row["rules"] |= drops.get(s, set())
916
+ for rule, switches, conditions in gate_sites():
917
+ link("gate:" + rule, rule, switches, conditions, switches)
918
+ hook = [w for n in ast.walk(_function(STOP_HOOK, "main")) if isinstance(n, ast.If)
919
+ for w in _switch_words(n.test)]
920
+ for cls, switches, conditions in stop_sites():
921
+ link("stop:" + cls, cls, switches, conditions, switches or hook)
922
+ for c in ast.walk(_function(STOP, "shape_flags")):
923
+ if isinstance(c, ast.Call) and getattr(c.func, "attr", "") == "append":
924
+ link("flag:" + c.args[0].value, None, (), (), ())
925
+ for eid, row in found.items():
926
+ hand = HAND_LINKS.get(eid) or HAND_LINKS.get(eid.split(":")[0] + ":*")
927
+ row["rules"].update({hand} if hand else set())
928
+ return found
929
+
930
+
931
+ def prose_only_rows():
932
+ """The record in docs/contract.md, as {rule, decision} dicts: why a rule has
933
+ no refusing enforcer, or which part of it no enforcer covers."""
934
+ return table_rows(CONTRACT_PAGE, PROSE_ONLY_COLUMNS)
935
+
936
+
937
+ def enforcers(core=None, record=None, links=None):
938
+ """[{rule, label, status, enforcers, guards, decision}], one per always-on
939
+ CORE rule in policy order. `status` is `enforced`, `partial` (every refusing
940
+ enforcer has a guard that can remove it while the rule's text stays, or the
941
+ record names a part no enforcer covers), `records` (only shape flags, which
942
+ refuse nothing) or `prose-only`; `guards` maps each enforcer to its switches
943
+ and conditions; `decision` is the record's row (None without one)."""
944
+ record = prose_only_rows() if record is None else record
945
+ decisions = {r["rule"]: r["decision"] for r in record}
946
+ links = enforcer_links() if links is None else links
947
+ drops = switch_drops()
948
+ rows = []
949
+ for key, label in core_rules(core):
950
+ ids = [eid for eid, row in links.items() if key in row["rules"]]
951
+ refusing = [eid for eid in ids if not eid.startswith("flag:")]
952
+ conditional = all(
953
+ links[eid]["conditions"] or any(key not in drops.get(s, set())
954
+ for s in links[eid]["switches"])
955
+ for eid in refusing)
956
+ decision = decisions.get(key)
957
+ status = ("prose-only" if not ids else "records" if not refusing
958
+ else "partial" if conditional or decision is not None else "enforced")
959
+ rows.append({"rule": key, "label": label, "status": status, "enforcers": ids,
960
+ "guards": {eid: list(links[eid]["switches"] + links[eid]["conditions"])
961
+ for eid in ids},
962
+ "decision": decision})
963
+ return rows
964
+
965
+
966
+ def enforcer_failures(core=None, record=None, links=None):
967
+ """The ways the matrix is not whole: a CORE rule that nothing refuses on
968
+ (no enforcer, or shape flags only) with no row in the record, and a row
969
+ naming no CORE rule."""
970
+ record = prose_only_rows() if record is None else record
971
+ rows = enforcers(core, record, links)
972
+ out = ["CORE rule `%s` (%s) is %s and has no row in docs/contract.md"
973
+ % (row["rule"], row["label"], "recorded only by %s" % ", ".join(row["enforcers"])
974
+ if row["status"] == "records" else "enforced by nothing")
975
+ for row in rows if row["status"] in ("records", "prose-only")
976
+ and row["decision"] is None]
977
+ named = {row["rule"] for row in rows}
978
+ out += ["record row `%s` names no CORE rule" % r["rule"]
979
+ for r in record if r["rule"] not in named]
980
+ return out
981
+
982
+
983
+ def enforcement_report():
984
+ """The matrix as `tezgah-status --enforcers` prints it: the rows, the
985
+ enforcers tied to no CORE rule, and the failures."""
986
+ return {"rows": enforcers(),
987
+ "unattached": sorted(e for e, row in enforcer_links().items() if not row["rules"]),
988
+ "failures": enforcer_failures()}
989
+
990
+
991
+ INVENTORY_CHECKS = (rule_site_failures, rule_order_failures, judge_caller_failures,
992
+ switch_failures, enforcer_failures)
993
+
994
+
995
+ def inventory_failures():
996
+ """Every inventory check above, one list."""
997
+ return [f for check in INVENTORY_CHECKS for f in check()]
998
+
999
+
1000
+ NONE = "none"
1001
+ # `none` is an option on purpose: without it the question has to pick a page, and
1002
+ # a query that names nothing in the layer (a typo, a nonsense word) would come
1003
+ # back with an arbitrary page instead of the "nothing matches" this command
1004
+ # promises. It is also what keeps `tezgah-docs zzz-nothing-matches` exiting 1.
1005
+ ASK = ("Which page of this project's docs answers the reader's question? The "
1006
+ "reader may name a thing with a different word from the pages' own - the "
1007
+ "same subject under a synonym - so match the topic rather than the wording, "
1008
+ "and pick the page whose answers would settle the question. Answer 'none' "
1009
+ "when the question names nothing any page answers - a typo, a nonsense "
1010
+ "string, or a topic outside this layer.")
1011
+
1012
+
1013
+ def off():
1014
+ """True when this command must not ask: its own switch first, then the seam's.
1015
+
1016
+ `docs-judge-off` answers for this command alone - the triage and the skill
1017
+ picker keep judging - and `judge-off`, or an unresolvable credential, turns the
1018
+ whole path off. Both are read before the request, so an armed switch makes no
1019
+ request at all rather than one whose answer is thrown away."""
1020
+ return tp.off("docs-judge-off") or not tezgah_judge.available()
1021
+
1022
+
1023
+ def record(result):
1024
+ """One `judge` ledger row for the call just paid for, in the shape the status
1025
+ counters fold on: which caller, which model, and what it cost.
1026
+
1027
+ A cost row, never a step and never a check - nothing here may license a done
1028
+ claim - and best effort: no session id, or an unwritable ledger, changes
1029
+ nothing this command prints."""
1030
+ usage = result["usage"]
1031
+ ti.note(os.environ.get("TEZGAH_SESSION"), "judge",
1032
+ "tezgah-docs %s in=%d out=%d ms=%d"
1033
+ % (result.get("model", tezgah_judge.MODEL),
1034
+ usage["input_tokens"], usage["output_tokens"],
1035
+ result["latency_ms"]))
1036
+
1037
+
1038
+ def judge_pick(query):
1039
+ """(judged, page): whether a TypeSafe Choice question came back with one of
1040
+ its options, and the page it named (None for `none`).
1041
+
1042
+ The last resort for a query the index could not place: a word the index does
1043
+ not carry is usually a word nobody wrote down, and a model reading the pages'
1044
+ own questions can still land the reader on the right one. Three things keep
1045
+ it from standing in for the match above - it runs only when that match found
1046
+ nothing, the question offers `none`, and every failure (no key, `judge-off`,
1047
+ a refused or unreadable reply, an option it was not offered) reads as not
1048
+ judged, so the caller falls back to the word ranking instead of trusting it.
1049
+
1050
+ Measured over 39 live queries the index could not place (33 paraphrases of
1051
+ the pages' own answers plus the queries naming nothing in the layer): 37 pages
1052
+ right, 0 wrong, 2 missed, 0 pages picked for a query outside the layer. All
1053
+ three of the misses the first wording produced were the reader's word rather
1054
+ than the page's, which is what ASK now speaks to - that instruction recovered
1055
+ one of them and cost nothing - and the two that remain (a client where the
1056
+ page says host, "support one more" where it says add) stay missed because the
1057
+ layer carries no synonym list to judge them against."""
1058
+ pages = entries()
1059
+ if not query or not pages or off():
1060
+ return False, None
1061
+ criteria = {NONE: "no page answers this question"}
1062
+ for page in pages:
1063
+ criteria[page["path"]] = "%s. Answers: %s" % (
1064
+ page.get("title") or "", "; ".join(page.get("answers") or []))
1065
+ result = tezgah_judge.ask(query, {"page": {
1066
+ "type": "choice", "instructions": ASK, "criteria": criteria}})
1067
+ if result is not None:
1068
+ record(result)
1069
+ chosen = tezgah_judge.choice(result, "page")
1070
+ page = next((p for p in pages if p["path"] == chosen), None)
1071
+ return bool(page) or chosen == NONE, page
1072
+
1073
+
1074
+ # How many pages the word ranking prints: the right page was in the top three for
1075
+ # 18 of 20 measured questions (10 of 10 English, 8 of 10 Turkish, the Turkish
1076
+ # phrasings included) and first for 12.
1077
+ RANKED = 3
1078
+
1079
+
1080
+ def ranked(query):
1081
+ """The pages sharing the most query words, best first: the fallback when the
1082
+ judge gave no judgement (unavailable, or a call that failed). A page is
1083
+ ranked on its title and answers and on their Turkish phrasings (`title_tr`,
1084
+ `answers_tr`), which the word match above does not read. A query sharing no
1085
+ word with any page gets [], so `tezgah-docs zzz-nothing-matches` still exits
1086
+ 1 - unless the opt-in embedding feature is on: then pages are ranked by
1087
+ meaning alone (`tezgah_embed.nearest`, 0.91 against the word-and-meaning
1088
+ fusion's 0.75 on held-out Turkish phrasings), which places every page."""
1089
+ pages = entries()
1090
+ groups = [[str(p.get("title") or "")] + [str(a) for a in (p.get("answers") or [])]
1091
+ + [str(p.get("title_tr") or "")]
1092
+ + [str(a) for a in (p.get("answers_tr") or [])] for p in pages]
1093
+ texts = [" ".join(g + [str(p["path"])]) for g, p in zip(groups, pages)]
1094
+ # an empty string embeds as the zero vector: a free cosine of 0.0
1095
+ groups = [[s for s in g if s] for g in groups]
1096
+ return [pages[i] for i in tezgah_embed.nearest(query, texts, RANKED, groups)]
1097
+
1098
+
1099
+ KNOWN = ("--json", "--read", "--citations", "--clarity", "--update", "--help", "-h")
1100
+
1101
+
1102
+ def parse(argv):
1103
+ """(flags, query words, unknown options). A known flag is a flag wherever it
1104
+ stands before `--`; an unknown `--word` is an error before the first query
1105
+ word and a query word after it (`--no-verify`); `--` ends the options."""
1106
+ flags, words, unknown = set(), [], []
1107
+ for i, a in enumerate(argv):
1108
+ if a == "--":
1109
+ return flags, words + argv[i + 1:], unknown
1110
+ if a in KNOWN:
1111
+ flags.add(a)
1112
+ elif a.startswith("--") and not words:
1113
+ unknown.append(a)
1114
+ else:
1115
+ words.append(a)
1116
+ return flags, words, unknown
1117
+
1118
+
1119
+ def main(argv):
1120
+ flags, words, unknown = parse(argv)
1121
+ if flags & {"-h", "--help"}:
1122
+ print(__doc__.strip())
1123
+ return 0
1124
+ if unknown:
1125
+ sys.stderr.write("tezgah-docs: unknown option(s): %s (try --help)\n"
1126
+ % ", ".join(sorted(unknown)))
1127
+ return 2
1128
+ if "--update" in flags and not flags & {"--clarity", "--citations"}:
1129
+ sys.stderr.write("tezgah-docs: --update needs --clarity or --citations\n")
1130
+ return 2
1131
+ if "--clarity" in flags:
1132
+ return tezgah_clarity.report(HERE, update="--update" in flags)
1133
+ if "--citations" in flags:
1134
+ flagged, judged, unjudged, invisible = citations(symbols())
1135
+ for where, token, symbol_in, low, high in flagged:
1136
+ if not low:
1137
+ print("%s %s -> %s defines no such symbol" % (where, token, symbol_in))
1138
+ continue
1139
+ print("%s %s -> the symbol it names lives at %s:%d-%d"
1140
+ % (where, token, symbol_in, low, high))
1141
+ print("citations: %d judged (%d outside the symbol they name or naming none), "
1142
+ "%d not judgeable (no single symbol named beside them; %d of them "
1143
+ "in a shape the audit cannot read)"
1144
+ % (judged, len(flagged), sum(unjudged.values()), invisible))
1145
+ if "--update" in flags:
1146
+ tezgah_clarity.save(os.path.join(HERE, CITATION_BASELINE), {
1147
+ rel: {"unjudged": n} for rel, n in unjudged.items() if n})
1148
+ print("citations: baseline rewritten (%s)" % CITATION_BASELINE)
1149
+ over = []
1150
+ else:
1151
+ over = ratchet_drift(unjudged, ratchet_baseline())
1152
+ for rel, count, allowed in over:
1153
+ print("citations: %s has %d unjudged, baseline %d - %s"
1154
+ % (rel, count, allowed,
1155
+ "name the symbol beside each new citation" if count > allowed
1156
+ else "run `bin/tezgah-docs --citations --update` to write the "
1157
+ "lower count down"))
1158
+ ledger = ledger_failures(ledger_rows())
1159
+ for problem in ledger:
1160
+ print("rule ledger: %s" % problem)
1161
+ if ledger:
1162
+ print("rule ledger: %d row(s) of the provenance table do not hold"
1163
+ % len(ledger))
1164
+ inventory = inventory_failures()
1165
+ for problem in inventory:
1166
+ print("inventory: %s" % problem)
1167
+ return 1 if flagged or over or ledger or inventory else 0
1168
+ query = " ".join(words).strip()
1169
+ found = matches(query) if query else entries()
1170
+ if not found:
1171
+ judged, pick = judge_pick(query)
1172
+ found = [pick] if pick else []
1173
+ if not judged:
1174
+ found = ranked(query)
1175
+ if not found:
1176
+ sys.stderr.write("tezgah-docs: nothing matches %r - the bare call lists "
1177
+ "every page\n" % query)
1178
+ return 1
1179
+ if "--json" in flags:
1180
+ print(json.dumps(found, indent=2, sort_keys=True))
1181
+ return 0
1182
+ if "--read" in flags:
1183
+ try:
1184
+ with open(os.path.join(HERE, found[0]["path"]), encoding="utf-8") as fh:
1185
+ sys.stdout.write(fh.read())
1186
+ except OSError as exc:
1187
+ sys.stderr.write("tezgah-docs: %s\n" % exc)
1188
+ return 1
1189
+ return 0
1190
+ for entry in found:
1191
+ print("%s %s" % (entry["path"], entry.get("title") or ""))
1192
+ answers = "; ".join(entry.get("answers") or [])
1193
+ if answers:
1194
+ print(" answers: %s" % answers)
1195
+ return 0
1196
+
1197
+
1198
+ if __name__ == "__main__":
1199
+ sys.exit(main(sys.argv[1:]))