@renoxar/koolie 1.25.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 (693) hide show
  1. package/.gitattributes +16 -0
  2. package/.gitignore +51 -0
  3. package/.koolie/QUELLREPOSITORIUM.md +18 -0
  4. package/.koolie/core/CHANGELOG.md +11832 -0
  5. package/.koolie/core/LICENSE +674 -0
  6. package/.koolie/core/LICENSE-HINWEIS.md +98 -0
  7. package/.koolie/core/OWNERS.md +36 -0
  8. package/.koolie/core/VERSION +1 -0
  9. package/.koolie/core/banner.py +397 -0
  10. package/.koolie/core/build/README.md +34 -0
  11. package/.koolie/core/build/assemble.py +354 -0
  12. package/.koolie/core/build/build-docx.py +194 -0
  13. package/.koolie/core/build/doc/00-kopf.md +18 -0
  14. package/.koolie/core/build/doc/01-executive-summary.md +24 -0
  15. package/.koolie/core/build/doc/02-zielbild.md +13 -0
  16. package/.koolie/core/build/doc/03-ziele-nichtziele.md +28 -0
  17. package/.koolie/core/build/doc/04-geltungsbereich.md +25 -0
  18. package/.koolie/core/build/doc/05-glossar.md +47 -0
  19. package/.koolie/core/build/doc/06-leitprinzipien.md +5 -0
  20. package/.koolie/core/build/doc/07-architektur.md +60 -0
  21. package/.koolie/core/build/doc/07a-abbildungsschicht.md +66 -0
  22. package/.koolie/core/build/doc/08-trennung.md +24 -0
  23. package/.koolie/core/build/doc/09-betriebsmodi.md +18 -0
  24. package/.koolie/core/build/doc/10-arbeitsablauf.md +5 -0
  25. package/.koolie/core/build/doc/11-datenschutz.md +5 -0
  26. package/.koolie/core/build/doc/12-sicherheit.md +5 -0
  27. package/.koolie/core/build/doc/13-risiko.md +5 -0
  28. package/.koolie/core/build/doc/14-rollen.md +9 -0
  29. package/.koolie/core/build/doc/15-referenzstruktur.md +124 -0
  30. package/.koolie/core/build/doc/16-agentenanweisung.md +8 -0
  31. package/.koolie/core/build/doc/17-overlay.md +29 -0
  32. package/.koolie/core/build/doc/18-skill-standard.md +5 -0
  33. package/.koolie/core/build/doc/19-skill-template.md +5 -0
  34. package/.koolie/core/build/doc/20-referenz-skills.md +70 -0
  35. package/.koolie/core/build/doc/21-prompts.md +53 -0
  36. package/.koolie/core/build/doc/22-checklisten.md +49 -0
  37. package/.koolie/core/build/doc/23-entscheidungsbaeume.md +29 -0
  38. package/.koolie/core/build/doc/24-onboarding.md +33 -0
  39. package/.koolie/core/build/doc/25-governance.md +35 -0
  40. package/.koolie/core/build/doc/26-qs-test.md +9 -0
  41. package/.koolie/core/build/doc/27-pilot.md +13 -0
  42. package/.koolie/core/build/doc/28-uebernahme.md +5 -0
  43. package/.koolie/core/build/doc/29-grenzen.md +87 -0
  44. package/.koolie/core/build/doc/30-roadmap.md +5 -0
  45. package/.koolie/core/build/doc/31-anhaenge.md +151 -0
  46. package/.koolie/core/build/doc/32-abschluss.md +92 -0
  47. package/.koolie/core/build/ref-a4.docx +0 -0
  48. package/.koolie/core/checklists/01-preflight.md +54 -0
  49. package/.koolie/core/checklists/02-privacy-context.md +51 -0
  50. package/.koolie/core/checklists/03-before-code-change.md +51 -0
  51. package/.koolie/core/checklists/04-review-ai-code.md +58 -0
  52. package/.koolie/core/checklists/05-testing.md +48 -0
  53. package/.koolie/core/checklists/06-security.md +50 -0
  54. package/.koolie/core/checklists/07-new-dependency.md +57 -0
  55. package/.koolie/core/checklists/08-merge-request.md +47 -0
  56. package/.koolie/core/checklists/09-onboarding.md +52 -0
  57. package/.koolie/core/checklists/10-project-adoption.md +74 -0
  58. package/.koolie/core/checklists/11-framework-release.md +71 -0
  59. package/.koolie/core/checklists/README.md +28 -0
  60. package/.koolie/core/clientmap.py +1358 -0
  61. package/.koolie/core/clients/README.md +113 -0
  62. package/.koolie/core/clients/_template/CLIENT_PACK.md +197 -0
  63. package/.koolie/core/clients/claude-code/CLIENT_PACK.md +383 -0
  64. package/.koolie/core/clients/claude-code/manifest.json +232 -0
  65. package/.koolie/core/clients/claude-code/root-template/.claude/README.md +127 -0
  66. package/.koolie/core/clients/cursor/CLIENT_PACK.md +229 -0
  67. package/.koolie/core/clients/cursor/manifest.json +330 -0
  68. package/.koolie/core/clients/cursor/root-template/.cursor/README.md +36 -0
  69. package/.koolie/core/clients/devin-desktop/CLIENT_PACK.md +251 -0
  70. package/.koolie/core/clients/devin-desktop/manifest.json +163 -0
  71. package/.koolie/core/clients/devin-desktop/root-template/.devin/README.md +79 -0
  72. package/.koolie/core/clients/kiro/CLIENT_PACK.md +223 -0
  73. package/.koolie/core/clients/kiro/manifest.json +300 -0
  74. package/.koolie/core/clients/kiro/root-template/.kiro/README.md +37 -0
  75. package/.koolie/core/clients/openai-codex/CLIENT_PACK.md +257 -0
  76. package/.koolie/core/clients/openai-codex/manifest.json +208 -0
  77. package/.koolie/core/clients/openai-codex/root-template/.codex/README.md +35 -0
  78. package/.koolie/core/decision-trees/01-context-allowed.md +46 -0
  79. package/.koolie/core/decision-trees/02-may-ai-do-task.md +47 -0
  80. package/.koolie/core/decision-trees/03-analyze-or-modify.md +50 -0
  81. package/.koolie/core/decision-trees/04-required-review.md +49 -0
  82. package/.koolie/core/decision-trees/05-stop-or-escalate.md +47 -0
  83. package/.koolie/core/decision-trees/06-rule-placement.md +58 -0
  84. package/.koolie/core/decision-trees/README.md +14 -0
  85. package/.koolie/core/docs/ADOPTION_GUIDE.md +582 -0
  86. package/.koolie/core/docs/DOCUMENTATION_STANDARD.md +91 -0
  87. package/.koolie/core/docs/PLACEHOLDER_REGISTRY.md +79 -0
  88. package/.koolie/core/docs/ROADMAP.md +848 -0
  89. package/.koolie/core/docs/RUNTIME_GLOSSARY.md +113 -0
  90. package/.koolie/core/examples/README.md +11 -0
  91. package/.koolie/core/examples/example-ergebnisbericht.md +31 -0
  92. package/.koolie/core/examples/example-mr-description.md +31 -0
  93. package/.koolie/core/examples/example-overlay-runtime.md +48 -0
  94. package/.koolie/core/framework/core/00-principles.md +93 -0
  95. package/.koolie/core/framework/core/01-governance.md +58 -0
  96. package/.koolie/core/framework/core/02-privacy.md +141 -0
  97. package/.koolie/core/framework/core/03-security.md +86 -0
  98. package/.koolie/core/framework/core/04-quality.md +48 -0
  99. package/.koolie/core/framework/core/05-working-model.md +199 -0
  100. package/.koolie/core/framework/core/06-prompting-rules.md +51 -0
  101. package/.koolie/core/framework/core/07-review-rules.md +52 -0
  102. package/.koolie/core/framework/core/08-skill-conventions.md +115 -0
  103. package/.koolie/core/framework/core/09-risk-model.md +87 -0
  104. package/.koolie/core/framework/core/10-error-escalation.md +53 -0
  105. package/.koolie/core/framework/org-policies/MAPPING_CLASSIFICATION.md +16 -0
  106. package/.koolie/core/framework/org-policies/README.md +29 -0
  107. package/.koolie/core/framework/overlay-patterns/general/documents/branching-strategy/muster-general.md +43 -0
  108. package/.koolie/core/framework/overlay-patterns/general/documents/coding-guidelines/muster-general.md +82 -0
  109. package/.koolie/core/framework/overlay-patterns/general/documents/definition-of-done/muster-general.md +52 -0
  110. package/.koolie/core/framework/overlay-patterns/general/documents/definition-of-ready/muster-general.md +54 -0
  111. package/.koolie/core/framework/overlay-patterns/general/documents/quality/muster-general.md +59 -0
  112. package/.koolie/core/framework/overlay-patterns/general/documents/security/muster-general.md +52 -0
  113. package/.koolie/core/framework/overlay-patterns/general.md +150 -0
  114. package/.koolie/core/framework/role-packs/README.md +63 -0
  115. package/.koolie/core/framework/role-packs/_template/ROLE_PACK.md +58 -0
  116. package/.koolie/core/framework/role-packs/requirements-engineering/ROLE_PACK.md +158 -0
  117. package/.koolie/core/framework/role-packs/requirements-engineering/runtime/30-role-requirements-engineering.md +88 -0
  118. package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/CHANGELOG.md +10 -0
  119. package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/EXAMPLES.md +148 -0
  120. package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/SKILL.md +195 -0
  121. package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/TESTS.md +22 -0
  122. package/.koolie/core/framework/role-packs/software-development/ROLE_PACK.md +95 -0
  123. package/.koolie/core/framework/role-packs/software-development/runtime/30-role-software-development.md +26 -0
  124. package/.koolie/core/framework/runtime/agents/fw-reviewer.md +34 -0
  125. package/.koolie/core/framework/runtime/client-settings.json +3 -0
  126. package/.koolie/core/framework/runtime/hooks.json +20 -0
  127. package/.koolie/core/framework/runtime/mcp-config.example.json +4 -0
  128. package/.koolie/core/framework/runtime/permissions.json +106 -0
  129. package/.koolie/core/framework/runtime/root-instruction-local.example.md +19 -0
  130. package/.koolie/core/framework/runtime/root-instruction.md +116 -0
  131. package/.koolie/core/framework/runtime/rules/00-framework-core.md +49 -0
  132. package/.koolie/core/framework/runtime/rules/10-privacy-security.md +49 -0
  133. package/.koolie/core/framework/runtime/rules/15-development-rules.md +41 -0
  134. package/.koolie/core/framework/runtime/rules/16-plan-spezifikation.md +32 -0
  135. package/.koolie/core/framework/runtime/rules/20-project-overlay.md +60 -0
  136. package/.koolie/core/framework/skills/fw-bugfix-prepare/CHANGELOG.md +14 -0
  137. package/.koolie/core/framework/skills/fw-bugfix-prepare/EXAMPLES.md +57 -0
  138. package/.koolie/core/framework/skills/fw-bugfix-prepare/SKILL.md +177 -0
  139. package/.koolie/core/framework/skills/fw-bugfix-prepare/TESTS.md +15 -0
  140. package/.koolie/core/framework/skills/fw-change-analyze/CHANGELOG.md +12 -0
  141. package/.koolie/core/framework/skills/fw-change-analyze/EXAMPLES.md +66 -0
  142. package/.koolie/core/framework/skills/fw-change-analyze/SKILL.md +167 -0
  143. package/.koolie/core/framework/skills/fw-change-analyze/TESTS.md +16 -0
  144. package/.koolie/core/framework/skills/fw-change-small/CHANGELOG.md +13 -0
  145. package/.koolie/core/framework/skills/fw-change-small/EXAMPLES.md +68 -0
  146. package/.koolie/core/framework/skills/fw-change-small/SKILL.md +175 -0
  147. package/.koolie/core/framework/skills/fw-change-small/TESTS.md +13 -0
  148. package/.koolie/core/framework/skills/fw-code-explain/CHANGELOG.md +11 -0
  149. package/.koolie/core/framework/skills/fw-code-explain/EXAMPLES.md +61 -0
  150. package/.koolie/core/framework/skills/fw-code-explain/SKILL.md +163 -0
  151. package/.koolie/core/framework/skills/fw-code-explain/TESTS.md +11 -0
  152. package/.koolie/core/framework/skills/fw-docs-update/CHANGELOG.md +10 -0
  153. package/.koolie/core/framework/skills/fw-docs-update/EXAMPLES.md +59 -0
  154. package/.koolie/core/framework/skills/fw-docs-update/SKILL.md +160 -0
  155. package/.koolie/core/framework/skills/fw-docs-update/TESTS.md +12 -0
  156. package/.koolie/core/framework/skills/fw-error-analyze/CHANGELOG.md +10 -0
  157. package/.koolie/core/framework/skills/fw-error-analyze/EXAMPLES.md +67 -0
  158. package/.koolie/core/framework/skills/fw-error-analyze/SKILL.md +160 -0
  159. package/.koolie/core/framework/skills/fw-error-analyze/TESTS.md +12 -0
  160. package/.koolie/core/framework/skills/fw-mr-description/CHANGELOG.md +12 -0
  161. package/.koolie/core/framework/skills/fw-mr-description/EXAMPLES.md +69 -0
  162. package/.koolie/core/framework/skills/fw-mr-description/SKILL.md +170 -0
  163. package/.koolie/core/framework/skills/fw-mr-description/TESTS.md +12 -0
  164. package/.koolie/core/framework/skills/fw-overlay-pflege/CHANGELOG.md +7 -0
  165. package/.koolie/core/framework/skills/fw-overlay-pflege/EXAMPLES.md +48 -0
  166. package/.koolie/core/framework/skills/fw-overlay-pflege/SKILL.md +147 -0
  167. package/.koolie/core/framework/skills/fw-overlay-pflege/TESTS.md +13 -0
  168. package/.koolie/core/framework/skills/fw-plan/CHANGELOG.md +14 -0
  169. package/.koolie/core/framework/skills/fw-plan/EXAMPLES.md +69 -0
  170. package/.koolie/core/framework/skills/fw-plan/SKILL.md +168 -0
  171. package/.koolie/core/framework/skills/fw-plan/TESTS.md +14 -0
  172. package/.koolie/core/framework/skills/fw-refactor/CHANGELOG.md +10 -0
  173. package/.koolie/core/framework/skills/fw-refactor/EXAMPLES.md +70 -0
  174. package/.koolie/core/framework/skills/fw-refactor/SKILL.md +173 -0
  175. package/.koolie/core/framework/skills/fw-refactor/TESTS.md +14 -0
  176. package/.koolie/core/framework/skills/fw-repo-analyze/CHANGELOG.md +10 -0
  177. package/.koolie/core/framework/skills/fw-repo-analyze/EXAMPLES.md +52 -0
  178. package/.koolie/core/framework/skills/fw-repo-analyze/SKILL.md +149 -0
  179. package/.koolie/core/framework/skills/fw-repo-analyze/TESTS.md +11 -0
  180. package/.koolie/core/framework/skills/fw-review-support/CHANGELOG.md +12 -0
  181. package/.koolie/core/framework/skills/fw-review-support/EXAMPLES.md +69 -0
  182. package/.koolie/core/framework/skills/fw-review-support/SKILL.md +179 -0
  183. package/.koolie/core/framework/skills/fw-review-support/TESTS.md +13 -0
  184. package/.koolie/core/framework/skills/fw-tests/CHANGELOG.md +9 -0
  185. package/.koolie/core/framework/skills/fw-tests/EXAMPLES.md +65 -0
  186. package/.koolie/core/framework/skills/fw-tests/SKILL.md +169 -0
  187. package/.koolie/core/framework/skills/fw-tests/TESTS.md +12 -0
  188. package/.koolie/core/framework/tech-packs/README.md +15 -0
  189. package/.koolie/core/framework/tech-packs/_template/TECH_PACK.md +61 -0
  190. package/.koolie/core/governance/ADOPTION_REGISTRY.md +89 -0
  191. package/.koolie/core/governance/CHANGE_REQUEST_TEMPLATE.md +60 -0
  192. package/.koolie/core/governance/DECISION_LOG.md +779 -0
  193. package/.koolie/core/governance/EXCEPTION_PROCESS.md +31 -0
  194. package/.koolie/core/governance/FEEDBACK_PROCESS.md +34 -0
  195. package/.koolie/core/governance/FRAMEWORK_DEV_PROFILE.md +135 -0
  196. package/.koolie/core/governance/INCIDENT_HANDLING.md +47 -0
  197. package/.koolie/core/governance/PRIORITY_HIERARCHY.md +61 -0
  198. package/.koolie/core/governance/RACI.md +37 -0
  199. package/.koolie/core/governance/RELEASE_PROCESS.md +183 -0
  200. package/.koolie/core/governance/change-requests/CR-2026-001-definition-release-1-0-0.md +67 -0
  201. package/.koolie/core/governance/change-requests/CR-2026-002-client-packs.md +63 -0
  202. package/.koolie/core/governance/change-requests/CR-2026-003-install-client-schalter.md +70 -0
  203. package/.koolie/core/governance/change-requests/CR-2026-004-client-pack-claude-code.md +82 -0
  204. package/.koolie/core/governance/change-requests/CR-2026-005-kern-neutralisierung.md +75 -0
  205. package/.koolie/core/governance/change-requests/CR-2026-006-gemeinsame-skill-quelle.md +77 -0
  206. package/.koolie/core/governance/change-requests/CR-2026-007-geteilte-kernbestandteile.md +80 -0
  207. package/.koolie/core/governance/change-requests/CR-2026-008-berechtigungen-und-hooks.md +85 -0
  208. package/.koolie/core/governance/change-requests/CR-2026-009-umbenennung-leitwerk.md +77 -0
  209. package/.koolie/core/governance/change-requests/CR-2026-010-restduplikation.md +73 -0
  210. package/.koolie/core/governance/change-requests/CR-2026-011-hauptdokument.md +72 -0
  211. package/.koolie/core/governance/change-requests/CR-2026-012-schreibschutz-kernverzeichnis.md +106 -0
  212. package/.koolie/core/governance/change-requests/CR-2026-013-validator-blindstellen.md +128 -0
  213. package/.koolie/core/governance/change-requests/CR-2026-014-kurzform-langform.md +125 -0
  214. package/.koolie/core/governance/change-requests/CR-2026-015-versionskette.md +150 -0
  215. package/.koolie/core/governance/change-requests/CR-2026-016-ap2-claude-code.md +127 -0
  216. package/.koolie/core/governance/change-requests/CR-2026-017-ladebedingungen-claude-code.md +173 -0
  217. package/.koolie/core/governance/change-requests/CR-2026-018-quellenliste-claude-code.md +121 -0
  218. package/.koolie/core/governance/change-requests/CR-2026-019-strukturentscheidungen-fortschreiben.md +143 -0
  219. package/.koolie/core/governance/change-requests/CR-2026-020-akteursbezeichnung.md +118 -0
  220. package/.koolie/core/governance/change-requests/CR-2026-021-hook-interpreter.md +113 -0
  221. package/.koolie/core/governance/change-requests/CR-2026-022-artefaktversionen.md +73 -0
  222. package/.koolie/core/governance/change-requests/CR-2026-023-shell-lesesperre.md +128 -0
  223. package/.koolie/core/governance/change-requests/CR-2026-024-clientbindungen.md +78 -0
  224. package/.koolie/core/governance/change-requests/CR-2026-025-betriebsmodi.md +110 -0
  225. package/.koolie/core/governance/change-requests/CR-2026-026-fail-closed.md +134 -0
  226. package/.koolie/core/governance/change-requests/CR-2026-027-zeichenlimit-einstufung.md +105 -0
  227. package/.koolie/core/governance/change-requests/CR-2026-028-berechtigungsmodi.md +130 -0
  228. package/.koolie/core/governance/change-requests/CR-2026-029-hook-ablageort.md +115 -0
  229. package/.koolie/core/governance/change-requests/CR-2026-030-lesende-werkzeuge.md +143 -0
  230. package/.koolie/core/governance/change-requests/CR-2026-031-regelquellen-ausserhalb.md +182 -0
  231. package/.koolie/core/governance/change-requests/CR-2026-032-fremde-skill-ablage.md +151 -0
  232. package/.koolie/core/governance/change-requests/CR-2026-033-modusabhaengige-durchsetzung.md +131 -0
  233. package/.koolie/core/governance/change-requests/CR-2026-034-nachweisbedingungen.md +116 -0
  234. package/.koolie/core/governance/change-requests/CR-2026-035-readme-in-der-regelablage.md +115 -0
  235. package/.koolie/core/governance/change-requests/CR-2026-036-ortsangaben-hook-konfiguration.md +108 -0
  236. package/.koolie/core/governance/change-requests/CR-2026-037-meldender-hook-clientgebunden.md +133 -0
  237. package/.koolie/core/governance/change-requests/CR-2026-038-importsteuerung.md +159 -0
  238. package/.koolie/core/governance/change-requests/CR-2026-039-normative-saetze-in-kommentaren.md +124 -0
  239. package/.koolie/core/governance/change-requests/CR-2026-040-erh01-ist-clientgebunden.md +115 -0
  240. package/.koolie/core/governance/change-requests/CR-2026-041-s5-nicht-abbildbar.md +128 -0
  241. package/.koolie/core/governance/change-requests/CR-2026-042-abwesenheitsnachweis-suchwerkzeug.md +102 -0
  242. package/.koolie/core/governance/change-requests/CR-2026-043-validator-gibt-trefferwerte-aus.md +135 -0
  243. package/.koolie/core/governance/change-requests/CR-2026-044-aktivierungspruefung-clientgebunden.md +112 -0
  244. package/.koolie/core/governance/change-requests/CR-2026-045-update-verliert-die-clientwahl.md +116 -0
  245. package/.koolie/core/governance/change-requests/CR-2026-046-erstinstallation-ueberschreibt-projektdatei.md +125 -0
  246. package/.koolie/core/governance/change-requests/CR-2026-047-zusagen-je-zugriffskanal.md +133 -0
  247. package/.koolie/core/governance/change-requests/CR-2026-048-modusgrenze-ohne-durchsetzung.md +137 -0
  248. package/.koolie/core/governance/change-requests/CR-2026-049-sondenlauf-kodierungsabhaengig.md +126 -0
  249. package/.koolie/core/governance/change-requests/CR-2026-050-allowed-tools-und-verworfene-permissions.md +110 -0
  250. package/.koolie/core/governance/change-requests/CR-2026-051-quellenkarte-und-ueberholter-stand.md +110 -0
  251. package/.koolie/core/governance/change-requests/CR-2026-052-drei-regelkonflikte.md +166 -0
  252. package/.koolie/core/governance/change-requests/CR-2026-053-lesesperre-und-entwicklungsprofil.md +144 -0
  253. package/.koolie/core/governance/change-requests/CR-2026-054-aktivierung-verlangt-aktivitaet.md +124 -0
  254. package/.koolie/core/governance/change-requests/CR-2026-055-domain-ausnahme-ohne-mechanismus.md +173 -0
  255. package/.koolie/core/governance/change-requests/CR-2026-056-eingabeschema-und-pfadidentitaet.md +245 -0
  256. package/.koolie/core/governance/change-requests/CR-2026-057-disallowed-tools-und-s3.md +148 -0
  257. package/.koolie/core/governance/change-requests/CR-2026-058-unteragent-reichweite.md +203 -0
  258. package/.koolie/core/governance/change-requests/CR-2026-059-unteragent-tiefe-und-widerspruch.md +125 -0
  259. package/.koolie/core/governance/change-requests/CR-2026-060-stumme-brueche.md +170 -0
  260. package/.koolie/core/governance/change-requests/CR-2026-061-berechtigungsdatei-ohne-pruefung.md +169 -0
  261. package/.koolie/core/governance/change-requests/CR-2026-062-werkzeugabbildung-des-frontmatters.md +178 -0
  262. package/.koolie/core/governance/change-requests/CR-2026-063-skillaufruf-ohne-freigabe.md +207 -0
  263. package/.koolie/core/governance/change-requests/CR-2026-064-pruefregister-nicht-nachgezaehlt.md +141 -0
  264. package/.koolie/core/governance/change-requests/CR-2026-065-devin-suchwerkzeuge-erhoben.md +170 -0
  265. package/.koolie/core/governance/change-requests/CR-2026-066-schlitzdeckung-ohne-abgleich.md +153 -0
  266. package/.koolie/core/governance/change-requests/CR-2026-067-hookblock-und-praeparationsregister.md +140 -0
  267. package/.koolie/core/governance/change-requests/CR-2026-068-sondenlauf-nebenlaeufig.md +106 -0
  268. package/.koolie/core/governance/change-requests/CR-2026-069-bytecode-des-kerns.md +108 -0
  269. package/.koolie/core/governance/change-requests/CR-2026-070-d11-zaehler.md +114 -0
  270. package/.koolie/core/governance/change-requests/CR-2026-071-strukturentscheidungen-bestaetigen.md +322 -0
  271. package/.koolie/core/governance/change-requests/CR-2026-072-modulstatus-heben.md +315 -0
  272. package/.koolie/core/governance/change-requests/CR-2026-073-nicht-skill-traeger-heben.md +246 -0
  273. package/.koolie/core/governance/change-requests/CR-2026-074-restliche-nicht-skill-traeger-heben.md +271 -0
  274. package/.koolie/core/governance/change-requests/CR-2026-075-ap2-zielversion-devin-desktop.md +335 -0
  275. package/.koolie/core/governance/change-requests/CR-2026-076-erster-sitzungstest.md +455 -0
  276. package/.koolie/core/governance/change-requests/CR-2026-077-sitzungstest-schranken.md +425 -0
  277. package/.koolie/core/governance/change-requests/CR-2026-078-planung-nach-1-0-0.md +294 -0
  278. package/.koolie/core/governance/change-requests/CR-2026-079-umbenennung-vorziehen.md +233 -0
  279. package/.koolie/core/governance/change-requests/CR-2026-080-clientbindung-des-kerns.md +321 -0
  280. package/.koolie/core/governance/change-requests/CR-2026-081-produktnamen-im-kern.md +297 -0
  281. package/.koolie/core/governance/change-requests/CR-2026-082-sitzungstest-pi-ds-2.md +145 -0
  282. package/.koolie/core/governance/change-requests/CR-2026-083-sitzungstest-ne-sc.md +300 -0
  283. package/.koolie/core/governance/change-requests/CR-2026-084-trockenlauf-gegen-den-committeten-stand.md +117 -0
  284. package/.koolie/core/governance/change-requests/CR-2026-085-vorbedingungen-sitzungstest-5.md +182 -0
  285. package/.koolie/core/governance/change-requests/CR-2026-086-grenzfaelle-gegen-die-fassungen.md +164 -0
  286. package/.koolie/core/governance/change-requests/CR-2026-087-produktbeobachtung-quellenliste.md +206 -0
  287. package/.koolie/core/governance/change-requests/CR-2026-088-vorbedingungen-testblaetter.md +127 -0
  288. package/.koolie/core/governance/change-requests/CR-2026-089-herrichtung-uebungsrepositorium.md +226 -0
  289. package/.koolie/core/governance/change-requests/CR-2026-090-vorbedingungen-sitzungstest-5-zweiter-durchgang.md +255 -0
  290. package/.koolie/core/governance/change-requests/CR-2026-091-sitzungstest-5.md +163 -0
  291. package/.koolie/core/governance/change-requests/CR-2026-092-pruefmittelwort-und-buendelschnitt.md +226 -0
  292. package/.koolie/core/governance/change-requests/CR-2026-093-sechzehnte-praeparation.md +151 -0
  293. package/.koolie/core/governance/change-requests/CR-2026-094-testblaetter-buendel-1.md +113 -0
  294. package/.koolie/core/governance/change-requests/CR-2026-095-pruefapparat-filter.md +65 -0
  295. package/.koolie/core/governance/change-requests/CR-2026-096-vorbedingungen-buendel-2.md +225 -0
  296. package/.koolie/core/governance/change-requests/CR-2026-097-testblaetter-buendel-2.md +162 -0
  297. package/.koolie/core/governance/change-requests/CR-2026-098-anforderungen-auslieferung.md +93 -0
  298. package/.koolie/core/governance/change-requests/CR-2026-099-k74-und-vorbedingungen-buendel-3.md +119 -0
  299. package/.koolie/core/governance/change-requests/CR-2026-100-testblaetter-buendel-3.md +120 -0
  300. package/.koolie/core/governance/change-requests/CR-2026-101-k77-eigener-posten.md +165 -0
  301. package/.koolie/core/governance/change-requests/CR-2026-102-k77-entschieden.md +118 -0
  302. package/.koolie/core/governance/change-requests/CR-2026-103-vorbedingungen-buendel-4.md +76 -0
  303. package/.koolie/core/governance/change-requests/CR-2026-104-herrichtung-buendel-4.md +82 -0
  304. package/.koolie/core/governance/change-requests/CR-2026-105-messapparat-buendel-4.md +227 -0
  305. package/.koolie/core/governance/change-requests/CR-2026-106-uebergabe-einchecken.md +91 -0
  306. package/.koolie/core/governance/change-requests/CR-2026-107-uebergabe-im-release-commit.md +111 -0
  307. package/.koolie/core/governance/change-requests/CR-2026-108-testblaetter-buendel-4.md +118 -0
  308. package/.koolie/core/governance/change-requests/CR-2026-109-berichtsweg-des-aufraeumers.md +108 -0
  309. package/.koolie/core/governance/change-requests/CR-2026-110-vorbedingungen-des-nachlaufs.md +184 -0
  310. package/.koolie/core/governance/change-requests/CR-2026-111-der-apparat-auf-dem-weg-der-wiederaufnahme.md +127 -0
  311. package/.koolie/core/governance/change-requests/CR-2026-112-der-arbeitsplatz-im-kern.md +103 -0
  312. package/.koolie/core/governance/change-requests/CR-2026-113-nachlauf-buendel-4.md +103 -0
  313. package/.koolie/core/governance/change-requests/CR-2026-114-vorbedingungen-buendel-5.md +317 -0
  314. package/.koolie/core/governance/change-requests/CR-2026-115-herrichtung-buendel-5.md +244 -0
  315. package/.koolie/core/governance/change-requests/CR-2026-116-messtag-buendel-5.md +178 -0
  316. package/.koolie/core/governance/change-requests/CR-2026-117-sammelzellen-zentraler-katalog.md +251 -0
  317. package/.koolie/core/governance/change-requests/CR-2026-118-quellenzuordnung-matrixzeilen.md +115 -0
  318. package/.koolie/core/governance/change-requests/CR-2026-119-umbenennung-koolie.md +204 -0
  319. package/.koolie/core/governance/change-requests/CR-2026-120-ap2-rest-devin-desktop.md +96 -0
  320. package/.koolie/core/governance/change-requests/CR-2026-121-verify-marker-abschaffen.md +185 -0
  321. package/.koolie/core/governance/change-requests/CR-2026-122-umbenennung-koolie-lauf.md +131 -0
  322. package/.koolie/core/governance/change-requests/CR-2026-123-vortrag-und-namensabsatz.md +170 -0
  323. package/.koolie/core/governance/change-requests/CR-2026-124-hauptdokument-ap11.md +237 -0
  324. package/.koolie/core/governance/change-requests/CR-2026-125-word-fassung-und-blinder-fleck.md +204 -0
  325. package/.koolie/core/governance/change-requests/CR-2026-126-lizenz-gpl3.md +112 -0
  326. package/.koolie/core/governance/change-requests/CR-2026-127-gegenzeichnung-rollenfrage.md +96 -0
  327. package/.koolie/core/governance/change-requests/CR-2026-128-freigabelauf-1.0.0.md +278 -0
  328. package/.koolie/core/governance/change-requests/CR-2026-129-archiv-zeilenenden.md +90 -0
  329. package/.koolie/core/governance/change-requests/CR-2026-130-reihenfolge-des-hebens.md +204 -0
  330. package/.koolie/core/governance/change-requests/CR-2026-131-chronik-und-vorlage.md +194 -0
  331. package/.koolie/core/governance/change-requests/CR-2026-132-erhebung-openai-codex.md +185 -0
  332. package/.koolie/core/governance/change-requests/CR-2026-133-bau-openai-codex.md +97 -0
  333. package/.koolie/core/governance/change-requests/CR-2026-134-uebergabe-lokal.md +117 -0
  334. package/.koolie/core/governance/change-requests/CR-2026-135-pfadlisten-nul-getrennt.md +139 -0
  335. package/.koolie/core/governance/change-requests/CR-2026-136-overlay-werte-einordnung.md +156 -0
  336. package/.koolie/core/governance/change-requests/CR-2026-137-kopierweg-kern.md +144 -0
  337. package/.koolie/core/governance/change-requests/CR-2026-138-overlay-muster-general.md +118 -0
  338. package/.koolie/core/governance/change-requests/CR-2026-139-overlay-muster-dokumente.md +105 -0
  339. package/.koolie/core/governance/change-requests/CR-2026-140-installer-je-zielsystem.md +120 -0
  340. package/.koolie/core/governance/change-requests/CR-2026-141-lieferumfang.md +123 -0
  341. package/.koolie/core/governance/change-requests/CR-2026-142-dokumentationsstandard.md +90 -0
  342. package/.koolie/core/governance/change-requests/CR-2026-143-klasse-b-core-laufzeit.md +89 -0
  343. package/.koolie/core/governance/change-requests/CR-2026-144-klasse-b-governance.md +90 -0
  344. package/.koolie/core/governance/change-requests/CR-2026-145-klasse-b-skills.md +81 -0
  345. package/.koolie/core/governance/change-requests/CR-2026-146-code-pack-posten.md +84 -0
  346. package/.koolie/core/governance/change-requests/CR-2026-147-regel-register-posten.md +78 -0
  347. package/.koolie/core/governance/change-requests/CR-2026-148-mehrprojekt-tokenlast.md +70 -0
  348. package/.koolie/core/governance/change-requests/CR-2026-149-regelablage-pfadtoken.md +71 -0
  349. package/.koolie/core/governance/change-requests/CR-2026-150-client-pack-kiro.md +76 -0
  350. package/.koolie/core/governance/change-requests/CR-2026-151-skill-anweisungen.md +71 -0
  351. package/.koolie/core/governance/change-requests/CR-2026-152-testblaetter-modellwechsel.md +78 -0
  352. package/.koolie/core/governance/change-requests/CR-2026-153-attributionszeile.md +88 -0
  353. package/.koolie/core/governance/change-requests/CR-2026-154-auffindbarkeit.md +102 -0
  354. package/.koolie/core/governance/change-requests/CR-2026-155-client-pack-cursor.md +80 -0
  355. package/.koolie/core/governance/change-requests/CR-2026-156-mandat-und-reibung.md +100 -0
  356. package/.koolie/core/governance/change-requests/CR-2026-157-mcp-anbindung.md +93 -0
  357. package/.koolie/core/governance/change-requests/CR-2026-158-registerpflege.md +69 -0
  358. package/.koolie/core/governance/change-requests/CR-2026-159-powershell.md +49 -0
  359. package/.koolie/core/governance/change-requests/CR-2026-160-messapparat.md +59 -0
  360. package/.koolie/core/governance/change-requests/CR-2026-161-pruefwerkzeuge.md +52 -0
  361. package/.koolie/core/governance/change-requests/CR-2026-162-schutzschicht.md +65 -0
  362. package/.koolie/core/governance/change-requests/CR-2026-163-pfadsemantik.md +61 -0
  363. package/.koolie/core/governance/change-requests/CR-2026-164-modi-ausnahmen.md +65 -0
  364. package/.koolie/core/governance/change-requests/CR-2026-165-installer-banner.md +406 -0
  365. package/.koolie/core/governance/change-requests/CR-2026-166-nachlauf-aufzeichnungen.md +59 -0
  366. package/.koolie/core/governance/change-requests/CR-2026-167-einsatzarchitektur.md +60 -0
  367. package/.koolie/core/governance/change-requests/CR-2026-168-paketquellen.md +64 -0
  368. package/.koolie/core/governance/change-requests/CR-2026-169-modusbindung-messfragen.md +58 -0
  369. package/.koolie/core/governance/change-requests/CR-2026-170-erste-veroeffentlichung.md +62 -0
  370. package/.koolie/core/governance/change-requests/CR-2026-171-befehl-im-projektverzeichnis.md +57 -0
  371. package/.koolie/core/governance/change-requests/CR-2026-172-auftritt-npm-suche.md +58 -0
  372. package/.koolie/core/install.py +2402 -0
  373. package/.koolie/core/install_dialog.py +227 -0
  374. package/.koolie/core/koexistenz.py +112 -0
  375. package/.koolie/core/mandat.py +605 -0
  376. package/.koolie/core/onboarding/COMPLETION_CRITERIA.md +37 -0
  377. package/.koolie/core/onboarding/GUIDE.md +103 -0
  378. package/.koolie/core/onboarding/KNOWLEDGE_CHECK.md +48 -0
  379. package/.koolie/core/onboarding/MENTOR_CHECKLIST.md +40 -0
  380. package/.koolie/core/onboarding/QUICKSTART.md +50 -0
  381. package/.koolie/core/onboarding/REFERENCE.md +69 -0
  382. package/.koolie/core/onboarding/exercises/EXERCISES.md +78 -0
  383. package/.koolie/core/onboarding/exercises/README.md +120 -0
  384. package/.koolie/core/pilot/METRICS.md +59 -0
  385. package/.koolie/core/pilot/PILOT_CONCEPT.md +42 -0
  386. package/.koolie/core/prompts/01-understand-codebase.md +99 -0
  387. package/.koolie/core/prompts/02-impact-analysis.md +104 -0
  388. package/.koolie/core/prompts/03-implementation-planning.md +102 -0
  389. package/.koolie/core/prompts/04-code-generation.md +100 -0
  390. package/.koolie/core/prompts/05-test-generation.md +103 -0
  391. package/.koolie/core/prompts/06-refactoring.md +102 -0
  392. package/.koolie/core/prompts/07-debugging.md +97 -0
  393. package/.koolie/core/prompts/08-security-review.md +92 -0
  394. package/.koolie/core/prompts/09-performance-analysis.md +89 -0
  395. package/.koolie/core/prompts/10-documentation.md +91 -0
  396. package/.koolie/core/prompts/11-merge-request-review.md +94 -0
  397. package/.koolie/core/prompts/12-developer-training.md +90 -0
  398. package/.koolie/core/prompts/README.md +88 -0
  399. package/.koolie/core/templates/MR_AI_DISCLOSURE.md +37 -0
  400. package/.koolie/core/templates/PLAN_TEMPLATE.md +57 -0
  401. package/.koolie/core/templates/SKILL_TEMPLATE.md +159 -0
  402. package/.koolie/core/templates/project-overlay/OVERLAY.md +315 -0
  403. package/.koolie/core/templates/project-overlay/documents/README.md +43 -0
  404. package/.koolie/core/templates/project-overlay/documents/ai-governance/.gitkeep.md +3 -0
  405. package/.koolie/core/templates/project-overlay/documents/ai-process-model/.gitkeep.md +3 -0
  406. package/.koolie/core/templates/project-overlay/documents/architecture/.gitkeep.md +3 -0
  407. package/.koolie/core/templates/project-overlay/documents/architecture/decisions/README.md +3 -0
  408. package/.koolie/core/templates/project-overlay/documents/branching-strategy/.gitkeep.md +3 -0
  409. package/.koolie/core/templates/project-overlay/documents/coding-guidelines/.gitkeep.md +3 -0
  410. package/.koolie/core/templates/project-overlay/documents/definition-of-done/.gitkeep.md +3 -0
  411. package/.koolie/core/templates/project-overlay/documents/definition-of-ready/.gitkeep.md +3 -0
  412. package/.koolie/core/templates/project-overlay/documents/deployment/.gitkeep.md +3 -0
  413. package/.koolie/core/templates/project-overlay/documents/glossary/.gitkeep.md +3 -0
  414. package/.koolie/core/templates/project-overlay/documents/quality/.gitkeep.md +3 -0
  415. package/.koolie/core/templates/project-overlay/documents/roadmap/.gitkeep.md +3 -0
  416. package/.koolie/core/templates/project-overlay/documents/roles/.gitkeep.md +3 -0
  417. package/.koolie/core/templates/project-overlay/documents/security/.gitkeep.md +3 -0
  418. package/.koolie/core/templates/project-overlay/exceptions/EXCEPTIONS.md +9 -0
  419. package/.koolie/core/templates/project-overlay/forbidden-terms.txt +8 -0
  420. package/.koolie/core/templates/project-overlay/overlay-manifest.yaml +73 -0
  421. package/.koolie/core/templates/rules/21-overlay-TEMPLATE.md.template +28 -0
  422. package/.koolie/core/templates/rules/40-tech-TEMPLATE.md.template +33 -0
  423. package/.koolie/core/tests/EDGE_CASES.md +67 -0
  424. package/.koolie/core/tests/TEST_CATALOG.md +170 -0
  425. package/.koolie/core/tests/erhebungen/README.md +326 -0
  426. package/.koolie/core/tests/erhebungen/ablage.py +357 -0
  427. package/.koolie/core/tests/erhebungen/apparat/__init__.py +22 -0
  428. package/.koolie/core/tests/erhebungen/apparat/baum.py +214 -0
  429. package/.koolie/core/tests/erhebungen/apparat/belege.py +91 -0
  430. package/.koolie/core/tests/erhebungen/apparat/clients.py +321 -0
  431. package/.koolie/core/tests/erhebungen/apparat/freigabe.py +74 -0
  432. package/.koolie/core/tests/erhebungen/apparat/kontingent.py +64 -0
  433. package/.koolie/core/tests/erhebungen/apparat/laeufer.py +155 -0
  434. package/.koolie/core/tests/erhebungen/apparat/reihe.py +202 -0
  435. package/.koolie/core/tests/erhebungen/apparat/selbsttest.py +280 -0
  436. package/.koolie/core/tests/erhebungen/apparat/stand.py +30 -0
  437. package/.koolie/core/tests/erhebungen/apparat/vorpruefung.py +70 -0
  438. package/.koolie/core/tests/erhebungen/auswerten-b4.py +468 -0
  439. package/.koolie/core/tests/erhebungen/auswerten-b5.py +517 -0
  440. package/.koolie/core/tests/erhebungen/auswerten-dd.py +172 -0
  441. package/.koolie/core/tests/erhebungen/baeume-b4.py +334 -0
  442. package/.koolie/core/tests/erhebungen/baeume-b5.py +339 -0
  443. package/.koolie/core/tests/erhebungen/baeume_loeschen.py +138 -0
  444. package/.koolie/core/tests/erhebungen/cc-overlay-fuellen.py +277 -0
  445. package/.koolie/core/tests/erhebungen/dossier-b4.py +178 -0
  446. package/.koolie/core/tests/erhebungen/dossier-b5.py +202 -0
  447. package/.koolie/core/tests/erhebungen/historie-bauen-b4.py +642 -0
  448. package/.koolie/core/tests/erhebungen/k-bauen-b3.py +841 -0
  449. package/.koolie/core/tests/erhebungen/lauf-dd.py +146 -0
  450. package/.koolie/core/tests/erhebungen/lauf.py +114 -0
  451. package/.koolie/core/tests/erhebungen/mcp-waechter.py +278 -0
  452. package/.koolie/core/tests/erhebungen/messbaum-schnitt.py +256 -0
  453. package/.koolie/core/tests/erhebungen/messen.py +72 -0
  454. package/.koolie/core/tests/erhebungen/node-waechter.py +74 -0
  455. package/.koolie/core/tests/erhebungen/packaktivierung.py +403 -0
  456. package/.koolie/core/tests/erhebungen/prompts-schreiben-b4.py +210 -0
  457. package/.koolie/core/tests/erhebungen/prompts-schreiben-b5.py +251 -0
  458. package/.koolie/core/tests/erhebungen/reihe-b4.py +171 -0
  459. package/.koolie/core/tests/erhebungen/reihe-b5.py +172 -0
  460. package/.koolie/core/tests/erhebungen/stand-b4.py +135 -0
  461. package/.koolie/core/tests/erhebungen/stand-b5.py +137 -0
  462. package/.koolie/core/tests/erhebungen/trust-b4.py +56 -0
  463. package/.koolie/core/tests/erhebungen/trust-b5.py +56 -0
  464. package/.koolie/core/tests/erhebungen/turn2-schreiben-b4.py +105 -0
  465. package/.koolie/core/tests/erhebungen/umgebungen-bauen-b4.py +377 -0
  466. package/.koolie/core/tests/erhebungen/umgebungen-bauen-b5.py +363 -0
  467. package/.koolie/core/tests/erhebungen/zaehlen46.py +74 -0
  468. package/.koolie/core/tests/erhebungen/zustand-b4.py +82 -0
  469. package/.koolie/core/tests/erhebungen/zustand-b5.py +82 -0
  470. package/.koolie/core/tests/protocols/2026-09-10-AP2-claude-code-wirkungsnachweise.md +194 -0
  471. package/.koolie/core/tests/protocols/2026-09-10-AP2-claude-code.md +468 -0
  472. package/.koolie/core/tests/protocols/2026-09-10-CR-2026-020-akteursbezeichnung.md +99 -0
  473. package/.koolie/core/tests/protocols/2026-09-10-CR-2026-021-hook-interpreter.md +138 -0
  474. package/.koolie/core/tests/protocols/2026-09-10-CR-2026-022-artefaktversionen.md +87 -0
  475. package/.koolie/core/tests/protocols/2026-09-10-CR-2026-023-shell-lesesperre.md +135 -0
  476. package/.koolie/core/tests/protocols/2026-09-10-CR-2026-024-clientbindungen.md +98 -0
  477. package/.koolie/core/tests/protocols/2026-09-10-FW-DS-03.md +90 -0
  478. package/.koolie/core/tests/protocols/2026-09-10-FW-KO-01.md +105 -0
  479. package/.koolie/core/tests/protocols/2026-09-10-FW-KO-02.md +123 -0
  480. package/.koolie/core/tests/protocols/2026-09-10-FW-KO-04.md +73 -0
  481. package/.koolie/core/tests/protocols/2026-09-10-FW-RE-02.md +99 -0
  482. package/.koolie/core/tests/protocols/2026-09-10-FW-VN-01-wiederholung.md +108 -0
  483. package/.koolie/core/tests/protocols/2026-09-10-FW-VN-01.md +249 -0
  484. package/.koolie/core/tests/protocols/2026-09-10-FW-ZA-05.md +99 -0
  485. package/.koolie/core/tests/protocols/2026-09-11-AP2-devin-desktop.md +257 -0
  486. package/.koolie/core/tests/protocols/2026-09-11-CR-2026-026-fail-closed.md +113 -0
  487. package/.koolie/core/tests/protocols/2026-09-11-erhebungen-K21-K26.md +243 -0
  488. package/.koolie/core/tests/protocols/2026-09-11-wirkungsnachweise-0.26.0.md +101 -0
  489. package/.koolie/core/tests/protocols/2026-09-12-B01-allowed-tools.md +152 -0
  490. package/.koolie/core/tests/protocols/2026-09-12-B04-B05-gegenpruefung.md +204 -0
  491. package/.koolie/core/tests/protocols/2026-09-12-erhebungen-K28-S5-B9-bypass.md +268 -0
  492. package/.koolie/core/tests/protocols/2026-09-12-mehrprojekt-arbeitsbereich.md +154 -0
  493. package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.26.1.md +126 -0
  494. package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.27.0.md +114 -0
  495. package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.28.0.md +129 -0
  496. package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.29.0.md +121 -0
  497. package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.30.0.md +112 -0
  498. package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.31.0.md +107 -0
  499. package/.koolie/core/tests/protocols/2026-09-13-B06-gegenpruefung.md +370 -0
  500. package/.koolie/core/tests/protocols/2026-09-13-B07-B09-gegenpruefung.md +211 -0
  501. package/.koolie/core/tests/protocols/2026-09-13-B08-B11-gegenpruefung.md +179 -0
  502. package/.koolie/core/tests/protocols/2026-09-13-erhebung-disallowed-tools.md +147 -0
  503. package/.koolie/core/tests/protocols/2026-09-13-erhebung-unteragent-tiefe.md +139 -0
  504. package/.koolie/core/tests/protocols/2026-09-13-erhebung-unteragent.md +214 -0
  505. package/.koolie/core/tests/protocols/2026-09-13-gegenpruefung-berechtigungsdatei.md +253 -0
  506. package/.koolie/core/tests/protocols/2026-09-13-gegenpruefung-stumme-brueche.md +202 -0
  507. package/.koolie/core/tests/protocols/2026-09-13-gegenpruefung-werkzeugabbildung.md +219 -0
  508. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.32.0.md +133 -0
  509. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.33.0.md +169 -0
  510. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.34.0.md +187 -0
  511. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.35.0.md +187 -0
  512. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.36.0.md +137 -0
  513. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.37.0.md +112 -0
  514. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.38.0.md +154 -0
  515. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.39.0.md +188 -0
  516. package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.40.0.md +214 -0
  517. package/.koolie/core/tests/protocols/2026-09-14-erhebung-devin-werkzeuge.md +267 -0
  518. package/.koolie/core/tests/protocols/2026-09-14-erhebung-skillaufruf.md +238 -0
  519. package/.koolie/core/tests/protocols/2026-09-14-gegenpruefung-pruefregister.md +156 -0
  520. package/.koolie/core/tests/protocols/2026-09-14-gegenpruefung-schlitzdeckung.md +206 -0
  521. package/.koolie/core/tests/protocols/2026-09-14-gegenpruefung-skillwahl.md +127 -0
  522. package/.koolie/core/tests/protocols/2026-09-14-migrationslauf-pilot.md +134 -0
  523. package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.41.0.md +175 -0
  524. package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.42.0.md +180 -0
  525. package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.43.0.md +165 -0
  526. package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.44.0.md +130 -0
  527. package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-d11-zaehlregeln.md +311 -0
  528. package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-modulstatus.md +524 -0
  529. package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-nicht-skill-traeger.md +225 -0
  530. package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-restliche-nicht-skill-traeger.md +345 -0
  531. package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-strukturentscheidungen.md +392 -0
  532. package/.koolie/core/tests/protocols/2026-09-15-herrichtung-uebungsrepositorium.md +213 -0
  533. package/.koolie/core/tests/protocols/2026-09-15-migrationslauf-pilot-0.46.0.md +143 -0
  534. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.45.0.md +127 -0
  535. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.46.0.md +193 -0
  536. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.47.0.md +149 -0
  537. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.49.0.md +202 -0
  538. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.50.0.md +183 -0
  539. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.51.0.md +202 -0
  540. package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.52.0.md +168 -0
  541. package/.koolie/core/tests/protocols/2026-09-16-AP2-zielversion-devin-desktop.md +231 -0
  542. package/.koolie/core/tests/protocols/2026-09-16-wirkungsnachweise-0.53.0.md +350 -0
  543. package/.koolie/core/tests/protocols/2026-09-17-nachtrag-migrationshinweis-0.54.1.md +121 -0
  544. package/.koolie/core/tests/protocols/2026-09-17-sitzungstest-pi-ds.md +523 -0
  545. package/.koolie/core/tests/protocols/2026-09-17-sitzungstest-schranken.md +433 -0
  546. package/.koolie/core/tests/protocols/2026-09-17-wirkungsnachweise-0.54.0.md +159 -0
  547. package/.koolie/core/tests/protocols/2026-09-18-FW-AK-01.md +442 -0
  548. package/.koolie/core/tests/protocols/2026-09-18-FW-KO-05.md +231 -0
  549. package/.koolie/core/tests/protocols/2026-09-18-herrichtung-uebungsrepositorium.md +273 -0
  550. package/.koolie/core/tests/protocols/2026-09-18-nachtrag-preis-umbenennung-0.56.1.md +102 -0
  551. package/.koolie/core/tests/protocols/2026-09-18-sitzungstest-5.md +349 -0
  552. package/.koolie/core/tests/protocols/2026-09-18-sitzungstest-ne-sc.md +430 -0
  553. package/.koolie/core/tests/protocols/2026-09-18-sitzungstest-pi-ds-2.md +499 -0
  554. package/.koolie/core/tests/protocols/2026-09-18-vorbedingungen-sitzungstest-5-zweiter-durchgang.md +170 -0
  555. package/.koolie/core/tests/protocols/2026-09-18-vorbedingungen-testblaetter.md +184 -0
  556. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.55.0.md +202 -0
  557. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.56.0.md +135 -0
  558. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.56.2.md +154 -0
  559. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.57.0.md +228 -0
  560. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.57.1.md +262 -0
  561. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.58.0.md +135 -0
  562. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.59.0.md +150 -0
  563. package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.60.0.md +340 -0
  564. package/.koolie/core/tests/protocols/2026-09-19-herrichtung-buendel-4.md +235 -0
  565. package/.koolie/core/tests/protocols/2026-09-19-k77-zurechenbarkeit.md +236 -0
  566. package/.koolie/core/tests/protocols/2026-09-19-pruefapparat-filter.md +50 -0
  567. package/.koolie/core/tests/protocols/2026-09-19-pruefmittelwort-und-buendelschnitt.md +239 -0
  568. package/.koolie/core/tests/protocols/2026-09-19-sechzehnte-praeparation.md +135 -0
  569. package/.koolie/core/tests/protocols/2026-09-19-testblaetter-buendel-1.md +250 -0
  570. package/.koolie/core/tests/protocols/2026-09-19-testblaetter-buendel-2.md +229 -0
  571. package/.koolie/core/tests/protocols/2026-09-19-testblaetter-buendel-3.md +293 -0
  572. package/.koolie/core/tests/protocols/2026-09-19-vorbedingungen-buendel-2.md +236 -0
  573. package/.koolie/core/tests/protocols/2026-09-19-vorbedingungen-buendel-3.md +253 -0
  574. package/.koolie/core/tests/protocols/2026-09-19-vorbedingungen-buendel-4.md +138 -0
  575. package/.koolie/core/tests/protocols/2026-09-20-messapparat-buendel-4.md +433 -0
  576. package/.koolie/core/tests/protocols/2026-09-20-testblaetter-buendel-4.md +321 -0
  577. package/.koolie/core/tests/protocols/2026-09-20-uebergabe-im-release-commit.md +146 -0
  578. package/.koolie/core/tests/protocols/2026-09-20-vorbedingungen-nachlauf-b4.md +259 -0
  579. package/.koolie/core/tests/protocols/2026-09-21-arbeitsplatz-im-kern.md +127 -0
  580. package/.koolie/core/tests/protocols/2026-09-21-nachlauf-buendel-4.md +157 -0
  581. package/.koolie/core/tests/protocols/2026-09-21-vorbedingungen-buendel-5.md +247 -0
  582. package/.koolie/core/tests/protocols/2026-09-21-wiederaufnahme-nachlauf-b4.md +190 -0
  583. package/.koolie/core/tests/protocols/2026-09-22-ap2-rest-devin-desktop.md +614 -0
  584. package/.koolie/core/tests/protocols/2026-09-22-hauptdokument-ap11.md +443 -0
  585. package/.koolie/core/tests/protocols/2026-09-22-herrichtung-buendel-5.md +224 -0
  586. package/.koolie/core/tests/protocols/2026-09-22-messtag-buendel-5.md +327 -0
  587. package/.koolie/core/tests/protocols/2026-09-22-quellenzuordnung-matrixzeilen.md +230 -0
  588. package/.koolie/core/tests/protocols/2026-09-22-sammelzellen-zentraler-katalog.md +354 -0
  589. package/.koolie/core/tests/protocols/2026-09-22-umbenennung-koolie.md +296 -0
  590. package/.koolie/core/tests/protocols/2026-09-22-verify-marker-abschaffen.md +283 -0
  591. package/.koolie/core/tests/protocols/2026-09-22-vortrag-und-name.md +261 -0
  592. package/.koolie/core/tests/protocols/2026-09-22-wirkungsnachweise-0.86.0.md +158 -0
  593. package/.koolie/core/tests/protocols/2026-09-23-archiv-zeilenenden.md +113 -0
  594. package/.koolie/core/tests/protocols/2026-09-23-bau-openai-codex.md +168 -0
  595. package/.koolie/core/tests/protocols/2026-09-23-chronik-und-vorlage.md +224 -0
  596. package/.koolie/core/tests/protocols/2026-09-23-erhebung-openai-codex.md +309 -0
  597. package/.koolie/core/tests/protocols/2026-09-23-freigabelauf-1.0.0-vorbereitung.md +119 -0
  598. package/.koolie/core/tests/protocols/2026-09-23-freigabelauf-1.0.0.md +150 -0
  599. package/.koolie/core/tests/protocols/2026-09-23-gegenzeichnung-rollenfrage.md +198 -0
  600. package/.koolie/core/tests/protocols/2026-09-23-reihenfolge-des-hebens.md +220 -0
  601. package/.koolie/core/tests/protocols/2026-09-23-word-fassung-und-blinder-fleck.md +307 -0
  602. package/.koolie/core/tests/protocols/2026-09-24-kopierweg-kern.md +98 -0
  603. package/.koolie/core/tests/protocols/2026-09-24-overlay-werte-einordnung.md +111 -0
  604. package/.koolie/core/tests/protocols/2026-09-24-pfadlisten-nul-getrennt.md +128 -0
  605. package/.koolie/core/tests/protocols/2026-09-24-uebergabe-lokal.md +85 -0
  606. package/.koolie/core/tests/protocols/2026-09-25-code-pack-posten.md +86 -0
  607. package/.koolie/core/tests/protocols/2026-09-25-dokumentationsstandard.md +101 -0
  608. package/.koolie/core/tests/protocols/2026-09-25-installer-je-zielsystem.md +134 -0
  609. package/.koolie/core/tests/protocols/2026-09-25-klasse-b-core-laufzeit.md +126 -0
  610. package/.koolie/core/tests/protocols/2026-09-25-klasse-b-governance.md +133 -0
  611. package/.koolie/core/tests/protocols/2026-09-25-klasse-b-skills.md +91 -0
  612. package/.koolie/core/tests/protocols/2026-09-25-lieferumfang.md +76 -0
  613. package/.koolie/core/tests/protocols/2026-09-25-overlay-muster-dokumente.md +107 -0
  614. package/.koolie/core/tests/protocols/2026-09-25-overlay-muster-general.md +100 -0
  615. package/.koolie/core/tests/protocols/2026-09-25-regel-register-posten.md +87 -0
  616. package/.koolie/core/tests/protocols/2026-09-26-attributionszeile.md +83 -0
  617. package/.koolie/core/tests/protocols/2026-09-26-auffindbarkeit.md +85 -0
  618. package/.koolie/core/tests/protocols/2026-09-26-bau-cursor.md +68 -0
  619. package/.koolie/core/tests/protocols/2026-09-26-bau-kiro.md +75 -0
  620. package/.koolie/core/tests/protocols/2026-09-26-mehrprojekt-tokenlast.md +174 -0
  621. package/.koolie/core/tests/protocols/2026-09-26-regelablage-pfadtoken.md +133 -0
  622. package/.koolie/core/tests/protocols/2026-09-26-skill-anweisungen.md +111 -0
  623. package/.koolie/core/tests/protocols/2026-09-26-testblaetter-modellwechsel.md +106 -0
  624. package/.koolie/core/tests/protocols/2026-09-27-mandat-und-reibung.md +58 -0
  625. package/.koolie/core/tests/protocols/2026-09-28-mcp-anbindung.md +72 -0
  626. package/.koolie/core/tests/protocols/2026-09-29-messapparat.md +75 -0
  627. package/.koolie/core/tests/protocols/2026-09-29-powershell.md +58 -0
  628. package/.koolie/core/tests/protocols/2026-09-29-pruefwerkzeuge.md +64 -0
  629. package/.koolie/core/tests/protocols/2026-09-29-registerpflege.md +228 -0
  630. package/.koolie/core/tests/protocols/2026-09-29-schutzschicht.md +95 -0
  631. package/.koolie/core/tests/protocols/2026-09-30-einsatzarchitektur.md +104 -0
  632. package/.koolie/core/tests/protocols/2026-09-30-modi-ausnahmen.md +77 -0
  633. package/.koolie/core/tests/protocols/2026-09-30-nachlauf-banner.md +85 -0
  634. package/.koolie/core/tests/protocols/2026-09-30-paketquellen.md +65 -0
  635. package/.koolie/core/tests/protocols/2026-09-30-pfadsemantik.md +79 -0
  636. package/.koolie/core/tests/protocols/2026-10-01-auftritt-und-suche.md +58 -0
  637. package/.koolie/core/tests/protocols/2026-10-01-befehl-im-projektverzeichnis.md +47 -0
  638. package/.koolie/core/tests/protocols/2026-10-01-erste-veroeffentlichung.md +60 -0
  639. package/.koolie/core/tests/protocols/2026-10-01-modusbindung-messfragen.md +107 -0
  640. package/.koolie/core/tests/protocols/README.md +14 -0
  641. package/.koolie/core/tests/scripts/hook-check-secrets.py +1307 -0
  642. package/.koolie/core/tests/scripts/hook-overlay-status.py +120 -0
  643. package/.koolie/core/tests/scripts/mermaid_renderer.py +67 -0
  644. package/.koolie/core/tests/scripts/overlay_status.py +130 -0
  645. package/.koolie/core/tests/scripts/probe-pruefungen.py +110 -0
  646. package/.koolie/core/tests/scripts/pruefungen/__init__.py +4 -0
  647. package/.koolie/core/tests/scripts/pruefungen/berechtigungen.py +1559 -0
  648. package/.koolie/core/tests/scripts/pruefungen/bestand.py +1433 -0
  649. package/.koolie/core/tests/scripts/pruefungen/dokumente.py +1262 -0
  650. package/.koolie/core/tests/scripts/pruefungen/gemeinsam.py +784 -0
  651. package/.koolie/core/tests/scripts/pruefungen/hooks.py +1188 -0
  652. package/.koolie/core/tests/scripts/pruefungen/overlay.py +1251 -0
  653. package/.koolie/core/tests/scripts/pruefungen/packs.py +1314 -0
  654. package/.koolie/core/tests/scripts/pruefungen/register.py +950 -0
  655. package/.koolie/core/tests/scripts/pruefungen/testkatalog.py +1202 -0
  656. package/.koolie/core/tests/scripts/pruefungen/werkzeuge.py +957 -0
  657. package/.koolie/core/tests/scripts/sonden/__init__.py +4 -0
  658. package/.koolie/core/tests/scripts/sonden/apparat.py +653 -0
  659. package/.koolie/core/tests/scripts/sonden/teil01_grundbestand_und_installation.py +999 -0
  660. package/.koolie/core/tests/scripts/sonden/teil02_packs_mandat_mcp.py +942 -0
  661. package/.koolie/core/tests/scripts/sonden/teil03_pruefungen_26_bis_36.py +1184 -0
  662. package/.koolie/core/tests/scripts/sonden/teil04_pruefungen_37_bis_45.py +1491 -0
  663. package/.koolie/core/tests/scripts/sonden/teil05_pruefungen_46_bis_55.py +1447 -0
  664. package/.koolie/core/tests/scripts/sonden/teil06_pruefungen_57_bis_65.py +739 -0
  665. package/.koolie/core/tests/scripts/sonden/teil07_overlay_und_lieferung.py +1014 -0
  666. package/.koolie/core/tests/scripts/sonden/teil08_pruefungen_66_bis_80.py +1325 -0
  667. package/.koolie/core/tests/scripts/sonden/teil09_pruefmittel_81_und_82.py +505 -0
  668. package/.koolie/core/tests/scripts/sonden/teil10_pruefungen_83_bis_95.py +1218 -0
  669. package/.koolie/core/tests/scripts/sonden/teil11_pruefungen_104_und_105.py +149 -0
  670. package/.koolie/core/tests/scripts/sonden/teil12_pruefungen_106_und_107.py +136 -0
  671. package/.koolie/core/tests/scripts/sonden/teil13_pfad_und_mustersemantik.py +150 -0
  672. package/.koolie/core/tests/scripts/sonden/teil14_modi_ausnahmen_skills.py +204 -0
  673. package/.koolie/core/tests/scripts/sonden/teil15_banner_und_nachlauf.py +193 -0
  674. package/.koolie/core/tests/scripts/sonden/teil16_koexistenz.py +114 -0
  675. package/.koolie/core/tests/scripts/sonden/teil17_paketquellen.py +115 -0
  676. package/.koolie/core/tests/scripts/sonden/teil18_modusbindung_m3_m5.py +113 -0
  677. package/.koolie/core/tests/scripts/validate-framework.py +1104 -0
  678. package/.koolie/core/tests/scripts/validate-output.py +296 -0
  679. package/.koolie/core/wirksamkeit.py +430 -0
  680. package/CONTRIBUTING.md +119 -0
  681. package/LICENSE +674 -0
  682. package/QUICKSTART.en.md +135 -0
  683. package/QUICKSTART.md +129 -0
  684. package/README.en.md +108 -0
  685. package/README.md +107 -0
  686. package/install.cmd +44 -0
  687. package/install.command +46 -0
  688. package/package.json +21 -0
  689. package/paketquellen/README.md +41 -0
  690. package/paketquellen/bauen.py +411 -0
  691. package/paketquellen/koolie.cmd +23 -0
  692. package/paketquellen/koolie_befehl.py +82 -0
  693. package/paketquellen/npm/koolie.js +26 -0
@@ -0,0 +1,1251 @@
1
+ """Das Project Overlay: aktiver Zustand, Aktivierungsreife, ausgeschlossene Pfade,
2
+ Schlitze, Freigabefolge, Pflichtplatzhalter, Platzhalterbindung, Vorbedingungen,
3
+ Befehlsschlitz, Wert- und Pfadabgleich.
4
+
5
+ Pruefungen 9, 9a, 28, 51, 52, 55a, 55b, 56, 57, 59, 60, 89, 110 und 111. Teil des Validators
6
+ validate-framework.py, seit 1.19.1 nach Gegenstand in Module geteilt (K-174). Das
7
+ Register aller Pruefungen steht im Kopfkommentar des Einstiegs, die Grenze jeder
8
+ einzelnen in ihrem Kopfkommentar hier."""
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import os
13
+ import re
14
+
15
+ from overlay_status import (
16
+ AKTIV, auswerten, INAKTIV, status_angaben, UNBEKANNT, WIDERSPRUECHLICH)
17
+
18
+ from .gemeinsam import (
19
+ _tabellenspalte, _ueb_katalogdateien, err, formatgebunden, KERN, PLACEHOLDER_RE,
20
+ fremde_praefixe,
21
+ PROJEKTPLATZHALTER, read, regeldatei, tabellenzellen, TBD_RE, warn)
22
+
23
+
24
+ # Abschnitte des Overlays, die ueber die Aktivierungsreife entscheiden. Sie muessen da
25
+ # sein und sie muessen ausgefuellt sein - das eine ohne das andere ist keine Pruefung.
26
+ SICHERHEITSABSCHNITTE = ("## 4.", "## 5.", "## 6.", "## 13.", "## 14.", "## 15.")
27
+
28
+
29
+ def check_strict_overlay(root: str, man: dict) -> None:
30
+ """Der **aktive** Zustand des Projekts (--strict-overlay) - clientneutral.
31
+
32
+ Diese Funktion hiess bis 0.32.0 im Docstring und im Uebernahmeleitfaden "Pruefung der
33
+ Aktivierungsreife" und verlangte dabei den Status 'aktiv'. Das war der Kern von B08:
34
+ Der Leitfaden fuhr sie in Schritt 7 und setzte 'aktiv' erst in Schritt 9, die
35
+ Uebernahmecheckliste trug sie als MUSS und galt "vor dem Setzen auf aktiv" - eine
36
+ Voraussetzung, die sich selbst verlangte. Die Reifepruefung heisst seit 0.33.0
37
+ --check-overlay-ready und ist eine eigene Funktion (D-57). Diese hier prueft den
38
+ fertigen Zustand und bleibt dafuer unveraendert; sie hat mit dem
39
+ Aktualisierungsablauf einen zweiten, funktionierenden Aufrufer.
40
+
41
+ Bis 0.27.0 las diese Funktion zwei fest verdrahtete Pfade **eines** Clients und bekam
42
+ das erkannte Manifest nicht uebergeben. In einer Installation des anderen Packs fand
43
+ sie nichts, uebersprang alles und meldete null zusaetzliche Fehler.
44
+
45
+ Gemessen am 2026-09-12 an zwei frischen Installationen mit demselben Defekt: Beim
46
+ einen Pack aenderte sich die Fehlerzahl, beim anderen nicht (B02, D-44). Die
47
+ Clienterkennung steht seit der Umstellung auf mehrere Packs in derselben Datei; sie
48
+ wurde hier nur nicht benutzt.
49
+
50
+ Zwei Befunde derselben Funktion sind dabei mit erledigt, beide aus der Messung:
51
+
52
+ * **Der Status wurde als Praefix geprueft.** Damit bestand 'aktivierung-ausstehend'
53
+ die Aktivierungspruefung - ein Wert, der woertlich sagt, dass die Aktivierung
54
+ aussteht, liess den Fehler verschwinden, der vorher stand. Wer ihn eintrug, machte
55
+ die Pruefung stiller. Jetzt gilt genau 'aktiv'.
56
+ * **Ein fehlender sicherheitsrelevanter Abschnitt wurde akzeptiert.** Die Pruefung
57
+ suchte nur in vorhandenen Abschnitten nach offenen Werten; fehlte der Abschnitt,
58
+ fand sie nichts. Ein Overlay ohne Abschnitt 13 stand damit besser da als eines mit
59
+ einem offenen Wert darin - die Pruefung auf den Kopf gestellt.
60
+
61
+ GRENZE: Geprueft wird die Aktivierungs**reife**, nicht die Aktivierung. Dass ein
62
+ Overlay 'aktiv' sagt, heisst nicht, dass der Client seine Regeln laedt.
63
+ """
64
+ runtime = os.path.join(root, *man["pack_runtime_dir"].split("/"), regeldatei(man, "20-project-overlay.md"))
65
+ overlay = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
66
+ for path in (runtime, overlay):
67
+ if not os.path.exists(path):
68
+ continue
69
+ text = read(path)
70
+ rel = os.path.relpath(path, root).replace(os.sep, "/")
71
+ angaben = status_angaben(text)
72
+ status, grund = auswerten(angaben)
73
+ if status != AKTIV:
74
+ # Der **Rohwert** gehoert in die Meldung, nicht nur die Auswertung: Der Fall,
75
+ # der D-44 ausgeloest hat, war der Wert 'aktivierung-ausstehend' - er sagt
76
+ # woertlich, dass die Aktivierung aussteht, und genau das soll lesbar sein.
77
+ # Die Sonde zu D-44 hat diesen Verlust beim Umbau auf overlay_status.py
78
+ # gefangen; ohne sie waere die Meldung stiller geworden.
79
+ roh = ", ".join("'%s'" % a.strip() for a in angaben) or "keine Angabe"
80
+ err(f"{rel}: Overlay-Status ist nicht '{AKTIV}', sondern {roh} "
81
+ f"(ausgewertet als '{status}': {grund}) (strict-overlay)")
82
+ if path == runtime and TBD_RE.search(text):
83
+ err(f"{rel}: enthält offene <TBD>-Werte (strict-overlay)")
84
+ if path == overlay:
85
+ for sec in SICHERHEITSABSCHNITTE:
86
+ m = re.search(rf"{re.escape(sec)}.*?(?=\n## |\Z)", text, re.S)
87
+ if not m:
88
+ err(f"{rel}: sicherheitsrelevanter Abschnitt {sec} fehlt (strict-overlay)")
89
+ elif TBD_RE.search(m.group(0)):
90
+ err(f"{rel}: Abschnitt {sec} enthält offene <TBD>-Werte (sicherheitsrelevant, strict-overlay)")
91
+ rechte = os.path.join(root, *man["permissions_file"].split("/"))
92
+ if os.path.exists(rechte):
93
+ inhalt = read(rechte)
94
+ if TBD_RE.search(inhalt) or PLACEHOLDER_RE.search(inhalt):
95
+ err(f"{man['permissions_file']}: enthält noch Platzhalter (strict-overlay)")
96
+
97
+
98
+ def check_overlay_ready(root: str, man: dict) -> None:
99
+ """Aktivierungsreife eines Kandidaten (--check-overlay-ready) - clientneutral.
100
+
101
+ Die Pruefung, die B08 gefehlt hat: Sie prueft alles, was --strict-overlay prueft,
102
+ **ausser** dem Status - und beim Status erwartet sie das Gegenteil, naemlich einen
103
+ Kandidaten, der noch nicht aktiv ist. Damit ist der dokumentierte Ablauf ohne
104
+ Regelbruch begehbar: erst vollstaendig ausfuellen und pruefen, dann durch einen
105
+ Menschen aktivieren, dann mit --strict-overlay nachpruefen (D-57).
106
+
107
+ Warum sie einen aktiven Kandidaten als Fehler meldet und nicht durchlaesst: Sonst
108
+ waere sie die schwaechere Variante von --strict-overlay und wuerde als deren Ersatz
109
+ benutzt. Eine Pruefung, die zwei Zustaende gleich behandelt, unterscheidet keine zwei
110
+ Zustaende.
111
+
112
+ Die Statusangaben MUESSEN untereinander uebereinstimmen. Ein Kandidat, der in der
113
+ Laufzeitfassung 'aktiv' und im Quell-Overlay 'inaktiv' erklaert, ist kein Kandidat,
114
+ sondern eine Drift - derselbe Fall, den der Status-Hook bis 0.32.0 zugunsten der
115
+ ersten gelesenen Datei entschied.
116
+
117
+ GRENZE: Geprueft wird die Vollstaendigkeit der Konfiguration. Die **fachliche**
118
+ Freigabe kann kein Skript erteilen; die Uebernahmecheckliste bleibt der Nachweis
119
+ (FW-CL-10). Und dass ein Overlay vollstaendig ist, heisst nicht, dass seine Werte
120
+ richtig sind.
121
+ """
122
+ runtime = os.path.join(root, *man["pack_runtime_dir"].split("/"), regeldatei(man, "20-project-overlay.md"))
123
+ overlay = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
124
+ vorhanden = [p for p in (runtime, overlay) if os.path.exists(p)]
125
+ if not vorhanden:
126
+ err("check-overlay-ready: weder die Laufzeitfassung des Overlays noch "
127
+ ".koolie/project-overlay/OVERLAY.md ist vorhanden - es gibt keinen Kandidaten")
128
+ return
129
+
130
+ alle_angaben = []
131
+ for path in vorhanden:
132
+ text = read(path)
133
+ rel = os.path.relpath(path, root).replace(os.sep, "/")
134
+ angaben = status_angaben(text)
135
+ alle_angaben.extend(angaben)
136
+ if not angaben:
137
+ err(f"{rel}: keine Angabe zum Overlay-Status gefunden (check-overlay-ready)")
138
+ # Offene Werte: in der Laufzeitfassung ueberall, im Overlay in den
139
+ # sicherheitsrelevanten Abschnitten. Dieselbe Aufteilung wie --strict-overlay;
140
+ # ein Kandidat unterscheidet sich vom aktiven Overlay im Status, nicht im Inhalt.
141
+ if path == runtime and TBD_RE.search(text):
142
+ err(f"{rel}: enthält offene <TBD>-Werte (check-overlay-ready)")
143
+ if path == overlay:
144
+ for sec in SICHERHEITSABSCHNITTE:
145
+ m = re.search(rf"{re.escape(sec)}.*?(?=\n## |\Z)", text, re.S)
146
+ if not m:
147
+ err(f"{rel}: sicherheitsrelevanter Abschnitt {sec} fehlt "
148
+ f"(check-overlay-ready)")
149
+ elif TBD_RE.search(m.group(0)):
150
+ err(f"{rel}: Abschnitt {sec} enthält offene <TBD>-Werte "
151
+ f"(sicherheitsrelevant, check-overlay-ready)")
152
+
153
+ status, grund = auswerten(alle_angaben)
154
+ if status == AKTIV:
155
+ err(f"check-overlay-ready: Der Overlay-Status ist bereits '{AKTIV}'. Diese "
156
+ f"Pruefung gilt fuer einen Kandidaten **vor** der Aktivierung; fuer den "
157
+ f"aktiven Zustand ist --strict-overlay zustaendig (D-57)")
158
+ elif status == WIDERSPRUECHLICH:
159
+ err(f"check-overlay-ready: {grund}. Ein Kandidat erklaert seinen Status an allen "
160
+ f"Stellen gleich; abweichende Angaben sind eine Drift, kein Kandidat")
161
+ elif status == UNBEKANNT:
162
+ err(f"check-overlay-ready: Overlay-Status nicht ausgefuellt ({grund}). Erwartet "
163
+ f"wird '{INAKTIV}' - ein Kandidat sagt, dass er noch nicht aktiv ist, statt "
164
+ f"die Angabe offen zu lassen")
165
+
166
+ rechte = os.path.join(root, *man["permissions_file"].split("/"))
167
+ if os.path.exists(rechte):
168
+ inhalt = read(rechte)
169
+ if TBD_RE.search(inhalt) or PLACEHOLDER_RE.search(inhalt):
170
+ err(f"{man['permissions_file']}: enthält noch Platzhalter "
171
+ f"(check-overlay-ready)")
172
+ else:
173
+ err(f"{man['permissions_file']}: fehlt - ohne Berechtigungsdatei ist kein "
174
+ f"Kandidat vollstaendig (check-overlay-ready)")
175
+
176
+
177
+ # Pruefung 28: Ein Strukturpfad des Frameworks gehoert nicht in <EXCLUDED_PATHS>.
178
+ #
179
+ # Zwei Schutzziele, zwei Kategorien - der Schutz-Hook unterscheidet sie seit D-30 und die
180
+ # Berechtigungsdatei ebenfalls: <EXCLUDED_PATHS> wird dort zu einer 'read'- UND einer
181
+ # 'write'-Verweigerung, die Strukturpfade stehen ausschliesslich als 'write'-Verweigerung
182
+ # bei 'read allow **'. Die Overlay-Vorlage und die Laufzeitregel fuehrten sie bis 0.31.0
183
+ # unter "weder lesen noch aendern" - genau die Pfade, die der KI-Client als
184
+ # Anweisungsquelle laden soll (B07). Wer die Vorlage woertlich ausfuellt, erzeugt damit
185
+ # eine Lesesperre auf die eigenen Regeldateien. Diese Pruefung findet den Fall im
186
+ # Repositorium, also bevor eine Installation entsteht (D-55).
187
+ STRUKTURPFAD_MARKER = (
188
+ "<RUNTIME_DIR>", "<ROOT_INSTRUCTION_FILE>", "<CORE_DIR>", "<RULES_DIR>",
189
+ ".koolie/project-overlay/", "framework/", "AGENTS.md", "CLAUDE.md", ".devin/", ".claude/",
190
+ )
191
+
192
+ # Die Beschriftung der Deklarationszeile - in der Overlay-Vorlage eine Tabellenzeile, in
193
+ # der Laufzeitregel ein Listeneintrag. Beide beginnen mit derselben Bezeichnung.
194
+ DEKLARATION_RE = re.compile(r"^\s*(?:\|\s*|-\s+)Ausgeschlossene Pfade")
195
+
196
+
197
+ def _excluded_paths_traeger(root: str, man: dict) -> list[str]:
198
+ """Dateien, die <EXCLUDED_PATHS> deklarieren - Quelle und Installation."""
199
+ kandidaten = [
200
+ os.path.join(KERN, "templates", "project-overlay", "OVERLAY.md"),
201
+ os.path.join(KERN, "framework", "runtime", "rules", "20-project-overlay.md"),
202
+ os.path.join(".koolie/project-overlay", "OVERLAY.md"),
203
+ ]
204
+ runtime_dir = (man or {}).get("runtime_dir")
205
+ if runtime_dir:
206
+ kandidaten.append(os.path.join(runtime_dir.replace("/", os.sep), "rules",
207
+ regeldatei(man, "20-project-overlay.md")))
208
+ treffer = []
209
+ for rel in kandidaten:
210
+ pfad = os.path.join(root, rel)
211
+ if os.path.isfile(pfad) and pfad not in treffer:
212
+ treffer.append(pfad)
213
+ return treffer
214
+
215
+
216
+ def check_excluded_paths(root: str, man: dict) -> None:
217
+ """Pruefung 28 (D-55): Ein Schreibschutz ist kein Leseverbot.
218
+
219
+ Geprueft wird die **Deklarationszeile** von <EXCLUDED_PATHS>: Sie darf keinen
220
+ Strukturpfad des Frameworks nennen. Der Nachweis ist eine Textpruefung - dass die
221
+ Berechtigungsdatei die beiden Kategorien trennt, prueft Pruefung 2.
222
+
223
+ Nur die Deklaration, nicht jede Nennung: Beide Traeger erklaeren im Fliesstext
224
+ ausdruecklich, dass die Strukturpfade **nicht** hierher gehoeren, und diese Saetze
225
+ nennen beides in einer Zeile. Eine Pruefung, die jede Nennung meldet, wuerde genau
226
+ den richtigen Text beanstanden. Erkannt wird die Deklaration an ihrer Beschriftung
227
+ (DEKLARATION_RE) - und weil eine verlorene Beschriftung eine Pruefung erzeugt, die
228
+ leise besteht, ist auch ihr Fehlen ein Fehler (D-23).
229
+ """
230
+ for pfad in _excluded_paths_traeger(root, man):
231
+ rel = os.path.relpath(pfad, root).replace(os.sep, "/")
232
+ zeilen = read(pfad).splitlines()
233
+ deklarationen = [(i, z) for i, z in enumerate(zeilen, 1)
234
+ if "<EXCLUDED_PATHS>" in z and DEKLARATION_RE.match(z)]
235
+ if not deklarationen and any("<EXCLUDED_PATHS>" in z for z in zeilen):
236
+ err(f"{rel}: nennt <EXCLUDED_PATHS>, aber keine Zeile ist als Deklaration "
237
+ f"erkennbar (Beschriftung 'Ausgeschlossene Pfade'). Pruefung 28 prueft "
238
+ f"damit nichts und bestuende leise - die Beschriftung ist Teil des "
239
+ f"Nachweises (D-23, D-55)")
240
+ for i, zeile in deklarationen:
241
+ gefunden = [m for m in STRUKTURPFAD_MARKER if m in zeile]
242
+ if not gefunden:
243
+ continue
244
+ err(f"{rel}:{i}: Die Deklaration von <EXCLUDED_PATHS> nennt "
245
+ f"{', '.join(gefunden)}. <EXCLUDED_PATHS> wird in der Berechtigungsdatei zu "
246
+ f"einer 'read'- UND einer 'write'-Verweigerung; die Strukturpfade des "
247
+ f"Frameworks sind integritaetsgeschuetzt, nicht vertraulich - sie gehoeren "
248
+ f"in <READ_ONLY_PATHS>. Ein Projekt, das dieser Zeile folgt, sperrt den "
249
+ f"Lesezugriff auf seine eigenen Regeldateien; der KI-Client kann die "
250
+ f"Anweisungen dann nicht laden, die er befolgen soll (B07, D-55)")
251
+
252
+
253
+ # --- Pruefung 51 -------------------------------------------------------------------
254
+ # Eine Regel kann als SATZ oder als AUSFUELLSCHLITZ ausgedrueckt sein, und ein Sweep nach
255
+ # einer Marke findet nur den Satz. 0.33.0 hat die Domain-Ausnahme in sechzehn Traegern
256
+ # angefasst, davon acht anweisenden - darunter eine Datei im selben Verzeichnis; die
257
+ # Overlay-Laufzeitfassung war nicht darunter, weil dort kein Satz stand. Dreiunddreissig
258
+ # Releases lang bot die Overlay-Laufzeitfassung damit an, was ihre eigene Quelle
259
+ # ausdruecklich ausschliesst (D-150, Grenzfall G-13).
260
+ #
261
+ # ABGELEITET, NICHT GEPFLEGT: Die Feldmenge stammt aus der Kontextquellentabelle der
262
+ # Overlay-Vorlage. Traegt eine Zeile dort einen FESTEN Wert in der Freigabespalte - also
263
+ # keinen <TBD> -, dann ist der Wert entschieden, und die Laufzeitfassung darf fuer
264
+ # dasselbe Feld keinen Schlitz fuehren. Traegt die Quelle selbst einen Schlitz, bleibt er
265
+ # in beiden zulaessig; die Zeile der MCP-Server belegt das als Gegenprobe.
266
+ #
267
+ # WAS SIE NICHT LEISTET: Sie vergleicht Feldnamen, nicht Werte. Ob der eingetragene feste
268
+ # Wert derselbe ist, sieht sie nicht - das ist FW-KO-05 und laeuft als Durchsicht.
269
+ P51_QUELLE = KERN + "/templates/project-overlay/OVERLAY.md"
270
+ P51_LAUFZEIT = KERN + "/framework/runtime/rules/20-project-overlay.md"
271
+ P51_TABELLENKOPF = "| Kontextquelle | Kontextklasse | Freigabe | Bedingungen |"
272
+ P51_KLAMMER_RE = re.compile(r"\s*\([^)]*\)\s*$")
273
+
274
+
275
+ def _51_feldname(zelle: str) -> str:
276
+ """Der blosse Feldname einer Zeile - ohne Fettmarkierung und ohne Klammerzusatz.
277
+
278
+ 'Freigegebene externe Domains (Fetch)' und 'Freigegebene externe Domains' sind
279
+ dasselbe Feld; die Klammer sagt, ueber welches Werkzeug es wirkt.
280
+ """
281
+ name = zelle.replace("**", "").strip()
282
+ return P51_KLAMMER_RE.sub("", name).strip()
283
+
284
+
285
+ def check_overlay_schlitze(root: str) -> None:
286
+ """Pruefung 51 (D-150): Kein Schlitz fuer einen Wert, den die Overlay-Vorlage festlegt."""
287
+ qpfad = os.path.join(root, P51_QUELLE.replace("/", os.sep))
288
+ lpfad = os.path.join(root, P51_LAUFZEIT.replace("/", os.sep))
289
+ if not os.path.isfile(qpfad) or not os.path.isfile(lpfad):
290
+ err(f"{P51_QUELLE} oder {P51_LAUFZEIT} fehlt – Prüfung 51 hat ihren Gegenstand "
291
+ f"verloren; sie bestünde sonst leise (D-23)")
292
+ return
293
+ quelle = read(qpfad).replace("\r\n", "\n")
294
+ if P51_TABELLENKOPF not in quelle:
295
+ err(f"{P51_QUELLE}: die Kontextquellentabelle mit dem Kopf "
296
+ f"'{P51_TABELLENKOPF}' ist nicht mehr auffindbar. Prüfung 51 leitet ihre "
297
+ f"Feldmenge daraus ab und hätte ohne sie nichts zu prüfen (D-23)")
298
+ return
299
+ rest = quelle.split(P51_TABELLENKOPF, 1)[1]
300
+ festgelegt = []
301
+ for zeile in rest.split("\n")[1:]:
302
+ if not zeile.startswith("|"):
303
+ break
304
+ zellen = tabellenzellen(zeile)
305
+ if len(zellen) < 3:
306
+ continue
307
+ if TBD_RE.search(zellen[2]):
308
+ continue
309
+ festgelegt.append((_51_feldname(zellen[0]), zellen[2].replace("**", "").strip()))
310
+ if not festgelegt:
311
+ err(f"{P51_QUELLE}: keine Zeile der Kontextquellentabelle trägt einen festen "
312
+ f"Freigabewert. Prüfung 51 hätte damit keinen Gegenstand mehr (D-23)")
313
+ return
314
+ laufzeit = read(lpfad).replace("\r\n", "\n")
315
+ for name, wert in festgelegt:
316
+ for zeile in laufzeit.split("\n"):
317
+ roh = zeile.lstrip("-* ").strip()
318
+ if not roh.startswith(name + ":"):
319
+ continue
320
+ if TBD_RE.search(roh):
321
+ err(f"{P51_LAUFZEIT}: Das Feld '{name}' trägt einen Ausfüllschlitz, "
322
+ f"obwohl {P51_QUELLE} seinen Wert auf '{wert}' festlegt. Eine Regel "
323
+ f"kann als Satz oder als Schlitz ausgedrückt sein – ein Sweep nach "
324
+ f"der Formulierung findet nur den Satz, und die Laufzeitfassung ist "
325
+ f"die, die in jede Sitzung lädt (D-150, Grenzfall G-13)")
326
+
327
+
328
+ # --- Pruefung 52 -------------------------------------------------------------------
329
+ # Eine Kurzfassung, die den einschraenkenden Halbsatz der Langform weglaesst, kehrt ihre
330
+ # Aussage um. Die Wurzel-Anweisungsdatei trennt seit D-53 zwei Saetze: Anwendungslogik mit
331
+ # Sicherheitsbezug ist Kontrollstufe hoch und nach Freigabe umsetzbar - tatsaechliche
332
+ # Berechtigungen und Betriebs-, Infrastruktur- und Sicherheitskonfigurationen sind V6 und
333
+ # auch nach Freigabe nicht delegierbar. Zwei Fassungen fuehrten beides in EINER
334
+ # Aufzaehlung, deren Rechtsfolge die Freigabe war, und stellten damit ein
335
+ # Delegationsverbot auf die freigebbare Seite - sechzig Releases lang (D-151,
336
+ # Grenzfaelle G-05 und G-06).
337
+ #
338
+ # ABGELEITET: Die Begriffe stammen aus der V6-Zeile der Delegationsverbotsliste. Die
339
+ # Schreibvarianten stehen daneben, wie bei Pruefung 29 - das Projekt fuehrt
340
+ # 'Sicherheitskonfiguration' und 'Security-Konfiguration' synonym, und eine Pruefung, die
341
+ # nur eine Schreibweise kennt, findet die Haelfte.
342
+ #
343
+ # AUSGENOMMEN ist die Langform, aus der die Begriffe stammen: Dort steht die Abgrenzung
344
+ # zu V6, und sie MUSS beide Seiten in einem Absatz nennen. Die Ausnahme ist abgeleitet -
345
+ # es ist die Datei, aus der gelesen wurde, nicht ein gepflegter Name.
346
+ #
347
+ # WAS SIE NICHT LEISTET: Sie liest Woerter, keine Bedeutung. Ein Text, der dieselbe
348
+ # Aussage ohne diese Begriffe trifft, entgeht ihr - dieselbe Grenze wie bei Pruefung 29.
349
+ P52_RISIKO = KERN + "/framework/core/09-risk-model.md"
350
+ P52_V6_RE = re.compile(r"^\|\s*V6\s*\|([^|]*)\|", re.M)
351
+ # Schreibvarianten je Begriff der V6-Zeile. Links der Wortstamm, wie er dort steht.
352
+ P52_VARIANTEN = {
353
+ "Produktionssystem": r"Produktionssystem",
354
+ "Infrastruktur": r"Infrastruktur",
355
+ "Berechtigung": r"Berechtigung",
356
+ "Sicherheitskonfiguration": r"Sicherheitskonfiguration|Security-Konfiguration",
357
+ }
358
+ P52_STUFE_RE = re.compile(r"Kontrollstufe\s+\*{0,2}hoch")
359
+ P52_FOLGE_RE = re.compile(r"Umsetzung[^.;]{0,120}Freigabe")
360
+ P52_EINZELN_RE = re.compile(r"^\s*(?:[-*+]\s|\d+\.\s|\|)")
361
+
362
+
363
+ def _52_v6_begriffe(root: str) -> list:
364
+ """Die Begriffe der V6-Zeile, auf ihren Stamm gebracht - abgeleitet, nicht gepflegt."""
365
+ pfad = os.path.join(root, P52_RISIKO.replace("/", os.sep))
366
+ if not os.path.isfile(pfad):
367
+ return []
368
+ m = P52_V6_RE.search(read(pfad).replace("\r\n", "\n"))
369
+ if not m:
370
+ return []
371
+ treffer = []
372
+ for stamm, muster in P52_VARIANTEN.items():
373
+ if re.search(stamm, m.group(1)):
374
+ treffer.append((stamm, re.compile(muster)))
375
+ return treffer
376
+
377
+
378
+ def _52_einheiten(text: str):
379
+ """Liefert die Pruefeinheiten: Listenpunkte und Tabellenzeilen einzeln, Prosa als Absatz.
380
+
381
+ Die Einheit muss die des Lesers sein. Wer einen Listenpunkt liest, liest ihn allein;
382
+ wer einen Absatz liest, liest ihn ganz. Eine Pruefung ueber die ganze Datei fände in
383
+ jeder normativen Datei beides und meldete ueberall.
384
+ """
385
+ absatz = []
386
+ for zeile in text.replace("\r\n", "\n").split("\n"):
387
+ if not zeile.strip() or P52_EINZELN_RE.match(zeile):
388
+ if absatz:
389
+ yield "\n".join(absatz)
390
+ absatz = []
391
+ if zeile.strip():
392
+ yield zeile
393
+ continue
394
+ absatz.append(zeile)
395
+ if absatz:
396
+ yield "\n".join(absatz)
397
+
398
+
399
+ def check_v6_freigabefolge(root: str) -> None:
400
+ """Pruefung 52 (D-151): Kein V6-Gegenstand in einer Aufzaehlung mit Freigabefolge."""
401
+ begriffe = _52_v6_begriffe(root)
402
+ if not begriffe:
403
+ err(f"{P52_RISIKO}: die Zeile 'V6' der Delegationsverbotsliste ist nicht mehr "
404
+ f"auffindbar oder nennt keinen der bekannten Begriffe. Prüfung 52 leitet "
405
+ f"ihren Gegenstand daraus ab und hätte ohne sie nichts zu prüfen (D-23)")
406
+ return
407
+ ausgenommen = os.path.join(root, P52_RISIKO.replace("/", os.sep))
408
+ for pfad in _normative_traeger(root):
409
+ if os.path.abspath(pfad) == os.path.abspath(ausgenommen):
410
+ continue
411
+ rel = os.path.relpath(pfad, root).replace(os.sep, "/")
412
+ for einheit in _52_einheiten(read(pfad)):
413
+ if not P52_STUFE_RE.search(einheit) or not P52_FOLGE_RE.search(einheit):
414
+ continue
415
+ for stamm, muster in begriffe:
416
+ if muster.search(einheit):
417
+ err(f"{rel}: '{stamm}' steht in einer Aufzählung, deren Rechtsfolge "
418
+ f"eine Freigabe ist ('…{P52_FOLGE_RE.search(einheit).group(0)[:60]}…'). "
419
+ f"V6 ist auch nach Freigabe nicht delegierbar; die Abgrenzung "
420
+ f"trennt Anwendungslogik von Betrieb (D-53, D-151, Grenzfälle "
421
+ f"G-05 und G-06)")
422
+ break
423
+
424
+
425
+ def _normative_traeger(root: str):
426
+ """Die anweisenden Fassungen: Wurzel-Anweisungsdatei, Regelablage, Langform,
427
+ Overlay-Vorlage, Skills und Checklisten - die sechs, die FW-KO-05 gegeneinander hält."""
428
+ pfade = []
429
+ p = os.path.join(root, KERN, "framework", "runtime", "root-instruction.md")
430
+ if os.path.isfile(p):
431
+ pfade.append(p)
432
+ for teil in (("framework", "runtime", "rules"), ("framework", "core"),
433
+ ("checklists",), ("templates", "project-overlay")):
434
+ verz = os.path.join(root, KERN, *teil)
435
+ if not os.path.isdir(verz):
436
+ continue
437
+ for name in sorted(os.listdir(verz)):
438
+ if name.endswith(".md"):
439
+ pfade.append(os.path.join(verz, name))
440
+ skills = os.path.join(root, KERN, "framework", "skills")
441
+ if os.path.isdir(skills):
442
+ for name in sorted(os.listdir(skills)):
443
+ p = os.path.join(skills, name, "SKILL.md")
444
+ if os.path.isfile(p):
445
+ pfade.append(p)
446
+ return pfade
447
+
448
+
449
+ # --- Pruefung 55 und 56 --------------------------------------------------------------
450
+ # ANLASS: der Durchgang durch die Vorbedingungen der dreizehn Testblaetter (2026-09-18).
451
+ # Er hat zwei Dinge gefunden, die nichts gemeldet hat.
452
+ #
453
+ # ERSTENS: Ein Overlay darf einen Platzhalter durch seinen WERT ERSETZEN statt ihn zu
454
+ # BINDEN. Fuer das Overlay selbst ist das folgenlos - es nennt ja den Wert. Fuer jeden
455
+ # KERNTEXT, der denselben Platzhalter traegt, ist es das nicht: Im Uebungsrepositorium
456
+ # standen 65 Fundstellen von fuenf Pflichtplatzhaltern in der geladenen Laufzeitschicht,
457
+ # die kein Leser aufloesen konnte - <ISSUE_TRACKER> allein in vierzehn Traegern.
458
+ # Der Validator meldete 0 Fehler, 0 Warnungen.
459
+ #
460
+ # ZWEITENS: Eine Vorbedingung darf einen Traeger verlangen, den dasselbe Overlay sperrt.
461
+ # SK-012-P01 verlangt die Merge-Request-Vorlage; ihr Pfad liegt unter .github/**, und das
462
+ # steht im Uebungs-Overlay unter <EXCLUDED_PATHS>. Die Zelle war damit nie fahrbar.
463
+ #
464
+ # WAS 55 NICHT LEISTET: (b) prueft, ob der Platzhalter GENANNT wird, nicht ob der Wert
465
+ # daneben richtig ist. Eine Bindung an den falschen Wert laeuft durch.
466
+ # WAS 56 NICHT LEISTET: Sie loest ueber die Bindungszeile auf. Eine Vorbedingung, die
467
+ # einen ausgeschlossenen Pfad WOERTLICH nennt statt ueber einen Platzhalter, entgeht ihr.
468
+ P55_REGISTER = KERN + "/docs/PLACEHOLDER_REGISTRY.md"
469
+ P55_VORLAGE = KERN + "/templates/project-overlay/OVERLAY.md"
470
+ P55_LAUFZEIT = (KERN + "/framework/runtime/rules", KERN + "/framework/skills")
471
+ P55_ZEILE_RE = re.compile(r"^\|\s*`<([A-Z][A-Z0-9_]*)>`\s*\|")
472
+
473
+
474
+ def _p55_pflicht(root: str) -> list:
475
+ """(Name, Ort) je Platzhalter mit 'Pflicht vor Aktivierung' = ja - abgeleitet."""
476
+ pfad = os.path.join(root, P55_REGISTER.replace("/", os.sep))
477
+ if not os.path.isfile(pfad):
478
+ err(f"{P55_REGISTER} fehlt - Prüfung 55 hat ihren Gegenstand verloren; sie "
479
+ f"bestünde sonst leise (D-23)")
480
+ return []
481
+ raus = []
482
+ for zeile in read(pfad).replace("\r\n", "\n").split("\n"):
483
+ z = zeile.strip()
484
+ treffer = P55_ZEILE_RE.match(z)
485
+ if not treffer:
486
+ continue
487
+ zellen = tabellenzellen(z)
488
+ if len(zellen) > 4 and zellen[4].startswith("ja"):
489
+ raus.append((treffer.group(1), zellen[2]))
490
+ if not raus:
491
+ err(f"{P55_REGISTER}: keine Zeile trägt 'Pflicht vor Aktivierung' = ja. Prüfung "
492
+ f"55 leitet ihre Menge von dort ab und hätte damit nichts zu prüfen (D-23)")
493
+ return raus
494
+
495
+
496
+ def check_pflichtplatzhalter(root: str) -> None:
497
+ """Pruefung 55a: Die Overlay-Vorlage bietet jeden Pflichtplatzhalter an."""
498
+ pfad = os.path.join(root, P55_VORLAGE.replace("/", os.sep))
499
+ if not os.path.isfile(pfad):
500
+ return
501
+ vorlage = read(pfad)
502
+ for name, ort in _p55_pflicht(root):
503
+ if not ort.lower().startswith("overlay"):
504
+ continue # anderswo gesetzt - die Vorlage muss ihn nicht anbieten
505
+ if name not in vorlage:
506
+ err(f"{P55_VORLAGE}: Der Platzhalter <{name}> ist im Register als 'Pflicht "
507
+ f"vor Aktivierung' geführt und dort in '{ort}' verortet, kommt in der "
508
+ f"Vorlage aber nicht vor. Ein Projekt, das die Vorlage ausfüllt, "
509
+ f"begegnet ihm nie (D-160)")
510
+
511
+
512
+ def check_platzhalterbindung(root: str, man: dict) -> None:
513
+ """Pruefung 55b (--strict-overlay): Das aktive Overlay BINDET, statt zu ersetzen.
514
+
515
+ 🔴 SIE PRUEFT SEIT 0.84.0 DIE BINDUNG UND NICHT MEHR DIE NENNUNG (K-88, D-257).
516
+ Bis 0.83.0 fragte sie `if name in text`, also die BUCHSTABEN des Platzhalternamens
517
+ irgendwo im Overlay - und meldete am Uebungsrepositorium NULL, waehrend 14 von 26
518
+ Pflichtplatzhaltern dort nirgends mit spitzen Klammern standen. Ihr eigener
519
+ Meldungstext sagte dabei "ist im Overlay aber nirgends gebunden".
520
+
521
+ Eine Bindung, die eine Teilzeichenkette ist, sagt nichts ueber einen Wert.
522
+
523
+ 🔴 Und die Gegenprobe deckte es mit: `BINDUNGEN` in `probe-pruefungen.py` schrieb
524
+ `ISSUE_TRACKER` ohne Klammern und nannte das eine Bindung. Zwei Stellen, die
525
+ einander decken - derselbe Befundtyp wie bei `LINK_ROOTS` und `OPTIONAL_RUNTIME_RE`
526
+ (0.57.0). Die Sonde `55d` misst jetzt genau den Unterschied.
527
+ """
528
+ overlay = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
529
+ if not os.path.isfile(overlay):
530
+ return
531
+ text = read(overlay)
532
+ genannt = {}
533
+ for basis in P55_LAUFZEIT:
534
+ wurzel = os.path.join(root, basis.replace("/", os.sep))
535
+ if not os.path.isdir(wurzel):
536
+ continue
537
+ for ordner, _, dateien in os.walk(wurzel):
538
+ for n in sorted(dateien):
539
+ if not n.endswith(".md"):
540
+ continue
541
+ p = os.path.join(ordner, n)
542
+ inhalt = read(p)
543
+ rel = os.path.relpath(p, root).replace(os.sep, "/")
544
+ for name, _ in _p55_pflicht(root):
545
+ if f"<{name}>" in inhalt:
546
+ genannt.setdefault(name, []).append(rel)
547
+ for name, traeger in sorted(genannt.items()):
548
+ # Der Name IN SPITZEN KLAMMERN - die Schreibweise, die den Kerntext aufloest.
549
+ # Ein blosses Vorkommen der Buchstaben ist eine Nennung, keine Bindung (D-257).
550
+ if f"<{name}>" in text:
551
+ continue
552
+ err(f".koolie/project-overlay/OVERLAY.md: Der Pflichtplatzhalter <{name}> wird von "
553
+ f"{len(traeger)} Träger(n) der geladenen Schicht genannt (z. B. "
554
+ f"{traeger[0]}), ist im Overlay aber nirgends gebunden. Ein Overlay, das den "
555
+ f"Platzhalter durch seinen Wert ersetzt statt ihn zu binden, lässt jeden "
556
+ f"Kerntext unauflösbar, der ihn trägt (D-160)")
557
+
558
+
559
+ P56_KATALOG = KERN + "/tests/TEST_CATALOG.md"
560
+
561
+
562
+ def _p56_ausgeschlossen(text: str) -> list:
563
+ """Die Globs aus der <EXCLUDED_PATHS>-Zeile des aktiven Overlays."""
564
+ for zeile in text.replace("\r\n", "\n").split("\n"):
565
+ if "<EXCLUDED_PATHS>" not in zeile:
566
+ continue
567
+ zellen = tabellenzellen(zeile.strip())
568
+ for zelle in zellen:
569
+ if "<EXCLUDED_PATHS>" in zelle:
570
+ continue
571
+ treffer = re.findall(r"`([^`]+)`", zelle)
572
+ if treffer:
573
+ return treffer
574
+ return []
575
+
576
+
577
+ def _p56_deckt(glob: str, pfad: str) -> bool:
578
+ """Deckt das Glob-Muster den Pfad? Segmentweise, nicht nach Pfadanfang.
579
+
580
+ DER ERSTE ENTWURF VERGLICH NUR DAS ERSTE SEGMENT, und das hat in EINEM Release
581
+ zweimal falsch gemeldet: erst bei drei Zellen, die <EXCLUDED_PATHS> selbst zum
582
+ Gegenstand haben, dann bei `.github/pull_request_template.md` gegen
583
+ `.github/workflows/**` - derselbe Anfang, verschiedene Pfade. Eine Pruefung, die
584
+ Pfadanfaenge vergleicht, meldet jede Nachbardatei mit.
585
+ """
586
+ muster = re.escape(glob).replace(r"\*\*", "\x00").replace(r"\*", "[^/]*")
587
+ muster = muster.replace("\x00", ".*")
588
+ return re.fullmatch(muster, pfad) is not None
589
+
590
+
591
+ def _p56_bindung(text: str, name: str) -> str:
592
+ """Der Wert, den die Bindungszeile des Platzhalters traegt - in BEIDEN Schreibweisen.
593
+
594
+ 🔴 GEFUNDEN DURCH DIE ABHILFE ZU K-88, AM SELBEN TAG (D-261). Pruefung 55b
595
+ verlangt seit 0.84.0 den Platzhalternamen IN SPITZEN KLAMMERN; diese Funktion suchte
596
+ ihn OHNE - `f"`{name}`"`. Nach der Bindung der vierzehn Platzhalter im
597
+ Uebungs-Overlay haette Pruefung 56 ihre Zeilen nicht mehr gefunden und waere leise
598
+ gruen geblieben; Sonde 56a ist genau daran gefallen.
599
+
600
+ Zwei Pruefungen, die dieselbe Zeile lesen, muessen sie gleich lesen - oder jede
601
+ fuer ihren eigenen Gegenstand, und das ausdruecklich.
602
+
603
+ Hier gilt das Zweite: **55b prueft die SCHREIBWEISE der Bindung, 56 den WERT
604
+ daneben.** Fuer den Wert ist gleichgueltig, wie der Name geschrieben steht - und ein
605
+ Overlay aus dem Bestand traegt ihn weiter ohne Klammern. Die Funktion nimmt deshalb
606
+ beide Formen; eine Pruefung, die einen Wert nicht mehr findet, meldet keinen Fehler,
607
+ sondern schweigt - und das ist die Bauform "die Null durch Konstruktion".
608
+
609
+ **Dieselbe Bauform wie D-248 und D-243:** Die Meldung entsteht durch die Abhilfe,
610
+ und zwei Pruefungen standen gegeneinander.
611
+ """
612
+ offen = None
613
+ for zeile in text.replace("\r\n", "\n").split("\n"):
614
+ z = zeile.strip()
615
+ if not z.startswith("|"):
616
+ continue
617
+ if f"`<{name}>`" not in z and f"`{name}`" not in z:
618
+ continue
619
+ zellen = tabellenzellen(z)
620
+ wert = " ".join(zellen[1:])
621
+ # \U0001f534 EINE ZEILE MIT `<TBD...>` BINDET NICHTS (D-261, zweiter Teil). Die
622
+ # Overlay-VORLAGE traegt zu jedem Pflichtplatzhalter eine Zeile mit offenem
623
+ # Wert; wer sie stehen laesst und den Wert weiter unten bindet, wurde bis
624
+ # 0.83.0 mit dem `<TBD>` der Vorlage gemessen - und die Pruefung schwieg.
625
+ # Aufgefallen ist es erst, als 55b die spitzen Klammern verlangte und diese
626
+ # Funktion dieselbe Schreibweise lesen musste: Die Enge der einen Stelle hat
627
+ # verhindert, dass die zweite auffaellt (die Bauform von 0.57.0).
628
+ if re.search(r"<TBD[:>]", wert):
629
+ offen = offen or wert
630
+ continue
631
+ return wert
632
+ # Keine gebundene Zeile - der offene Wert ist die ehrlichere Auskunft als "".
633
+ return offen or ""
634
+
635
+
636
+ def check_ausgeschlossene_vorbedingung(root: str, man: dict) -> None:
637
+ """Pruefung 56 (--strict-overlay): Keine Vorbedingung verlangt einen gesperrten Traeger."""
638
+ overlay = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
639
+ if not os.path.isfile(overlay):
640
+ return
641
+ otext = read(overlay)
642
+ globs = _p56_ausgeschlossen(otext)
643
+ if not globs:
644
+ err(f".koolie/project-overlay/OVERLAY.md: Die Zeile zu <EXCLUDED_PATHS> führt keine "
645
+ f"Pfade in Backticks. Prüfung 56 leitet ihre Menge von dort ab und hätte "
646
+ f"damit nichts zu prüfen (D-23)")
647
+ return
648
+ dateien = [os.path.join(root, P56_KATALOG.replace("/", os.sep))]
649
+ for ordner, _, namen in os.walk(os.path.join(root, KERN.replace("/", os.sep),
650
+ "framework")):
651
+ for n in sorted(namen):
652
+ if n == "TESTS.md":
653
+ dateien.append(os.path.join(ordner, n))
654
+ for pfad in dateien:
655
+ if not os.path.isfile(pfad):
656
+ continue
657
+ rel = os.path.relpath(pfad, root).replace(os.sep, "/")
658
+ for zeile in read(pfad).replace("\r\n", "\n").split("\n"):
659
+ z = zeile.strip()
660
+ if not z.startswith("| ") or z.startswith("|---"):
661
+ continue
662
+ zellen = tabellenzellen(z)
663
+ if len(zellen) < 4:
664
+ continue
665
+ vorbed = zellen[2]
666
+ for name in re.findall(r"<([A-Z][A-Z0-9_]*)>", vorbed):
667
+ # DER ZUSCHNITT, UND ER IST BEIM ERSTEN LAUF NOETIG GEWORDEN: Eine Zelle,
668
+ # die <EXCLUDED_PATHS> SELBST nennt, hat die Sperre zum Gegenstand - sie
669
+ # prueft, ob der Client sie achtet. Ohne diese Ausnahme meldete die
670
+ # Pruefung am 2026-09-18 drei solche Zellen (SK-001-N01, SK-010-N02,
671
+ # SK-012-N03) als nicht fahrbar, und alle drei sind es. Eine Pruefung,
672
+ # die ihren eigenen Gegenstand beanstandet, behauptet mehr als ihr Fall
673
+ # hergibt.
674
+ if name == "EXCLUDED_PATHS":
675
+ continue
676
+ wert = _p56_bindung(otext, name)
677
+ if not wert:
678
+ continue
679
+ for pfadangabe in re.findall(r"`([^`]+)`", wert):
680
+ for g in globs:
681
+ if _p56_deckt(g, pfadangabe):
682
+ err(f"{rel}: Die Vorbedingung von '{zellen[0]}' verlangt "
683
+ f"<{name}>; der Wert '{pfadangabe}' liegt unter "
684
+ f"'{g}', das dasselbe Overlay unter <EXCLUDED_PATHS> "
685
+ f"führt - weder lesen noch ändern. Die Zelle ist damit "
686
+ f"nicht fahrbar (D-161)")
687
+ break
688
+
689
+
690
+ # --- Pruefung 57: kein ungebundener Pflichtplatzhalter als Vorbedingung -------------
691
+ #
692
+ # DIE KEHRSEITE VON 55b. Pruefung 55b meldet ein aktives Overlay, das einen
693
+ # Pflichtplatzhalter nicht bindet. Eine Testzelle, die genau diesen Zustand als
694
+ # Vorbedingung verlangt, ist damit nur in einem Baum fahrbar, den der Validator
695
+ # beanstandet - eine Pruefung und ein Testfall desselben Repositoriums stehen
696
+ # gegeneinander, und keiner von beiden sagt es.
697
+ #
698
+ # WARUM TEILSAETZE UND NICHT ZELLEN. Eine Vorbedingung nennt haeufig mehrere Zustaende
699
+ # in einer Zelle. Wer die ganze Zelle nach einer Verneinung durchsucht, meldet jede
700
+ # Zelle mit, die irgendwo ein "ohne" traegt - die Bauform, an der Pruefung 56 beim
701
+ # ersten Lauf zweimal zu breit gemeldet hat. Getrennt wird an Semikolon und Punkt.
702
+ #
703
+ # WAS SIE NICHT LEISTET. Sie erkennt die AUFGEZAEHLTEN Wendungen, nicht jede moegliche.
704
+ # "Ein Overlay, in dem der Platzhalter fehlt" entgeht ihr - dieselbe Grenze, die
705
+ # Pruefung 29 bei Bedingungswoertern hat. Die Aufzaehlung steht hier und nirgends sonst.
706
+ P57_VERNEINUNG = (
707
+ "ohne gesetzt",
708
+ "ohne gebunden",
709
+ "ohne belegt",
710
+ "nicht gesetzt",
711
+ "nicht gebunden",
712
+ "nicht belegt",
713
+ "ungebunden",
714
+ )
715
+
716
+
717
+ def _p57_teilsaetze(text: str) -> list:
718
+ """Die Vorbedingung in Teilsaetze zerlegen - Semikolon und Punkt trennen."""
719
+ return [t.strip() for t in re.split(r"[;.]", text) if t.strip()]
720
+
721
+
722
+ def check_ungebundene_vorbedingung(root: str) -> None:
723
+ """Pruefung 57: Keine Vorbedingung verlangt einen ungebundenen Pflichtplatzhalter."""
724
+ pflicht = {name for name, _ in _p55_pflicht(root)}
725
+ if not pflicht:
726
+ return # _p55_pflicht hat den verlorenen Gegenstand bereits gemeldet
727
+ for rel in _ueb_katalogdateien(root):
728
+ pfad = os.path.join(root, *rel.split(os.sep))
729
+ if not os.path.isfile(pfad):
730
+ continue
731
+ anzeige = rel.replace(os.sep, "/")
732
+ for zeile in read(pfad).replace("\r\n", "\n").split("\n"):
733
+ z = zeile.strip()
734
+ if not z.startswith("| ") or z.startswith("|---"):
735
+ continue
736
+ zellen = tabellenzellen(z)
737
+ if len(zellen) < 4:
738
+ continue
739
+ for teil in _p57_teilsaetze(zellen[2]):
740
+ namen = [n for n in re.findall(r"<([A-Z][A-Z0-9_]*)>", teil)
741
+ if n in pflicht]
742
+ if not namen:
743
+ continue
744
+ klein = teil.lower()
745
+ marke = next((m for m in P57_VERNEINUNG if m in klein), None)
746
+ if marke is None:
747
+ continue
748
+ err(f"{anzeige}: Die Vorbedingung von '{zellen[0]}' verlangt "
749
+ f"<{namen[0]}> als nicht gesetzt ('{marke}'). Der Platzhalter ist "
750
+ f"im Register 'Pflicht vor Aktivierung', und Pruefung 55b meldet "
751
+ f"genau diesen Zustand im aktiven Overlay als Fehler - die Zelle "
752
+ f"waere nur in einem Baum fahrbar, den der Validator beanstandet. "
753
+ f"Gemeint ist in aller Regel ein gebundener Platzhalter OHNE Wert, "
754
+ f"also ein Ausfuellschlitz (D-166)")
755
+ break
756
+
757
+ # --- Pruefung 60: Der Befehlsschlitz, den der Ausloeser braucht ---------------------
758
+ #
759
+ # ANLASS. Gemessen am 2026-09-18 im fuenften Sitzungstest (CR-2026-091). `FW-SC-01`
760
+ # ist zum dritten Mal gefahren worden und zum dritten Mal nicht abnehmbar gewesen -
761
+ # diesmal aus einem neuen Grund: Der Lauf hat NICHTS geaendert. `fw-change-small`
762
+ # verlangt in Schritt 4, den Testbefehl VOR dem ersten Schreibzugriff auszufuehren;
763
+ # im Messbaum stand `<TEST_COMMAND>` im `ask`-Korb, und `ask` ist im
764
+ # nicht-interaktiven Betrieb eine Abweisung (D-134). Der Lauf hat angehalten und
765
+ # gefragt - regelkonform. Gemessen war der Korb, nicht die Scope-Treue.
766
+ #
767
+ # Der Schreibzuschnitt deckt das SCHREIBEN, nicht das AUSFUEHREN.
768
+ #
769
+ # Derselbe Fehler hat im selben Release `FW-PO-02` einen Durchgang gekostet, und er
770
+ # wiederholt sich bei jeder Wiederholung, weil die Zelle ihn nicht nennt. `FW-SC-02`
771
+ # ist mit 0.59.0 abgenommen worden, weil sein Messbaum den Befehl freigab - seine
772
+ # Zelle sagt es nicht, und wer sie ohne dieses Wissen wiederholt, faellt hinein.
773
+ #
774
+ # GEGENSTAND. Eine `sitzung`-Zelle, deren Ausloeser einen Skill als `/name` aufruft,
775
+ # dessen Frontmatter einen Befehlsschlitz AUSFUEHRT (`Exec(<..._COMMAND>)` unter
776
+ # `permissions`), muss diesen Schlitz in ihrer VORBEDINGUNG nennen.
777
+ #
778
+ # WARUM DAS FRONTMATTER UND NICHT DER FLIESSTEXT. `fw-plan` nennt `<TEST_COMMAND>`
779
+ # in seinen Vorbedingungen und in seiner Teststrategie - es fuehrt den Befehl aber
780
+ # nicht aus, sondern plant ihn. Ein Zuschnitt ueber den Fliesstext haette `FW-FI-02`
781
+ # mitgemeldet, dessen Lauf am 2026-09-18 keinen einzigen Befehl gebraucht hat. Das
782
+ # Frontmatter sagt, was der Skill TUT; der Fliesstext, wovon er redet.
783
+ #
784
+ # GRENZE, UND SIE STEHT HIER. Geprueft wird die NENNUNG des Schlitzes, nicht die
785
+ # Aussage darueber. Eine Vorbedingung, die `<TEST_COMMAND>` nennt und etwas Falsches
786
+ # darueber sagt, laeuft durch. Die Pruefung faengt das Vergessen, nicht den Irrtum.
787
+ # ZWEITE GRENZE: Sie gilt nur fuer `sitzung`-Zellen. Ein `review` braucht keinen
788
+ # Messbaum, und ein `skript` fuehrt seine Befehle selbst.
789
+ P60_EXEC_RE = re.compile(r"Exec\((<[A-Z_]+_COMMAND>)\)")
790
+
791
+
792
+ def _skills_mit_befehlsschlitz(root: str) -> dict:
793
+ """Skillname -> Liste der Befehlsschlitze, die sein Frontmatter ausfuehrt.
794
+
795
+ Abgeleitet aus den Skillquellen, nicht gepflegt: Wer einem Skill einen Befehl
796
+ hinzufuegt, soll nicht daran denken muessen, eine Liste im Validator nachzuziehen.
797
+ """
798
+ raus = {}
799
+ basen = [os.path.join(root, KERN, "framework", "skills"),
800
+ os.path.join(root, KERN, "framework", "role-packs")]
801
+ for basis in basen:
802
+ if not os.path.isdir(basis):
803
+ continue
804
+ for wurzel, _, files in os.walk(basis):
805
+ if "SKILL.md" not in files:
806
+ continue
807
+ text = read(os.path.join(wurzel, "SKILL.md"))
808
+ if not text.startswith("---"):
809
+ continue
810
+ ende = text.find("---", 3)
811
+ if ende < 0:
812
+ continue
813
+ schlitze = sorted(set(P60_EXEC_RE.findall(text[3:ende])))
814
+ if schlitze:
815
+ raus[os.path.basename(wurzel)] = schlitze
816
+ return raus
817
+
818
+
819
+ def check_befehlsschlitz_in_vorbedingung(root: str) -> None:
820
+ """Pruefung 60 (D-178): Der Ausloeser braucht einen Befehl - die Zelle sagt es."""
821
+ skills = _skills_mit_befehlsschlitz(root)
822
+ if not skills:
823
+ err(f"{KERN}/framework/skills/: kein Skill mit 'Exec(<..._COMMAND>)' im "
824
+ f"Frontmatter gefunden – Prüfung 60 leitet ihren Gegenstand daraus ab und "
825
+ f"hat ihn verloren; sie bestünde sonst leise (D-23)")
826
+ return
827
+ dateien = [os.path.join(root, KERN, "tests", "TEST_CATALOG.md")]
828
+ for basis in (os.path.join(root, KERN, "framework", "skills"),
829
+ os.path.join(root, KERN, "framework", "role-packs")):
830
+ for wurzel, _, files in os.walk(basis):
831
+ if "TESTS.md" in files:
832
+ dateien.append(os.path.join(wurzel, "TESTS.md"))
833
+ for pfad in sorted(dateien):
834
+ if not os.path.isfile(pfad):
835
+ continue
836
+ rel = os.path.relpath(pfad, root).replace(os.sep, "/")
837
+ i_vor = i_ein = i_pm = -1
838
+ for nr, zeile in enumerate(read(pfad).splitlines(), 1):
839
+ if not zeile.lstrip().startswith("|"):
840
+ continue
841
+ if "Prüfmethode" in zeile and "Eingabe" in zeile:
842
+ i_vor = _tabellenspalte(zeile, "Vorbedingung")
843
+ i_ein = _tabellenspalte(zeile, "Eingabe")
844
+ i_pm = _tabellenspalte(zeile, "Prüfmethode")
845
+ continue
846
+ if min(i_vor, i_ein, i_pm) < 0:
847
+ continue
848
+ zellen = [z.strip() for z in zeile.strip().strip("|").split("|")]
849
+ if len(zellen) <= max(i_vor, i_ein, i_pm):
850
+ continue
851
+ if "sitzung" not in zellen[i_pm]:
852
+ continue
853
+ ausloeser, vorbedingung = zellen[i_ein], zellen[i_vor]
854
+ gebraucht = []
855
+ for skill, schlitze in skills.items():
856
+ if f"/{skill}" in ausloeser:
857
+ gebraucht += schlitze
858
+ fehlend = sorted({s for s in gebraucht if s not in vorbedingung})
859
+ if fehlend:
860
+ err(f"{rel}:{nr}: Der Auslöser ruft einen Skill auf, der "
861
+ f"{', '.join(fehlend)} ausführt – die Vorbedingung nennt den "
862
+ f"Schlitz nicht. Steht der Befehl im Meßbaum im `ask`-Korb, hält "
863
+ f"der Lauf regelkonform an, und gemessen ist der Korb statt des "
864
+ f"Gegenstands (D-134, D-178)")
865
+
866
+
867
+ # --- Pruefung 59: Der Overlay-Wert in der Schicht, die ihn durchsetzt ---------------
868
+ #
869
+ # ANLASS. Gemessen am 2026-09-18 am Uebungsrepositorium (CR-2026-090, Befund 3): 0.63.0
870
+ # hat die Sperre von .github/** auf .github/workflows/** EINGEENGT, weil sie sonst die
871
+ # Merge-Request-Vorlage mitsperrt - einen Traeger, den fw-mr-description ausdruecklich
872
+ # als zulaessige Kontextquelle fuehrt (D-161). Die Einengung steht in
873
+ # .koolie/project-overlay/OVERLAY.md. Die beiden Traeger, die den Client WIRKLICH binden - die
874
+ # Laufzeitfassung des Overlays und der deny-Korb der Berechtigungsdatei - tragen
875
+ # weiterhin den alten, weiteren Wert. git log -S sagt: seit dem ersten Commit des
876
+ # Repositoriums unveraendert.
877
+ #
878
+ # DREI PRUEFUNGEN SAHEN ES NICHT, JEDE AUS EINEM EIGENEN GRUND. Pruefung 56 loest ueber
879
+ # die BINDUNGSZEILE DER QUELLE auf - sie liest genau den Traeger, der richtig ist.
880
+ # Pruefung 55b fragt, ob der Platzhalter GEBUNDEN ist, nicht welchen WERT er traegt; ihr
881
+ # eigener Kopfkommentar sagt seit 0.63.0 "Eine Bindung an den falschen Wert laeuft
882
+ # durch." Und --strict-overlay vergleicht Quelle und Laufzeitfassung allein im STATUS -
883
+ # der Abgleich der uebrigen Werte ist seit CR-2026-044 E4 offen.
884
+ #
885
+ # DREI GEGENSTAENDE:
886
+ # (a) Die Laufzeitfassung des Overlays NENNT <EXCLUDED_PATHS>. Ein Traeger, der den
887
+ # Wert nur einsetzt, ist fuer sich stimmig - und faellt niemandem auf. Das ist
888
+ # 55b eine Schicht tiefer.
889
+ # (b) Die Zeile, die ihn nennt, traegt DIESELBE Globmenge wie die Bindungszeile der
890
+ # Quelle. Abweichung in beide Richtungen ist ein Fehler; die Quelle ist massgeblich.
891
+ # (c) Jeder Glob der Quelle hat im deny-Korb eine Lese- UND eine Schreibsperre.
892
+ #
893
+ # GRENZE, UND SIE IST GENANNT. Geprueft wird EIN Platzhalter. <EXCLUDED_PATHS> ist der
894
+ # einzige, dessen Wert eine maschinell vergleichbare Gestalt hat - eine Globliste - und
895
+ # der zugleich zwei Schichten bindet. Das ist eine Enthaltung, keine Stille.
896
+ # ZWEITE GRENZE. (c) prueft nur die Richtung Quelle -> Korb. Ein ueberzaehliger Eintrag
897
+ # bleibt zulaessig - dasselbe Argument, mit dem Pruefung 42 die vier Pfadschlitze
898
+ # ausnimmt: dort ist Ueberzaehliges ohnehin erlaubt. Der gemessene Fall wird trotzdem
899
+ # gefangen, weil .github/workflows/** im Korb FEHLT.
900
+ P59_PLATZHALTER = "<EXCLUDED_PATHS>"
901
+
902
+
903
+ def _p59_globs(zellentext: list) -> list:
904
+ """Die Globs einer Wertangabe - je Backtick-Token, an Kommas getrennt.
905
+
906
+ Die beiden uebernehmenden Projekte schreiben den Wert verschieden: eines als drei
907
+ Backtick-Token, das andere als eines mit Kommas darin. Wer nur das eine kennt,
908
+ vergleicht Zeichenketten statt Mengen.
909
+ """
910
+ raus = []
911
+ for token in zellentext:
912
+ for teil in token.split(","):
913
+ t = teil.strip()
914
+ if t and t not in raus:
915
+ raus.append(t)
916
+ return raus
917
+
918
+
919
+ def check_overlay_wertabgleich(root: str, man: dict) -> None:
920
+ """Pruefung 59 (D-171): Der Overlay-Wert gilt in der Schicht, die ihn durchsetzt."""
921
+ overlay_pfad = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
922
+ if not os.path.isfile(overlay_pfad):
923
+ return # Kandidatenphase - dieselbe Enthaltung wie Pruefung 42
924
+ quelle = _p59_globs(_p56_ausgeschlossen(read(overlay_pfad)))
925
+ if not quelle or any(TBD_RE.search(g) for g in quelle):
926
+ return # noch nicht ausgefuellt - das meldet --check-overlay-ready
927
+ rel_rt = f"{man['pack_runtime_dir']}/{regeldatei(man, '20-project-overlay.md')}"
928
+ runtime_pfad = os.path.join(root, *rel_rt.split("/"))
929
+ if os.path.isfile(runtime_pfad):
930
+ text = read(runtime_pfad)
931
+ zeilen = [z for z in text.split("\n") if P59_PLATZHALTER in z]
932
+ if not zeilen:
933
+ err(f"{rel_rt}: nennt {P59_PLATZHALTER} nicht. Die Laufzeitfassung setzt den "
934
+ f"Wert damit ein, statt den Platzhalter zu binden – sie ist für sich "
935
+ f"stimmig, und niemand sieht, ob ihr Wert noch der des Quell-Overlays "
936
+ f"ist. Genau so hat eine Einengung aus 0.63.0 die Schicht nie erreicht, "
937
+ f"die sie durchsetzt (D-171)")
938
+ elif not TBD_RE.search(zeilen[0]):
939
+ # Ein Ausfuellschlitz in der Laufzeitfassung ist Sache von --strict-overlay,
940
+ # das ihn schon meldet - zwei Meldungen fuer einen Zustand sind eine zuviel.
941
+ ist = _p59_globs([t for t in re.findall(r"`([^`]+)`", zeilen[0])
942
+ if t != P59_PLATZHALTER])
943
+ fehlt = [g for g in quelle if g not in ist]
944
+ zuviel = [g for g in ist if g not in quelle]
945
+ if fehlt or zuviel:
946
+ err(f"{rel_rt}: die ausgeschlossenen Pfade weichen vom Quell-Overlay ab – "
947
+ f"dort fehlend: {', '.join(fehlt) or 'keine'}; dort nicht vorgesehen: "
948
+ f"{', '.join(zuviel) or 'keine'}. Maßgeblich ist "
949
+ f".koolie/project-overlay/OVERLAY.md; die Laufzeitfassung ist die Schicht, die "
950
+ f"der Client lädt (D-171)")
951
+ # Gegenstand (c) liest den deny-Korb einer JSON-Datei. Bis 1.4.4 stand er nicht in
952
+ # FORMATGEBUNDENE_PRUEFUNGEN und kehrte bei einem Pack mit TOML an json.loads still
953
+ # zurueck - gemessen mit CR-2026-138 an einer Wegwerf-Installation von openai-codex:
954
+ # <EXCLUDED_PATHS> in Quelle und Laufzeitfassung gefuellt, in der Berechtigungsdatei
955
+ # keine Sperre, und diese Pruefung meldete nichts. Die Luecke ist dieselbe; sie ist
956
+ # jetzt erklaert (D-346) statt verschwiegen.
957
+ if formatgebunden(man, 59):
958
+ return
959
+ rel_perm = man["permissions_file"]
960
+ perm_pfad = os.path.join(root, *rel_perm.split("/"))
961
+ if not os.path.isfile(perm_pfad):
962
+ return
963
+ try:
964
+ cfg = json.loads(read(perm_pfad))
965
+ except json.JSONDecodeError:
966
+ return # check_config hat das bereits gemeldet
967
+ deny = [r for r in cfg.get("permissions", {}).get("deny", []) if isinstance(r, str)]
968
+ if any(P59_PLATZHALTER in r for r in deny):
969
+ return # Schlitz noch ungefuellt - das ist Sache von --check-overlay-ready
970
+ praefix = man.get("permission_path_prefix", "")
971
+ werkzeuge = man.get("permission_tools", {})
972
+ for art in ("read", "write"):
973
+ for werkzeug in werkzeuge.get(art, ()):
974
+ for glob in quelle:
975
+ if any(f"{werkzeug}({p}{glob})" in deny for p in ("", praefix)):
976
+ continue
977
+ err(f"{rel_perm}: der ausgeschlossene Pfad `{glob}` des Quell-Overlays hat "
978
+ f"keine Regel {werkzeug}({glob}) im deny-Korb. Die Berechtigungsdatei "
979
+ f"ist die Schicht, die technisch sperrt – ein Wert, der nur im Overlay "
980
+ f"steht, sperrt nichts (D-171)")
981
+
982
+
983
+ # --- Pruefung 89: die uebrigen Pfadplatzhalter in den Schichten, die sie tragen ------
984
+ #
985
+ # ANLASS (K-69, CR-2026-136 E4, CR-2026-138 E6). Pruefung 59 vergleicht EINEN
986
+ # Pfadplatzhalter. <ALLOWED_PATHS>, <TEST_PATHS>, <DOC_PATHS> und <READ_ONLY_PATHS> haben
987
+ # dieselbe Gestalt - eine Globliste - und waren ungeprueft, nicht geprueft-und-gut:
988
+ # Gezaehlt am 2026-09-18 wich von sechs Werten des Uebungs-Overlays einer ab. Weil
989
+ # Laufzeitfassung und Berechtigungsdatei Saat bleiben (D-353), faengt nur eine Pruefung
990
+ # die Handpflege an drei Stellen auf.
991
+ #
992
+ # DREI GEGENSTAENDE, dieselben wie bei 59:
993
+ # (a) Die Laufzeitfassung NENNT jeden der vier Platzhalter. Ein Traeger, der den Wert
994
+ # nur einsetzt, ist fuer sich stimmig - und niemand sieht, ob sein Wert noch der
995
+ # der Quelle ist (D-160).
996
+ # (b) Der Wert hinter dem Platzhalter ist dieselbe Menge wie in der Quelle.
997
+ # (c) Jeder Nur-Lese-Pfad hat im deny-Korb eine Schreibsperre. Seit 1.5.0 fuehrt die
998
+ # Kernquelle den Schlitz dafuer (D-356); bis dahin trugen beide uebernehmenden
999
+ # Projekte die Sperre von Hand - auf dieselbe Weise.
1000
+ # Seit 1.20.1 ebenso jeder Glob von <CI_CONFIG_PATHS> und
1001
+ # <QUALITY_GATE_CONFIG_PATHS> (CR-2026-163, D-493, K-35). Beide stehen nur im
1002
+ # deny-Korb, und ein zu ENG gefuellter Schlitz ist eine stille Lockerung.
1003
+ # "Deckungsgleich" heisst: Jeder Glob der Quelle hat seine eigene Regel - so
1004
+ # entfaltet install.py den Schlitz auch. Die Laufzeitfassung fuehrt die beiden
1005
+ # nicht; (a) und (b) gelten fuer sie deshalb nicht.
1006
+ #
1007
+ # DIE QUELLE IST DIE DREISPALTIGE ZEILE VON ABSCHNITT 4, gefunden ueber die
1008
+ # Platzhalterzelle. Ein Overlay, das anders bindet, ENTGEHT der Pruefung nicht still: Es
1009
+ # bekommt eine eigene Meldung (CR-2026-136 E4, "melden, nicht verschweigen").
1010
+ #
1011
+ # "KEIN WERT" HAT MEHRERE SCHREIBWEISEN, und sie sind gemessen: Der Pilot fuehrt fuer
1012
+ # <DOC_PATHS> in der Quelle `nicht vorhanden`, in der Laufzeitfassung `keine`. Beides
1013
+ # ist die leere Menge; wer Zeichenketten vergleicht, meldet einen Unterschied, den es
1014
+ # nicht gibt.
1015
+ #
1016
+ # GRENZE. Der Wert in der Laufzeitfassung endet am ersten " · " oder " – " der Zeile;
1017
+ # was danach steht ("zusaetzlich immer ...") ist Erlaeuterung und gehoert nicht zur
1018
+ # Menge. Und (c) prueft wie 59 nur die Richtung Quelle -> Korb.
1019
+ P89_PLATZHALTER = ("<ALLOWED_PATHS>", "<TEST_PATHS>", "<DOC_PATHS>", "<READ_ONLY_PATHS>")
1020
+ P89_SPERRPLATZHALTER = ("<READ_ONLY_PATHS>", "<CI_CONFIG_PATHS>", "<QUALITY_GATE_CONFIG_PATHS>")
1021
+ P89_KEIN_WERT = {"nicht vorhanden", "keine", "keiner", "kein", "–", "-"}
1022
+
1023
+
1024
+ def _p89_menge(werte: list) -> list:
1025
+ """Die Globs einer Wertangabe ohne die Schreibweisen fuer "kein Wert"."""
1026
+ return [g for g in _p59_globs(werte) if g.lower() not in P89_KEIN_WERT]
1027
+
1028
+
1029
+ def _p89_quelle(text: str, platzhalter: str):
1030
+ """(Zeile gefunden, Werte oder None bei Ausfuellschlitz) aus Abschnitt 4 des Overlays."""
1031
+ for zeile in text.replace("\r\n", "\n").split("\n"):
1032
+ zellen = tabellenzellen(zeile.strip())
1033
+ for i, zelle in enumerate(zellen[:-1]):
1034
+ if zelle.strip() != f"`{platzhalter}`":
1035
+ continue
1036
+ wert = zellen[i + 1]
1037
+ if TBD_RE.search(wert):
1038
+ return True, None
1039
+ treffer = re.findall(r"`([^`]+)`", wert)
1040
+ return True, _p89_menge(treffer or [wert.strip()])
1041
+ return False, None
1042
+
1043
+
1044
+ def _p89_laufzeit(text: str, platzhalter: str):
1045
+ """(Zeile gefunden, Werte oder None bei Ausfuellschlitz) aus der Laufzeitfassung."""
1046
+ token = f"`{platzhalter}`"
1047
+ for zeile in text.replace("\r\n", "\n").split("\n"):
1048
+ if token not in zeile:
1049
+ continue
1050
+ rest = zeile.split(token, 1)[1].lstrip(")").lstrip(":").strip()
1051
+ rest = re.split(r" · | – ", rest, maxsplit=1)[0]
1052
+ if TBD_RE.search(rest):
1053
+ return True, None
1054
+ treffer = [t for t in re.findall(r"`([^`]+)`", rest)
1055
+ if not PROJEKTPLATZHALTER.fullmatch(t)]
1056
+ return True, _p89_menge(treffer or [rest.strip().rstrip(".")])
1057
+ return False, None
1058
+
1059
+
1060
+ def check_overlay_pfadabgleich(root: str, man: dict) -> None:
1061
+ """Pruefung 89 (K-69, D-357): die vier uebrigen Pfadplatzhalter in allen Schichten."""
1062
+ overlay_pfad = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
1063
+ if not os.path.isfile(overlay_pfad):
1064
+ return # Kandidatenphase - dieselbe Enthaltung wie Pruefung 59
1065
+ quelltext = read(overlay_pfad)
1066
+ rel_rt = f"{man['pack_runtime_dir']}/{regeldatei(man, '20-project-overlay.md')}"
1067
+ runtime_pfad = os.path.join(root, *rel_rt.split("/"))
1068
+ laufzeit = read(runtime_pfad) if os.path.isfile(runtime_pfad) else None
1069
+ sperren: dict = {}
1070
+ for platzhalter in P89_PLATZHALTER:
1071
+ da, quelle = _p89_quelle(quelltext, platzhalter)
1072
+ if not da:
1073
+ err(f".koolie/project-overlay/OVERLAY.md: keine Zeile mit der Platzhalterzelle "
1074
+ f"`{platzhalter}` in Abschnitt 4. Prüfung 89 liest den Wert aus der "
1075
+ f"dreispaltigen Zeile der Vorlage; ein Overlay, das anders bindet, wird "
1076
+ f"gemeldet und nicht übergangen (K-69, CR-2026-136 E4)")
1077
+ continue
1078
+ if quelle is None:
1079
+ continue # noch nicht ausgefuellt - das meldet --check-overlay-ready
1080
+ if platzhalter == "<READ_ONLY_PATHS>":
1081
+ sperren[platzhalter] = quelle
1082
+ if laufzeit is None:
1083
+ continue
1084
+ da, ist = _p89_laufzeit(laufzeit, platzhalter)
1085
+ if not da:
1086
+ err(f"{rel_rt}: nennt {platzhalter} nicht. Die Laufzeitfassung setzt den Wert "
1087
+ f"damit ein, statt den Platzhalter zu binden – sie ist für sich stimmig, und "
1088
+ f"niemand sieht, ob ihr Wert noch der des Quell-Overlays ist (D-160, K-69)")
1089
+ continue
1090
+ if ist is None:
1091
+ continue # Ausfuellschlitz in der Laufzeitfassung - Sache von --strict-overlay
1092
+ fehlt = [g for g in quelle if g not in ist]
1093
+ zuviel = [g for g in ist if g not in quelle]
1094
+ if fehlt or zuviel:
1095
+ err(f"{rel_rt}: der Wert von {platzhalter} weicht vom Quell-Overlay ab – dort "
1096
+ f"fehlend: {', '.join(fehlt) or 'keine'}; dort nicht vorgesehen: "
1097
+ f"{', '.join(zuviel) or 'keine'}. Maßgeblich ist "
1098
+ f".koolie/project-overlay/OVERLAY.md (K-69)")
1099
+ for platzhalter in P89_SPERRPLATZHALTER[1:]:
1100
+ da, quelle = _p89_quelle(quelltext, platzhalter)
1101
+ if da and quelle:
1102
+ sperren[platzhalter] = quelle # fehlende Zeile: dasselbe Schweigen wie --strict
1103
+ if not sperren or formatgebunden(man, 89):
1104
+ return
1105
+ rel_perm = man["permissions_file"]
1106
+ perm_pfad = os.path.join(root, *rel_perm.split("/"))
1107
+ if not os.path.isfile(perm_pfad):
1108
+ return
1109
+ try:
1110
+ cfg = json.loads(read(perm_pfad))
1111
+ except json.JSONDecodeError:
1112
+ return # check_config hat das bereits gemeldet
1113
+ deny = [r for r in cfg.get("permissions", {}).get("deny", []) if isinstance(r, str)]
1114
+ praefix = man.get("permission_path_prefix", "")
1115
+ for platzhalter, globs in sperren.items():
1116
+ if any(platzhalter in r for r in deny):
1117
+ continue # Schlitz noch ungefuellt - das ist Sache von --strict-overlay
1118
+ for werkzeug in man.get("permission_tools", {}).get("write", ()):
1119
+ for glob in globs:
1120
+ if any(f"{werkzeug}({p}{glob})" in deny for p in ("", praefix)):
1121
+ continue
1122
+ if platzhalter == "<READ_ONLY_PATHS>":
1123
+ err(f"{rel_perm}: der Nur-Lese-Pfad `{glob}` des Quell-Overlays hat keine "
1124
+ f"Regel {werkzeug}({glob}) im deny-Korb. Ein Integritätsschutz, der nur "
1125
+ f"im Overlay steht, sperrt nichts (K-69, D-356)")
1126
+ else:
1127
+ err(f"{rel_perm}: der Pfad `{glob}` aus {platzhalter} des Quell-Overlays "
1128
+ f"hat keine Regel {werkzeug}({glob}) im deny-Korb. Ein zu eng "
1129
+ f"gefüllter Schlitz ist eine stille Lockerung (K-35, D-493)")
1130
+
1131
+ # --- Pruefung 110: die aktivierten Packs gegen die Regelablage (CR-2026-164, D-504, K-44)
1132
+ #
1133
+ # ANLASS, GEGENGEPRUEFT (2026-09-17, CR-2026-076 Abschnitt 6.3): Das Overlay des
1134
+ # Uebungsrepositoriums fuehrt zwei Role Packs und zwei Technology Packs als aktiviert. Vier
1135
+ # Regeldateien und zwei Skills entfernt - --strict-overlay meldete vorher wie nachher
1136
+ # dasselbe. Ein Projekt verlor so seine Ebenen 5 und 6 still: durch einen Packwechsel, einen
1137
+ # verunglueckten Lauf von --update oder ein Versehen. Aufgefallen war es dem KI-Client, nicht dem
1138
+ # Pruefapparat.
1139
+ #
1140
+ # GEPRUEFT (nur --strict-overlay): Die Zeilen 'Aktivierte Role Packs' und 'Aktivierte
1141
+ # Technology Packs' des Overlays gegen die Laufzeitfassungen 30-role-<pack> und
1142
+ # 40-tech-<pack> der Regelablage, in beide Richtungen. Ein Pack gilt als genannt, wenn sein
1143
+ # Name in Backticks in der Wertzelle steht ('tech-' davor ist erlaubt); `keine` heisst
1144
+ # keines. Eine Zeile, die noch `<TBD…>` traegt, wird nicht gelesen.
1145
+ # FEHLER: ein genanntes Pack ohne Laufzeitfassung und eine Laufzeitfassung ohne Nennung.
1146
+ # WARNUNG: Laufzeitfassungen, aber keine Zeile - die Vorlage fuehrt die Zeile fuer Role
1147
+ # Packs erst seit 1.20.2.
1148
+ # GRENZE: die Laufzeitfassung, nicht die Skills eines Packs (die haelt Pruefung 72 gegen
1149
+ # die Berechtigungen); die Nennung, nicht die Version.
1150
+ P110_ZEILEN = (("Aktivierte Role Packs", "30-role-"), ("Aktivierte Technology Packs", "40-tech-"))
1151
+ P110_NAME_RE = re.compile(r"`([a-z0-9][a-z0-9-]*)`")
1152
+
1153
+
1154
+ def _p110_genannt(text: str, beschriftung: str):
1155
+ """(Zeile vorhanden?, genannte Packs oder None bei `<TBD…>`)."""
1156
+ m = re.search(r"^\|\s*" + re.escape(beschriftung) + r"[^|]*\|([^|\n]*)\|", text, re.M)
1157
+ if not m:
1158
+ return False, None
1159
+ zelle = m.group(1)
1160
+ if TBD_RE.search(zelle):
1161
+ return True, None
1162
+ namen = {n[len("tech-"):] if n.startswith("tech-") else n
1163
+ for n in P110_NAME_RE.findall(zelle)}
1164
+ return True, namen - {"keine"}
1165
+
1166
+
1167
+ def check_aktivierte_packs(root: str, man: dict) -> None:
1168
+ """Pruefung 110 (D-504, K-44): Was das Overlay als aktiviert fuehrt, liegt in der Regelablage."""
1169
+ overlay_pfad = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
1170
+ ablage = os.path.join(root, *man["pack_runtime_dir"].split("/"))
1171
+ if not os.path.isfile(overlay_pfad) or not os.path.isdir(ablage):
1172
+ return
1173
+ text = read(overlay_pfad)
1174
+ endung = regeldatei(man, "x.md")[1:]
1175
+ dateien = [f for f in os.listdir(ablage) if f.endswith(endung)]
1176
+ for beschriftung, praefix in P110_ZEILEN:
1177
+ da = {f[len(praefix):-len(endung)] for f in dateien if f.startswith(praefix)}
1178
+ zeile, genannt = _p110_genannt(text, beschriftung)
1179
+ if not zeile:
1180
+ if da:
1181
+ warn(f".koolie/project-overlay/OVERLAY.md: keine Zeile '{beschriftung}', aber "
1182
+ f"{len(da)} Laufzeitfassung(en) {praefix}* in {man['pack_runtime_dir']}/ - "
1183
+ f"welches Pack aktiviert ist, entscheidet das Overlay (K-44)")
1184
+ continue
1185
+ if genannt is None:
1186
+ continue
1187
+ for pack in sorted(genannt - da):
1188
+ err(f".koolie/project-overlay/OVERLAY.md: führt '{pack}' unter '{beschriftung}', "
1189
+ f"aber {man['pack_runtime_dir']}/{praefix}{pack}{endung} fehlt - die Ebene "
1190
+ f"dieses Packs lädt in keiner Sitzung (K-44)")
1191
+ for pack in sorted(da - genannt):
1192
+ err(f"{man['pack_runtime_dir']}/{praefix}{pack}{endung}: lädt in jeder passenden "
1193
+ f"Sitzung, aber das Overlay führt '{pack}' nicht unter '{beschriftung}' - "
1194
+ f"aktiviert wird ein Pack im Overlay (K-44)")
1195
+
1196
+
1197
+ # --- Pruefung 111: fremde Skills, deklariert (CR-2026-167, D-514, K-31) ----------------
1198
+ #
1199
+ # ANLASS, GEMESSEN (2026-09-30): OpenSpec 1.13.2 und GitHub Spec Kit legen ihre Skills in
1200
+ # dieselbe Laufzeitablage wie Koolie (.claude/skills/openspec-*, speckit-*). Der Validator
1201
+ # prueft jeden Skill dort nach den Regeln fuer Koolie-Skills - sechs OpenSpec-Skills ergaben
1202
+ # 60 Fehler und 18 Warnungen, in einem Projekt, in dem beide Rahmenwerke einander keine
1203
+ # Datei aendern. Seither deklariert das Projekt sie im Overlay-Manifest
1204
+ # ('fremde_skills: openspec-, speckit-'), und Pruefung 5 nimmt sie aus.
1205
+ #
1206
+ # GEPRUEFT: FEHLER, wenn ein deklariertes Praefix einen Koolie-Skill treffen koennte (fw-,
1207
+ # prj-, role-, tech-, oder kuerzer als drei Zeichen) - die Deklaration waere sonst ein Weg,
1208
+ # die eigenen Skills der Pruefung zu entziehen; ein solches Praefix nimmt nichts aus.
1209
+ # WARNUNG, wenn ein Praefix keinen Skill der Laufzeitablage trifft (Deklaration ohne
1210
+ # Gegenstand), wenn koexistenz.py ein bekanntes fremdes Rahmenwerk mit Skills erkennt,
1211
+ # deren Praefix nicht deklariert ist, und wenn die Wurzel-Anweisung den markierten Block
1212
+ # eines fremden Generators traegt - er zaehlt ins Budget (Pruefung 4, K-185), und die
1213
+ # naechste Aktualisierung bricht davor ab (D-515).
1214
+ # GRENZE: die Deklaration und die Ablage, nicht der Inhalt eines fremden Skills. Den Korb
1215
+ # der Berechtigungsdatei verlangt Pruefung 72 weiter (D-238); was ein fremder Skill tut,
1216
+ # prueft Koolie nicht - das ist die Abgrenzung nach Gegenstand (D-478).
1217
+ def check_fremde_skills(root: str, man: dict) -> None:
1218
+ """Pruefung 111 (D-514, K-31): deklarierte fremde Skills - zulaessig und mit Gegenstand."""
1219
+ manifest = os.path.join(root, ".koolie", "project-overlay", "overlay-manifest.yaml")
1220
+ if not os.path.isfile(manifest):
1221
+ return
1222
+ fremde_praefixe(root) # legt den Kern in den Suchpfad
1223
+ try:
1224
+ import koexistenz
1225
+ except ImportError:
1226
+ return
1227
+ wurzel = man.get("root_instruction_file", "")
1228
+ wurzel_pfad = os.path.join(root, *wurzel.split("/")) if wurzel else ""
1229
+ if wurzel_pfad and os.path.isfile(wurzel_pfad):
1230
+ for name, zeichen in koexistenz.markierte_bloecke(read(wurzel_pfad)):
1231
+ warn(f"{wurzel}: traegt den markierten Block '{name}' eines fremden Generators "
1232
+ f"({zeichen} Zeichen) - er zaehlt ins Budget der stets geladenen Texte "
1233
+ f"(K-185), und install.py --update bricht davor ab (D-515)")
1234
+ ablage = os.path.join(root, *man["skills_dir"].split("/"))
1235
+ skills = sorted(os.listdir(ablage)) if os.path.isdir(ablage) else []
1236
+ deklariert = koexistenz.deklariert(root)
1237
+ rel = ".koolie/project-overlay/overlay-manifest.yaml"
1238
+ for p in deklariert:
1239
+ if not koexistenz.zulaessig(p):
1240
+ err(f"{rel}: fremde_skills nennt '{p}' - das Praefix koennte einen Koolie-Skill "
1241
+ f"treffen (fw-, prj-, role-, tech-) und nimmt deshalb nichts aus (D-514)")
1242
+ elif not any(s.startswith(p) for s in skills):
1243
+ warn(f"{rel}: fremde_skills nennt '{p}', aber kein Skill in {man['skills_dir']}/ "
1244
+ f"beginnt damit - eine Deklaration ohne Gegenstand (D-514)")
1245
+ for b in koexistenz.erkennen(root, man["skills_dir"]):
1246
+ offen = [s for s in b["skills"] if not koexistenz.ist_deklariert(s, deklariert)]
1247
+ if b["name"] != "unbekannt" and offen:
1248
+ warn(f"{man['skills_dir']}/: {len(offen)} Skill(s) von {b['name']} "
1249
+ f"({b['praefix']}*) sind nicht deklariert - im Overlay-Manifest "
1250
+ f"'fremde_skills: {b['praefix']}' eintragen, sonst prueft Pruefung 5 sie als "
1251
+ f"Koolie-Skills (K-31, D-514)")