@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,1262 @@
1
+ """Die Dokumente: Steuerzeichen, Altname, Dokumentstand und -zahlen, Lizenz,
2
+ Gegenzeichnung, Zeilenendeform, Bestandsliste, Chronikspanne, Zielangabe,
3
+ Rechtschreibung, Form und Steckbrief.
4
+
5
+ Pruefungen 66, 75, 77, 78, 79, 80, 81, 82, 83, 85, 92, 93 und 94. 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 os
12
+ import re
13
+ import subprocess
14
+
15
+ from .gemeinsam import (
16
+ _git_pfade, _verfolgte_dateien, DOK_WURZEL, err, hinweis, ist_quellrepositorium,
17
+ iter_text_files, KERN, nicht_geliefert, QUELLREPO_KENNZEICHEN, read,
18
+ REGISTER_ANKER, REGISTER_ENDE, tabellenzellen, TBD_RE, TEXT_EXT, warn)
19
+
20
+
21
+ # --- Pruefung 66: das verirrte Steuerzeichen ---------------------------------------
22
+ #
23
+ # GEGENSTAND ist ein Wagenruecklauf, dem kein Zeilenvorschub folgt. Er rendert nicht,
24
+ # er druckt nicht, und keine Leseroutine dieses Apparats gibt ihn heraus.
25
+ #
26
+ # 🔴 WARUM ER TROTZDEM ETWAS KAPUTT MACHT (D-217, CR-2026-107): git stuft einen
27
+ # Traeger mit einem einzelnen CR als BINAER ein und normalisiert seine Zeilenenden
28
+ # deshalb NICHT - weder ueber core.autocrlf noch ueber ein text=auto in einer
29
+ # .gitattributes. Gemessen am 2026-09-20 in einem eigens dafuer gebauten
30
+ # Repositorium, beide Faelle nebeneinander: Die Datei ohne verirrtes CR liegt danach
31
+ # als LF im Blob, die mit ihm unveraendert als CRLF.
32
+ # ➡️ DIE REGEL FUER ZEILENENDEN GREIFT BEI GENAU DEN DATEIEN NICHT, DIE SIE BRAUCHEN.
33
+ # Im Bestand war die Deckung vollstaendig: 14 Traeger mit verirrtem CR und 14 Traeger,
34
+ # deren Blob git nicht normalisiert hat - dieselben 14 von 440 Texttraegern.
35
+ #
36
+ # 🔴 UND KEINE DER 65 AELTEREN PRUEFUNGEN KONNTE ES SEHEN. `read` oeffnet im
37
+ # Universal-Newline-Modus; dort ist jedes CR bereits ein Zeilenvorschub, bevor eine
38
+ # Pruefung es zu Gesicht bekommt. Diese liest deshalb Bytes. Eine Pruefung, die ihren
39
+ # Gegenstand an der eigenen Leseroutine verliert, ist die stillste Bauform von D-23.
40
+ VERIRRTES_CR = re.compile(bytes([13]) + b'(?!' + bytes([10]) + b')')
41
+
42
+
43
+ def check_verirrtes_steuerzeichen(root: str) -> None:
44
+ """Pruefung 66 (D-217): Kein Textraeger traegt ein CR ohne folgenden LF."""
45
+ geprueft = 0
46
+ for path in iter_text_files(root):
47
+ try:
48
+ with open(path, "rb") as fh:
49
+ roh = fh.read()
50
+ except OSError:
51
+ continue
52
+ if b"\x00" in roh[:8000]:
53
+ continue
54
+ geprueft += 1
55
+ treffer = VERIRRTES_CR.findall(roh)
56
+ if not treffer:
57
+ continue
58
+ rel = os.path.relpath(path, root).replace(os.sep, "/")
59
+ stelle = VERIRRTES_CR.search(roh).start()
60
+ err(f"{rel}: {len(treffer)} verirrte(s) Steuerzeichen - Wagenrücklauf ohne "
61
+ f"folgenden Zeilenvorschub, das erste an Byte {stelle}. Es ist unsichtbar "
62
+ f"und nimmt git die Normalisierung: Ein solcher Träger gilt als binär, "
63
+ f"landet unverändert im Repositorium und lässt den nächsten Commit die "
64
+ f"ganze Datei neu schreiben (D-217)")
65
+ if not geprueft:
66
+ err(f"{KERN}/: keine einzige Textdatei gelesen – Prüfung 66 hat ihren "
67
+ f"Gegenstand verloren und bestünde sonst leise (D-23)")
68
+
69
+ # --- Pruefung 75: Kein Restbestand des alten Namens ---------------------------------
70
+ #
71
+ # ANLASS (D-271, CR-2026-119 E3). Mit 0.88.0 heisst das Framework Koolie und der Kern
72
+ # liegt unter .koolie/core. Ohne eine Pruefung ist "der Name ist weg" eine BEHAUPTUNG
73
+ # ohne Pruefung - und genau das ist der wiederkehrende Befundtyp dieses Repositoriums.
74
+ #
75
+ # ZAEHLBEREICH, UND ER HAENGT DAVON AB, WESSEN BESTAND GEPRUEFT WIRD.
76
+ #
77
+ # * Im FRAMEWORK-REPOSITORIUM: jeder verfolgte Traeger. Das ist die Lehre von D-295 -
78
+ # der Zaehlbereich von Kriterium 1 war kleiner als die Wirkungsflaeche, und wer nur
79
+ # den Kern sweept, laesst die Form in der Wurzel-README stehen.
80
+ # * In einem UEBERNEHMENDEN PROJEKT: nur, was das Framework AUSLIEFERT - der Kern und
81
+ # die Laufzeitschicht. Was ein Projekt in seine eigenen Dateien schreibt, geht das
82
+ # Framework nichts an; es darf eine Belegablage nennen, die den alten Namen traegt.
83
+ # Gemessen am 2026-09-22 im Uebungsrepositorium: zwei solche Nennungen, beide
84
+ # legitim nach D-300, und die Pruefung meldete sie als Fehler (D-299).
85
+ #
86
+ # ERKANNT WIRD DAS AM KENNZEICHEN DES QUELLREPOSITORIUMS (D-351). Bis 1.4.0 war es
87
+ # die Uebergabe; seit sie nicht mehr versioniert ist (D-350), traegt eine eigene
88
+ # Datei den Anker. Sie wandert NICHT in ein Zielprojekt; `install.py` legt sie nicht
89
+ # an, und das Heben kopiert nur den Kern. Ein Anker, der sich selbst belegt.
90
+ # ➡️ Eine Pruefung, die im Framework gruen und in jeder Installation rot ist, ist
91
+ # falsch gebaut - dieselbe Ueberlegung, mit der Pruefung 46 ihren Zaehlbereich auf
92
+ # den Kern beschraenkt: Was nicht in jeder Installation gleich ist, gehoert nicht in
93
+ # eine installationsunabhaengige Aussage.
94
+ #
95
+ # AUSGENOMMEN, UND ZWAR DEKLARIERT (dieselbe Bauform wie P73_OFFEN und LINK_EXCEPTIONS):
96
+ #
97
+ # (a) DIE CHRONIK. Protokolle, Aenderungsantraege, CHANGELOG und Decision Log
98
+ # beschreiben einen vergangenen Zustand (D-125, D-273). Ein Pfad, den es nicht
99
+ # mehr gibt, ist dort richtig - er hat damals existiert.
100
+ # (b) NAMEN DATIERTER BELEGABLAGEN AUSSERHALB DES REPOSITORIUMS. Die Erhebungen
101
+ # heissen `leitwerk-erhebungen-<datum>-<buendel>` und werden NICHT umbenannt:
102
+ # Eine Reihe von Belegablagen, deren Namenskonvention mitten in der Reihe
103
+ # wechselt, ist nicht mehr als Reihe auffindbar (D-300).
104
+ # (c) AUSSAGEN UEBER DEN ALTEN NAMEN. Der Changelog-Eintrag zu 0.7.0, die
105
+ # Entscheidungsaufzeichnung im Releaseplan und die Kommentare, die erklaeren,
106
+ # warum eine Stelle umgebaut wurde. Ein Sweep kehrt sie ins Sinnlose - das war
107
+ # der teuerste Befund des Umbenennungslaufs (D-301).
108
+ # (d) DER PRUEFAPPARAT DIESER PRUEFUNG SELBST. Dieser Validator traegt das Muster,
109
+ # und probe-pruefungen.py SCHREIBT den alten Namen in den Sondenbaum, um die
110
+ # Pruefung ueberhaupt messen zu koennen. Die Klasse ist strukturell
111
+ # unvermeidbar: Eine Pruefung, die einen Namen sucht, muss ihn nennen - und ihre
112
+ # Sonde muss ihn schreiben. Beide Traeger sind beim ersten Lauf dieser Pruefung
113
+ # aufgefallen, weil sie sich selbst gemeldet hat.
114
+ #
115
+ # IN BEIDE RICHTUNGEN GEPRUEFT. Ein nicht gelisteter Traeger mit Fundstelle faellt auf,
116
+ # ein gelisteter OHNE Fundstelle ebenso: Eine Ausnahme, die nichts mehr ausnimmt, sieht
117
+ # wie Sorgfalt aus und ist toter Code (0.57.1).
118
+ #
119
+ # PREIS, UND ER IST BENANNT: Die Ausnahme gilt je TRAEGER, nicht je Zeile. Eine neue,
120
+ # falsche Nennung in einem gelisteten Traeger faellt nicht auf. Die Gegenseite waere
121
+ # eine Zahl je Traeger - und eine Zahl, die gepflegt werden muss, wird nicht gepflegt
122
+ # (zweimal an derselben Tabelle dieses Projekts belegt).
123
+ #
124
+ # DER PREIS IST BEIM ERSTEN LAUF ANGEFALLEN, an der Chronik statt an der Ausnahmemenge:
125
+ # CHANGELOG.md fuehrt in Zeile 3 den Satz "Prozess: <pfad>/RELEASE_PROCESS.md", und
126
+ # tests/protocols/README.md nennt in Zeile 3 den Testkatalog. Beides sind KOPFZEILEN,
127
+ # die einen AKTUELLEN Pfad nennen - keine Chronik -, und die Chronikausnahme deckt sie
128
+ # mit. Beide sind von Hand nachgezogen worden.
129
+ # ➡️ Ein Chroniktraeger kann eine Zeile fuehren, die keine Chronik ist. Wer eine
130
+ # Ausnahme je Traeger zieht, liest den KOPF des Traegers, bevor er sie zieht.
131
+ P75_ALTNAME = re.compile(r"(?i)leitwerk")
132
+
133
+ P75_CHRONIK = (
134
+ KERN + "/CHANGELOG.md",
135
+ KERN + "/governance/DECISION_LOG.md",
136
+ KERN + "/governance/change-requests/",
137
+ KERN + "/tests/protocols/",
138
+ )
139
+
140
+ P75_AUSNAHMEN = {
141
+ # (b) Namen datierter Belegablagen ausserhalb des Repositoriums
142
+ KERN + "/tests/erhebungen/README.md",
143
+ KERN + "/tests/erhebungen/ablage.py",
144
+ KERN + "/tests/erhebungen/baeume-b4.py",
145
+ KERN + "/tests/erhebungen/umgebungen-bauen-b4.py",
146
+ KERN + "/tests/scripts/validate-framework.py",
147
+ KERN + "/tests/scripts/pruefungen/hooks.py",
148
+ # (c) Aussagen ueber den alten Namen
149
+ KERN + "/build/doc/32-abschluss.md",
150
+ KERN + "/clientmap.py",
151
+ # (d) Der Pruefapparat dieser Pruefung selbst - seit 1.19.1 in den Modulen, die
152
+ # ihr Muster und ihre Sonden tragen (K-174)
153
+ KERN + "/tests/scripts/pruefungen/dokumente.py",
154
+ KERN + "/tests/scripts/sonden/teil08_pruefungen_66_bis_80.py",
155
+ }
156
+
157
+
158
+ def _p75_chronik(rel: str) -> bool:
159
+ return any(rel == c or rel.startswith(c) for c in P75_CHRONIK)
160
+
161
+
162
+ def _p75_verfolgt(root: str) -> list | None:
163
+ """Alle verfolgten Pfade des Repositoriums - oder None, wenn nicht messbar.
164
+
165
+ Nicht _verfolgte_dateien(): die liest nur unter KERN, und der Zaehlbereich dieser
166
+ Pruefung ist ausdruecklich groesser (D-295).
167
+ """
168
+ try:
169
+ lauf = subprocess.run(["git", "-C", root, "ls-files", "-z"],
170
+ capture_output=True, text=True, encoding="utf-8",
171
+ errors="replace", timeout=30)
172
+ except (OSError, subprocess.SubprocessError):
173
+ return None
174
+ if lauf.returncode != 0:
175
+ return None
176
+ return _git_pfade(lauf.stdout)
177
+
178
+
179
+ def check_altname_restbestand(root: str, man: dict) -> None:
180
+ """Pruefung 75 (D-271): Kein Traeger nennt den alten Namen ausser den erklaerten."""
181
+ if not P75_ALTNAME.search("LeItWeRk"):
182
+ err("Prüfung 75: das eigene Muster findet den alten Namen nicht mehr – sie "
183
+ "bestünde leise und meldete jede Umbenennung als vollständig (D-23)")
184
+ return
185
+ dateien = _p75_verfolgt(root)
186
+ if dateien is None:
187
+ warn("Prüfung 75: kein Git-Bestand lesbar – der Restbestand des alten Namens "
188
+ "ist an diesem Ort nicht prüfbar. Das ist KEIN Messergebnis (D-23)")
189
+ return
190
+ bestand = set(dateien)
191
+ # Das Framework-Repositorium fuehrt das Kennzeichen; ein uebernehmendes Projekt
192
+ # nicht (D-351). Gelesen am VERSIONIERTEN Bestand, wie der Zaehlbereich selbst.
193
+ eigenes_repo = QUELLREPO_KENNZEICHEN in bestand
194
+ ausgeliefert = tuple(
195
+ d + "/" for d in (KERN, man.get("runtime_dir", "")) if d) + tuple(
196
+ f for f in (man.get("root_instruction_file", ""),) if f)
197
+ gefunden = set()
198
+ for rel in sorted(bestand):
199
+ if _p75_chronik(rel):
200
+ continue
201
+ if not eigenes_repo and not rel.startswith(ausgeliefert):
202
+ continue
203
+ if os.path.splitext(rel)[1] not in TEXT_EXT:
204
+ continue
205
+ pfad = os.path.join(root, *rel.split("/"))
206
+ if not os.path.isfile(pfad):
207
+ continue
208
+ treffer = P75_ALTNAME.findall(read(pfad))
209
+ if not treffer:
210
+ continue
211
+ gefunden.add(rel)
212
+ if rel in P75_AUSNAHMEN:
213
+ continue
214
+ err(f"{rel}: nennt den alten Namen noch {len(treffer)}mal. Das Framework heißt "
215
+ f"seit 0.88.0 Koolie, der Kern liegt unter {KERN} (D-271). Steht die Nennung "
216
+ f"dort mit Grund – eine Aufzeichnung, ein Belegablagenname –, gehört der "
217
+ f"Träger als MENGE in P75_AUSNAHMEN und wird nicht als Einzelfall "
218
+ f"durchgelassen")
219
+ for rel in sorted(P75_AUSNAHMEN - gefunden):
220
+ # EIN TRAEGER, DEN DIESE INSTALLATION NICHT FUEHRT, IST KEINE LEERE AUSNAHME.
221
+ # Die Menge beschreibt den Bestand des Framework-Repositoriums; ein
222
+ # uebernehmendes Projekt hat einen anderen - es fuehrt kein Kennzeichen, und
223
+ # sein Kern ist erst nach dem ersten `git add` verfolgt. Ohne diese
224
+ # Unterscheidung waere die Pruefung im Framework gruen und in JEDER
225
+ # Installation rot; gemessen am 2026-09-22 im Uebungsrepositorium, mit neun
226
+ # Meldungen (D-299). Pruefung 46 loest denselben Anspruch mit demselben
227
+ # Gedanken: Was nicht in jeder Installation gleich ist, gehoert nicht in eine
228
+ # installationsunabhaengige Aussage.
229
+ if rel not in bestand:
230
+ continue
231
+ err(f"P75_AUSNAHMEN führt '{rel}' als erklärte Fundstelle des alten Namens – "
232
+ f"dort steht er nicht mehr. Die Ausnahme nimmt nichts mehr aus und "
233
+ f"gehört entfernt (0.57.1)")
234
+
235
+
236
+ # ---------------------------------------------------------------------------
237
+ # Pruefung 77: Das Hauptdokument nennt den Stand, auf dem es gebaut ist
238
+ # ---------------------------------------------------------------------------
239
+ #
240
+ # Sie war die Schwester der mit 1.4.1 entfallenen Pruefung 67 (D-350). Dort hielt die
241
+ # Titelzeile der Uebergabe gegen VERSION, hier die Kopfzeile des Hauptdokuments - und
242
+ # der Unterschied war, dass die Uebergabe jede Sitzung gelesen wurde und das
243
+ # Hauptdokument nur zur Auslieferung.
244
+ # Genau deshalb ist es zweiundvierzig Releases zurueckgefallen, ohne dass es jemandem
245
+ # aufgefallen waere.
246
+ #
247
+ # WARUM DIE QUELLE UND NICHT DAS ERZEUGNIS. build/out/hauptdokument.md steht in der
248
+ # .gitignore und ist in einer frischen Auscheckung gar nicht da; eine Pruefung dagegen
249
+ # waere in jeder Installation rot. Der Gegenstand ist die KAPITELQUELLE, und die ist
250
+ # versioniert - derselbe Gedanke wie bei Pruefung 46 und 75.
251
+ DOKUMENT_KOPF = KERN + "/build/doc/00-kopf.md"
252
+ DOKUMENT_VERSION_RE = re.compile(
253
+ r"^\|\s*Dokumentversion\s*\|\s*(\d+\.\d+\.\d+)\s*"
254
+ r"\(entspricht Framework-Release\s*(\d+\.\d+\.\d+)\)\s*\|", re.M)
255
+
256
+
257
+ def check_dokumentstand(root: str) -> None:
258
+ """Pruefung 77 (D-312): Das Hauptdokument steht auf dem Stand des Kerns."""
259
+ pfad = os.path.join(root, *DOKUMENT_KOPF.split("/"))
260
+ if not os.path.isfile(pfad) and nicht_geliefert(root, DOKUMENT_KOPF):
261
+ hinweis(f"Prüfung 77 ohne Gegenstand: {DOKUMENT_KOPF} gehört zur Nachweisschicht "
262
+ f"und ist nicht geliefert (D-367)")
263
+ return
264
+ if not os.path.isfile(pfad):
265
+ err(f"{DOKUMENT_KOPF}: fehlt. Prüfung 77 hält dort die Dokumentversion gegen "
266
+ f"{KERN}/VERSION; ohne den Träger hat sie ihren Gegenstand verloren (D-312)")
267
+ return
268
+ vpfad = os.path.join(root, KERN, "VERSION")
269
+ if not os.path.isfile(vpfad):
270
+ return # Pruefung 1 meldet die fehlende Pflichtdatei bereits
271
+ stand = read(vpfad).strip()
272
+ m = DOKUMENT_VERSION_RE.search(read(pfad))
273
+ if not m:
274
+ err(f"{DOKUMENT_KOPF}: keine Zeile der Form "
275
+ f"`| Dokumentversion | X.Y.Z (entspricht Framework-Release X.Y.Z) |`. "
276
+ f"Prüfung 77 hält sie gegen {KERN}/VERSION und bestünde ohne sie leise – "
277
+ f"genau der Zustand, in dem das Dokument 42 Releases zurückgefallen ist "
278
+ f"(D-312, D-23)")
279
+ return
280
+ dokument, genannt = m.group(1), m.group(2)
281
+ if dokument != genannt:
282
+ err(f"{DOKUMENT_KOPF}: die Kopfzeile nennt die Dokumentversion {dokument} und "
283
+ f"das Framework-Release {genannt}. Die Zeile sagt aus, dass beide "
284
+ f"übereinstimmen; nennt sie zwei Werte, lässt sie offen, auf welchem Stand "
285
+ f"das Dokument gebaut ist (D-312)")
286
+ return
287
+ if dokument != stand:
288
+ err(f"{DOKUMENT_KOPF}: die Kopfzeile nennt den Stand {dokument}, "
289
+ f"{KERN}/VERSION führt {stand}. Das Hauptdokument wird aus diesem "
290
+ f"Repositorium assembliert und hat keinen eigenen Stand; eine abweichende "
291
+ f"Zahl behauptet einen Bau, den es nicht gegeben hat (D-312)")
292
+
293
+
294
+ # ---------------------------------------------------------------------------
295
+ # Pruefung 78: Die zaehlbaren Aussagen des Hauptdokuments (CR-2026-125, D-315)
296
+ # ---------------------------------------------------------------------------
297
+ #
298
+ # ANLASS, UND ER IST DAS RELEASE DAVOR. 0.89.0 hat das Hauptdokument nach ZWEIUNDVIERZIG
299
+ # Releases auf den geltenden Stand gesetzt und dabei vierzehn stehengebliebene Zahlen
300
+ # berichtigt. Sein eigenes Protokoll schreibt den Grund auf:
301
+ # "Das ist die Bauform von Pruefung 40 an einem groesseren Gegenstand - und der
302
+ # Grund, warum die Behebung eine PRUEFUNG braucht und nicht nur eine Textaenderung."
303
+ # Gebaut wurde dann Pruefung 77, und die misst die VERSION, nicht den INHALT. Gemessen am
304
+ # 2026-09-23, EIN Release spaeter: Der Satz in 26-qs-test.md nannte "76 Pruefungen ueber
305
+ # 502 versionierte Dateien, davon 450 Markdown-Dateien". Richtig waren 77, 504 und 452 -
306
+ # und die beiden Dateien, die fehlten, waren der Antrag und das Protokoll DESSELBEN
307
+ # Releases. DREI ZAHLEN IN EINEM SATZ, UEBERHOLT IM RELEASE, DAS DAS DOKUMENT AUF DEN
308
+ # GELTENDEN STAND GESETZT HAT.
309
+ #
310
+ # DIE BAUFORM IST DIE VON PRUEFUNG 40: den Satz AUSRECHNEN und woertlich verlangen. Ein
311
+ # Vergleich einzelner Zahlen liesse offen, in welchem Satz sie stehen; ein woertlicher
312
+ # Sollsatz sagt zugleich, wie er zu schreiben ist.
313
+ #
314
+ # ZUSCHNITT, UND ER IST DER GANZE PUNKT (D-318): Diese Pruefung gilt der GEGENWARTSFORM.
315
+ # "Der Validator fuehrt 79 Pruefungen" ist eine Aussage ueber jetzt und veraltet.
316
+ # "Gezaehlt am 2026-09-22: 312 Decision Records" ist eine DATIERTE Aussage und veraltet
317
+ # nie - sie muss nur fuer ihr Datum stimmen. Die datierten Zahlen stehen in 29-grenzen.md
318
+ # und 32-abschluss.md, beide ausgewiesene Zeitdokumente (D-273, D-311), und werden hier
319
+ # ausdruecklich NICHT geprueft. Eine Pruefung, die ein Zeitdokument fortzuschreiben
320
+ # verlangt, hat seinen Zweck nicht verstanden.
321
+ #
322
+ # GRENZE, benannt: Sie misst DREI Zahlen in EINEM Satz. Jede andere Zahl des Dokuments
323
+ # laeuft weiter durch - das ist der Rest von K-104, und er ist nicht kleiner geworden,
324
+ # nur gezaehlt.
325
+ #
326
+ # ZWEITER GEGENSTAND VON 1.0.0 BIS 1.4.0 (D-325): DIE ABNAHMEZEILE DER UEBERGABE.
327
+ # ENTFALLEN mit 1.4.1 (D-350) - die Uebergabe ist nicht mehr versioniert, und eine
328
+ # Pruefung gegen einen Traeger, den eine frische Auscheckung nicht fuehrt, liefe nur
329
+ # an einem Arbeitsplatz. Die Lehre bleibt stehen, weil sie ueber die Uebergabe
330
+ # hinausreicht: WER EINE ZAHL PRUEFT, SUCHT IHRE ZWEITE FUNDSTELLE (D-295).
331
+ #
332
+ # ENTHALTUNG: Ein uebernehmendes Projekt fuehrt das Kennzeichen des
333
+ # Quellrepositoriums nicht (D-351). Dort enthaelt sich die ganze Pruefung (D-326).
334
+ P78_TRAEGER = KERN + "/build/doc/26-qs-test.md"
335
+ P78_ANKER = "Der Validator führt **"
336
+ P78_SATZ = ("Der Validator führt **{0} Prüfungen** über {1} versionierte Dateien des "
337
+ "Kerns, davon {2} Markdown-Dateien")
338
+
339
+
340
+ def check_dokumentzahlen(root: str) -> None:
341
+ """Pruefung 78 (D-315): Die Gegenwartszahlen des Hauptdokuments gegen den Bestand."""
342
+ # 🔴 ENTHALTUNG IM UEBERNEHMENDEN PROJEKT (D-326, gemessen am 2026-09-23).
343
+ # Der Satz beschreibt den Bestand des FRAMEWORK-Repositoriums und wird byte-gleich
344
+ # ausgeliefert (D-25). Ein uebernehmendes Projekt fuehrt weniger Kerndateien - beim
345
+ # ersten Lauf dieser Pruefung gegen eines waren es 500 gegen 515 -, und es pflegt den
346
+ # Traeger nicht. Ohne diese Unterscheidung waere sie im Framework gruen und in JEDER
347
+ # Installation rot; das ist der Konstruktionsfehler aus D-299, und die Lehre stand im
348
+ # SELBEN Release, das diese Pruefung gebaut hat.
349
+ if not ist_quellrepositorium(root):
350
+ return
351
+ pfad = os.path.join(root, *P78_TRAEGER.split("/"))
352
+ if not os.path.isfile(pfad):
353
+ err(f"{P78_TRAEGER}: fehlt. Prüfung 78 hält dort die zählbaren Aussagen des "
354
+ f"Hauptdokuments gegen den Bestand (D-315)")
355
+ return
356
+ text = read(pfad)
357
+ # --- Gegenstand 1: der Anker ---------------------------------------------------
358
+ if text.count(P78_ANKER) != 1:
359
+ err(f"{P78_TRAEGER}: der Satz über den Prüfapparat steht nicht genau einmal "
360
+ f"(gesucht: '{P78_ANKER}', gefunden: {text.count(P78_ANKER)}x). Prüfung 78 "
361
+ f"hat ihren Gegenstand verloren und bestünde sonst leise (D-23)")
362
+ return
363
+ # --- Gegenstand 2: die Zahl der Pruefungen --------------------------------------
364
+ vpfad = os.path.join(root, KERN, "tests", "scripts", "validate-framework.py")
365
+ if not os.path.isfile(vpfad):
366
+ return # Pruefung 40 meldet den verlorenen Traeger bereits
367
+ doc = read(vpfad)
368
+ doc = doc.split('"""')[1] if doc.count('"""') >= 2 else ""
369
+ if REGISTER_ANKER not in doc or REGISTER_ENDE not in doc:
370
+ return # Pruefung 40 meldet das verlorene Register bereits
371
+ liste = doc.split(REGISTER_ANKER, 1)[1].split(REGISTER_ENDE, 1)[0]
372
+ gefuehrt = sorted({int(n) for n in re.findall(r"^\s{0,2}(\d+)[a-z]?\. ", liste, re.M)})
373
+ if not gefuehrt:
374
+ return # ebenfalls Pruefung 40
375
+ # --- Gegenstand 3: der Bestand ---------------------------------------------------
376
+ dateien = _verfolgte_dateien(root)
377
+ if dateien is None:
378
+ warn("Prüfung 78: kein Git-Bestand lesbar – die Zahl der versionierten Dateien "
379
+ "ist an diesem Ort nicht prüfbar. Das ist KEIN Messergebnis (D-23)")
380
+ return
381
+ soll = P78_SATZ.format(gefuehrt[-1], len(dateien),
382
+ sum(1 for d in dateien if d.endswith(".md")))
383
+ if soll not in text:
384
+ err(f"{P78_TRAEGER}: der Satz über den Prüfapparat nennt nicht die gezählten "
385
+ f"Werte. Erwartet wörtlich: '{soll}'. Gezählt wurden {gefuehrt[-1]} "
386
+ f"Prüfungen im Register, {len(dateien)} versionierte Dateien unter {KERN}/ "
387
+ f"und {sum(1 for d in dateien if d.endswith('.md'))} davon als Markdown. "
388
+ f"Bis 0.89.0 standen hier 76, 502 und 450 – alle drei waren in DEM Release "
389
+ f"überholt, das das Dokument auf den geltenden Stand gesetzt hat (D-315)")
390
+
391
+
392
+ # ---------------------------------------------------------------------------
393
+ # Pruefung 79: Die Lizenz liegt an zwei Stellen und ist dieselbe (CR-2026-126, D-317)
394
+ # ---------------------------------------------------------------------------
395
+ #
396
+ # ZWEI STELLEN, UND BEIDE WERDEN GEBRAUCHT: Die Wurzel, weil die Hostingdienste die
397
+ # Lizenz dort suchen und ein Repositorium ohne erkannte Lizenz als "alle Rechte
398
+ # vorbehalten" gilt. Der Kern, weil ein uebernehmendes Projekt ihn ALS GANZES kopiert
399
+ # (docs/ADOPTION_GUIDE.md Schritt 2) - eine Lizenzdatei, die nur in der Wurzel liegt,
400
+ # wandert dabei nicht mit, und ein Werk ohne seine Lizenz weiterzugeben ist nach
401
+ # §4 GPL-3.0 unzulaessig.
402
+ #
403
+ # WARUM DAS EINE PRUEFUNG BRAUCHT: Zwei Stellen mit demselben Inhalt laufen auseinander,
404
+ # sobald eine von beiden angefasst wird. Das ist in diesem Repositorium der haeufigste
405
+ # Befundtyp, und die Antwort darauf ist dieselbe wie bei Pruefung 76: nicht "bitte beide
406
+ # pflegen", sondern ein Vergleich, der meldet.
407
+ #
408
+ # GRENZE, benannt: Sie vergleicht die beiden Dateien MITEINANDER und prueft, dass es die
409
+ # GPL-3.0 ist. Sie prueft nicht, ob der Text der amtlichen Fassung der Free Software
410
+ # Foundation entspricht - dafuer waere ein Netzzugriff noetig, und eine Pruefung, die
411
+ # das Netz braucht, ist in einer Installation nicht fahrbar.
412
+ #
413
+ # 🔴 BERICHTIGT MIT 1.0.0 (D-326, CR-2026-128 B14): DIE WURZELFASSUNG GILT NUR IM
414
+ # FRAMEWORK-REPOSITORIUM. Gemessen am 2026-09-23 beim ersten Lauf dieser Pruefung gegen
415
+ # ein uebernehmendes Projekt: Sie meldete "LICENSE: fehlt" - und verlangte damit von
416
+ # JEDEM uebernehmenden Projekt eine GPL-3.0 in SEINER Wurzel.
417
+ # 🔴 DAS WAERE DAS GEGENTEIL DESSEN, WAS DIESELBE ENTSCHEIDUNG WOLLTE: Die
418
+ # Zusatzerlaubnis nach §7 (D-317, Abschnitt 2 des Lizenzhinweises) nimmt die Ausgaben
419
+ # und ausgefuellten Vorlagen ausdruecklich AUS der GPL heraus, damit ein Projekt nicht
420
+ # zur Offenlegung gezwungen wird. Eine Pruefung, die dem Projekt die GPL in die Wurzel
421
+ # schreibt, hebt genau das wieder auf.
422
+ # ➡️ EINE PRUEFUNG, DIE IM FRAMEWORK GRUEN UND IN JEDER INSTALLATION ROT IST, IST FALSCH
423
+ # GEBAUT (D-299) - und die Lehre stand im SELBEN Release, das diese Pruefung gebaut
424
+ # hat: "Jede neue Pruefung laeuft einmal gegen ein uebernehmendes Projekt, bevor sie
425
+ # als fertig gilt." Sie ist notiert und nicht angewandt worden.
426
+ #
427
+ # ZUSCHNITT seit 1.0.0: Im uebernehmenden Projekt gilt allein die KERNFASSUNG - sie ist
428
+ # die, die nach §4 GPL-3.0 mitwandern muss. Die Wurzel gehoert dem Projekt.
429
+ P79_WURZEL = "LICENSE"
430
+ P79_KERN = KERN + "/LICENSE"
431
+ P79_MARKE = "GNU GENERAL PUBLIC LICENSE"
432
+ P79_VERSION = "Version 3, 29 June 2007"
433
+
434
+
435
+ def check_lizenz(root: str) -> None:
436
+ """Pruefung 79 (D-317): Die Lizenz liegt an beiden Stellen und ist dieselbe."""
437
+ # Das Framework-Repositorium fuehrt das Kennzeichen; ein uebernehmendes Projekt
438
+ # nicht (D-351). Dort gilt allein die Kernfassung - die Wurzel gehoert dem
439
+ # Projekt (D-326).
440
+ eigenes_repo = ist_quellrepositorium(root)
441
+ stellen = (P79_WURZEL, P79_KERN) if eigenes_repo else (P79_KERN,)
442
+ inhalte = {}
443
+ for rel in stellen:
444
+ pfad = os.path.join(root, *rel.split("/"))
445
+ if not os.path.isfile(pfad):
446
+ err(f"{rel}: fehlt. Die Lizenz MUSS an beiden Stellen liegen – in der Wurzel "
447
+ f"für die Erkennung durch die Hostingdienste, im Kern, weil ein "
448
+ f"übernehmendes Projekt ihn als Ganzes kopiert und die Lizenz sonst nicht "
449
+ f"mitwandert (§4 GPL-3.0, D-317)")
450
+ return
451
+ with open(pfad, "rb") as fh:
452
+ inhalte[rel] = fh.read()
453
+ if not eigenes_repo:
454
+ # Kein Vergleich moeglich und keiner noetig: Was mitwandern muss, ist da.
455
+ text = inhalte[P79_KERN].decode("utf-8", "replace")
456
+ if P79_MARKE not in text or P79_VERSION not in text:
457
+ err(f"{P79_KERN}: trägt nicht die GNU General Public License Version 3 "
458
+ f"(gesucht: '{P79_MARKE}' und '{P79_VERSION}'). Der Kern wandert mit "
459
+ f"seiner Lizenz oder gar nicht (§4 GPL-3.0, D-317, D-326)")
460
+ return
461
+ if inhalte[P79_WURZEL] != inhalte[P79_KERN]:
462
+ err(f"{P79_KERN}: trägt nicht denselben Inhalt wie {P79_WURZEL} "
463
+ f"({len(inhalte[P79_WURZEL])} gegen {len(inhalte[P79_KERN])} Bytes). Zwei "
464
+ f"Lizenzdateien mit verschiedenem Inhalt lassen offen, welche gilt – und für "
465
+ f"ein übernehmendes Projekt gilt die aus dem Kern (D-317)")
466
+ return
467
+ text = inhalte[P79_WURZEL].decode("utf-8", "replace")
468
+ if P79_MARKE not in text or P79_VERSION not in text:
469
+ err(f"{P79_WURZEL}: trägt nicht die GNU General Public License Version 3 "
470
+ f"(gesucht: '{P79_MARKE}' und '{P79_VERSION}'). Prüfung 79 hätte sonst nur "
471
+ f"belegt, dass zwei Dateien gleich sind – auch zwei leere sind das (D-23)")
472
+
473
+
474
+ # ---------------------------------------------------------------------------
475
+ # Pruefung 80: Jedes Abnahmeprotokoll traegt seine Gegenzeichnung (CR-2026-127, D-319)
476
+ # ---------------------------------------------------------------------------
477
+ #
478
+ # ANLASS. AP11 verlangte die Gegenzeichnung der Protokolle. 0.89.0 hat sie nicht
479
+ # gefahren und den Grund als ABGRENZUNG aufgeschrieben: Eine Gegenzeichnung ist die
480
+ # Handlung einer ZWEITEN ROLLE, und ein Werkzeug, das <TBD: Rolle> ersetzt, faelscht
481
+ # sie. Das traegt - und es hatte eine Folge, die niemand benannt hatte: An diesem
482
+ # Framework arbeitet EINE Person. Die Pflicht war in ihrer damaligen Form nicht
483
+ # erfuellbar, und zwar konstruktiv. Das ist die Bauform "die Regel mit leerer
484
+ # Schnittmenge" (D-189, K-72) an einer GOVERNANCE-Regel statt an einer Testzelle.
485
+ #
486
+ # DER ZUSCHNITT IST DIE HALBE ENTSCHEIDUNG (D-319). Nicht jedes Protokoll ist eine
487
+ # Abnahme: Von 125 sind die meisten Arbeits- und Messprotokolle, die ihren Beleg in
488
+ # sich tragen. Eine Gegenzeichnung sagt etwas anderes - eine zweite Person hat geprueft
489
+ # und steht dafuer ein. Gemessen am 2026-09-23 ueber BEIDE Zaehlregeln: Der Gesamtbestand
490
+ # ergibt 17/47/61 oder 13/45/64, je nach Regel; die ZEHN Abnahmeprotokolle ergeben
491
+ # 3/2/5 unter BEIDEN. Ein Gegenstand, der unter zwei Regeln derselbe ist, ist der
492
+ # richtige Gegenstand.
493
+ #
494
+ # WAS SIE PRUEFT UND WAS NICHT. Sie prueft, dass der Abschnitt DA ist und kein offenes
495
+ # <TBD> mehr traegt. Sie prueft NICHT, ob jemand wirklich gelesen hat - das kann kein
496
+ # Skript, und deshalb steht neben der Zeile der Commit: Die Zeile sagt, WAS
497
+ # gegengezeichnet wurde, der Commit sagt, WER. Eine Unterschrift, die ein Werkzeug
498
+ # erzeugen kann, belegt nichts; diese Pruefung erzeugt keine, sie verlangt eine.
499
+ #
500
+ # GRENZE, benannt: Sie erkennt ein Abnahmeprotokoll am DATEINAMEN. Ein Protokoll, das
501
+ # anders heisst, laeuft durch - dieselbe Ehrlichkeit wie bei Pruefung 48, die Pfade
502
+ # findet und keine Prosa.
503
+ P80_ABLAGE = KERN + "/tests/protocols"
504
+ P80_NAME = re.compile(r"^\d{4}-\d{2}-\d{2}-FW-[A-Z]{2}-\d{2}[\w.-]*\.md$")
505
+ P80_ABSCHNITT = re.compile(r"^#{1,6}\s.*Gegenzeichnung", re.M | re.I)
506
+
507
+
508
+ def check_gegenzeichnung(root: str) -> None:
509
+ """Pruefung 80 (D-319): Abnahmeprotokolle tragen ihre Gegenzeichnung."""
510
+ ablage = os.path.join(root, *P80_ABLAGE.split("/"))
511
+ if not os.path.isdir(ablage) and nicht_geliefert(root, P80_ABLAGE):
512
+ hinweis(f"Prüfung 80 ohne Gegenstand: {P80_ABLAGE}/ gehört zur Nachweisschicht "
513
+ f"und ist nicht geliefert (D-367)")
514
+ return
515
+ if not os.path.isdir(ablage):
516
+ err(f"{P80_ABLAGE}/: fehlt. Prüfung 80 hält dort die Gegenzeichnung der "
517
+ f"Abnahmeprotokolle; ohne die Ablage hat sie ihren Gegenstand verloren "
518
+ f"(D-319)")
519
+ return
520
+ # --- Gegenstand 1: der Anker ---------------------------------------------------
521
+ kandidaten = sorted(n for n in os.listdir(ablage) if P80_NAME.match(n))
522
+ if not kandidaten:
523
+ err(f"{P80_ABLAGE}/: kein einziges Abnahmeprotokoll des Testkatalogs gefunden "
524
+ f"(Muster `JJJJ-MM-TT-FW-<Klasse>-<NN>.md`). Prüfung 80 hätte nichts zu "
525
+ f"prüfen und bestünde leise – genau der Zustand, aus dem die Pflicht "
526
+ f"zweiundzwanzig Releases lang unbemerkt offen war (D-23, D-319)")
527
+ return
528
+ # --- Gegenstand 2: Abschnitt vorhanden und ohne offenes <TBD> -------------------
529
+ for name in kandidaten:
530
+ text = read(os.path.join(ablage, name))
531
+ treffer = P80_ABSCHNITT.search(text)
532
+ if not treffer:
533
+ err(f"{P80_ABLAGE}/{name}: kein Abschnitt `Gegenzeichnung`. Ein "
534
+ f"Abnahmeprotokoll des Testkatalogs trägt ihn (D-319, `FW-CL-11`). "
535
+ f"Arbeits- und Meßprotokolle bekommen ihn nicht – die Abgrenzung steht "
536
+ f"in `{P80_ABLAGE}/README.md`")
537
+ continue
538
+ abschnitt = text[treffer.start():]
539
+ naechste = re.search(r"^#{1,6}\s", abschnitt[treffer.end() - treffer.start():],
540
+ re.M)
541
+ if naechste:
542
+ abschnitt = abschnitt[:treffer.end() - treffer.start() + naechste.start()]
543
+ if TBD_RE.search(abschnitt):
544
+ err(f"{P80_ABLAGE}/{name}: der Abschnitt `Gegenzeichnung` trägt noch ein "
545
+ f"offenes `<TBD>`. Eine Gegenzeichnung ist die Handlung einer Rolle und "
546
+ f"kein Feld, das ein Werkzeug füllt – ist keine zweite Rolle vorhanden, "
547
+ f"wird selbst gegengezeichnet und der Abschnitt weist das ausdrücklich "
548
+ f"aus (D-319, `CR-2026-127` E1)")
549
+
550
+
551
+ # Pruefung 81: eine Zeilenendeform je Repositorium (CR-2026-128, D-320, K-81)
552
+ # ---------------------------------------------------------------------------
553
+ #
554
+ # ANLASS, und er ist ein eigener Fehler. Eine Sitzung hat 28 LF-Zeilen in einen Bestand
555
+ # eingeschleppt, der durchgehend CRLF traegt. Gefunden hat es KEINE der 80 Pruefungen,
556
+ # sondern ein Suchtext, der danach nicht mehr traf. Pruefung 66 prueft die
557
+ # GEGENRICHTUNG - einen Wagenruecklauf ohne Zeilenvorschub - und greift hier nicht.
558
+ #
559
+ # 🔴 UND DER BEFUND, DER IHN AUSLOESTE, WAR AM FALSCHEN GEGENSTAND GEMESSEN. Die
560
+ # Uebergabe schrieb "kein VERSIONIERTER Texttraeger traegt reine LF". Gemessen am
561
+ # 2026-09-23 ueber alle 517 verfolgten Eintraege: im ARBEITSBAUM 515 auf CRLF, im BLOB
562
+ # 515 auf reinem LF. Versioniert ist der Blob. Die Aussage ist an einem Gegenstand
563
+ # richtig und am anderen in ihr Gegenteil verkehrt.
564
+ # ➡️ WER EINE ZEILENENDEFORM MISST, SAGT DAZU, WELCHEN GEGENSTAND ER GEMESSEN HAT.
565
+ #
566
+ # ZUSCHNITT: DEN ARBEITSBAUM, NICHT DEN BLOB. Der Blob ist seit D-320 durch die
567
+ # .gitattributes gesetzt (`* text=auto`) - dort braucht es keine Pruefung, sondern eine
568
+ # Datei. Was die .gitattributes NICHT sieht, ist der Arbeitsbaum zwischen zwei
569
+ # Auscheckungen, und genau dort lag der Fall.
570
+ #
571
+ # SIE SCHREIBT KEINE FORM VOR. Sie verlangt, dass es EINE ist. Ein Linux-Arbeitsbaum
572
+ # traegt LF, ein Windows-Arbeitsbaum CRLF, und beide sind richtig; ein Bestand, der
573
+ # beides mischt, ist es nirgends. ➡️ EINHEITLICHKEIT IST IN JEDER INSTALLATION RICHTIG,
574
+ # EINE BESTIMMTE FORM NUR AN EINEM ARBEITSPLATZ. Damit ist der Konstruktionsfehler
575
+ # ausgeschlossen, den D-299 benannt hat: im Framework gruen und in jeder Installation rot.
576
+ #
577
+ # ZAEHLBEREICH wie bei Pruefung 75: die verfolgten Dateien. Im Framework-Repositorium
578
+ # (erkennbar am Kennzeichen, D-351) alle; in einem uebernehmenden Projekt nur die
579
+ # ausgelieferten - was ein Projekt in SEINEN Dateien fuer eine Form fuehrt, geht das
580
+ # Framework nichts an.
581
+ #
582
+ # GRENZE, benannt: Sie liest BYTES und misst den Arbeitsbaum. Welche Form im
583
+ # Repositorium ankommt, entscheidet die .gitattributes und nicht diese Pruefung. Ohne
584
+ # Git-Bestand meldet sie eine Warnung und KEIN Messergebnis (D-23).
585
+ P81_GEMISCHT = "gemischt"
586
+
587
+
588
+ def _p81_form(pfad: str) -> str | None:
589
+ """Die Zeilenendeform eines Traegers, an seinen Bytes gemessen."""
590
+ try:
591
+ with open(pfad, "rb") as fh:
592
+ roh = fh.read()
593
+ except OSError:
594
+ return None
595
+ if b"\x00" in roh[:8000]:
596
+ return None
597
+ lf = roh.count(b"\n")
598
+ if not lf:
599
+ return None # ein Traeger ohne Zeilenumbruch traegt keine Form
600
+ crlf = roh.count(b"\r\n")
601
+ if crlf == lf:
602
+ return "CRLF"
603
+ if crlf == 0:
604
+ return "LF"
605
+ return P81_GEMISCHT
606
+
607
+
608
+ def check_zeilenendeform(root: str) -> None:
609
+ """Pruefung 81 (D-320): Alle versionierten Texttraeger tragen dieselbe Form."""
610
+ dateien = _p75_verfolgt(root)
611
+ if dateien is None:
612
+ warn("Prüfung 81: kein Git-Bestand lesbar – die Zeilenendeform ist an diesem "
613
+ "Ort nicht prüfbar. Das ist KEIN Messergebnis (D-23)")
614
+ return
615
+ bestand = set(dateien)
616
+ eigenes_repo = QUELLREPO_KENNZEICHEN in bestand
617
+ ausgeliefert = (KERN + "/",)
618
+ formen: dict[str, list[str]] = {}
619
+ for rel in sorted(bestand):
620
+ if not eigenes_repo and not rel.startswith(ausgeliefert):
621
+ continue
622
+ if os.path.splitext(rel)[1] not in TEXT_EXT and os.path.basename(rel) not in (
623
+ "VERSION", ".gitignore", ".gitattributes"):
624
+ continue
625
+ pfad = os.path.join(root, *rel.split("/"))
626
+ if not os.path.isfile(pfad):
627
+ continue
628
+ form = _p81_form(pfad)
629
+ if form is None:
630
+ continue
631
+ formen.setdefault(form, []).append(rel)
632
+ if not formen:
633
+ err("Prüfung 81: kein einziger versionierter Textträger gelesen – sie hätte "
634
+ "nichts zu prüfen und bestünde leise (D-23)")
635
+ return
636
+ # --- Gegenstand 1: kein Traeger mischt beide Formen in sich ---------------------
637
+ for rel in formen.get(P81_GEMISCHT, []):
638
+ err(f"{rel}: mischt CRLF- und LF-Zeilen in derselben Datei. Ein gemischter "
639
+ f"Träger ist an keinem Arbeitsplatz richtig; er entsteht, wenn ein Werkzeug "
640
+ f"einzelne Zeilen in der jeweils anderen Form nachträgt (D-320, `K-81`)")
641
+ # --- Gegenstand 2: der Bestand traegt EINE Form ---------------------------------
642
+ rein = {f: n for f, n in formen.items() if f != P81_GEMISCHT}
643
+ if len(rein) < 2:
644
+ return
645
+ haupt = max(rein, key=lambda f: len(rein[f]))
646
+ for form, namen in sorted(rein.items()):
647
+ if form == haupt:
648
+ continue
649
+ for rel in namen:
650
+ err(f"{rel}: trägt {form}-Zeilenenden, während {len(rein[haupt])} von "
651
+ f"{sum(len(n) for n in rein.values())} versionierten Textträgern "
652
+ f"{haupt} tragen. Diese Prüfung schreibt keine Form vor – sie verlangt, "
653
+ f"daß es eine ist (D-320, `K-81`). Welche Form im Repositorium ankommt, "
654
+ f"setzt `.gitattributes`, nicht der Arbeitsplatz")
655
+
656
+
657
+
658
+ # Pruefung 82: die Bestandsliste steht auf dem Stand des Releases (CR-2026-130, D-331)
659
+ # ---------------------------------------------------------------------------
660
+ #
661
+ # ANLASS, UND ER IST ZWEIMAL IN ZWEI RELEASES AUFGETRETEN. `1.0.0` hat die Bestandsliste
662
+ # angelegt, und ihr erster Eintrag war zugleich ihr erster Befund: Beide uebernehmenden
663
+ # Projekte standen drei Releases hinter `main`, waehrend Kriterium 5 von D-11 als
664
+ # erfuellt gefuehrt wurde. `1.0.1` hat es wiederholt - die Liste stand einen halben Tag
665
+ # auf `1.0.0`, waehrend die Projekte `1.0.1` trugen.
666
+ #
667
+ # URSACHE IST DIE REIHENFOLGE, NICHT DIE SORGFALT. Gehoben wurde NACH dem Merge; damit
668
+ # war `FW-CL-11` Pruefpunkt 20 zum Merge-Zeitpunkt nicht erfuellt.
669
+ # ➡️ EINE LISTE, DIE ERST NACH DEM RELEASE FORTGESCHRIEBEN WIRD, IST BEIM RELEASE FALSCH.
670
+ #
671
+ # 🔴 UND SIE WAR AN ZWEI STELLEN FALSCH, NICHT AN EINER. Gemessen am 2026-09-23 vor dem
672
+ # ersten Handgriff dieses Releases: Das Framework hatte seine Liste auf `1.0.1`
673
+ # berichtigt - die AUSGELIEFERTEN Kopien in beiden uebernehmenden Projekten trugen
674
+ # weiter `1.0.0` neben einer VERSION `1.0.1`. Diese Pruefung waere dort rot gewesen.
675
+ # ➡️ WER EINE LISTE NACH DEM HEBEN FORTSCHREIBT, SCHREIBT SIE AN EINER STELLE FORT UND
676
+ # LIEFERT SIE AN ZWEI. Das ist der gemessene Grund, weshalb ein Verfahrensschritt allein
677
+ # hier nicht getragen haette.
678
+ #
679
+ # ZUSCHNITT: DIE SPALTE `Framework-Version` GEGEN `<KERN>/VERSION`. Mehr ist von hier aus
680
+ # nicht messbar - die Projekte liegen ausserhalb dieses Repositoriums, und eine Pruefung,
681
+ # die sie sucht, waere auf jedem anderen Arbeitsplatz rot (D-299).
682
+ #
683
+ # D-299-PROBE, GEFUEHRT UND BESTANDEN: In einem uebernehmenden Projekt sind Liste und
684
+ # VERSION beide byte-gleich aus DEMSELBEN Release ausgeliefert und tragen deshalb
685
+ # denselben Wert - auch dann, wenn das Projekt mehrere Releases zurueckliegt. Die
686
+ # Pruefung ist dort gruen, und sie braucht dafuer keine Ausnahme.
687
+ #
688
+ # 🔴 GRENZE, BENANNT: SIE MISST DIE BEHAUPTUNG, NICHT DIE TATSACHE. Wer die Zeile
689
+ # aendert, ohne zu heben, kommt durch. Das ist dieselbe Bauform wie bei Pruefung 77, die
690
+ # die VERSION des Hauptdokuments misst und nicht seinen INHALT - und sie steht hier,
691
+ # weil eine Grenze, die man nicht nennt, wie eine Zusage aussieht.
692
+ #
693
+ # ⚠️ PREIS, BENANNT: Jedes Release fasst diese Tabelle an - derselbe Preis wie bei
694
+ # Pruefung 67 und Pruefung 77.
695
+ P82_LISTE = KERN + "/governance/ADOPTION_REGISTRY.md"
696
+ P82_SPALTE = "Framework-Version"
697
+ P82_PROJEKTSPALTE = "Projekt"
698
+
699
+
700
+ def check_bestandsliste_stand(root: str) -> None:
701
+ """Pruefung 82 (D-331): Die Bestandsliste nennt den Stand dieses Releases."""
702
+ pfad = os.path.join(root, *P82_LISTE.split("/"))
703
+ if not os.path.isfile(pfad):
704
+ err(f"{P82_LISTE}: fehlt. `RELEASE_PROCESS.md` Abschnitt 4 Punkt 4 verlangt sie "
705
+ f"als Nachweis der Auditierbarkeit, und Kriterium 5 von D-11 hängt an ihr "
706
+ f"(D-322). Prüfung 82 hätte ohne sie ihren Gegenstand verloren")
707
+ return
708
+ vpfad = os.path.join(root, KERN, "VERSION")
709
+ if not os.path.isfile(vpfad):
710
+ return # Pruefung 1 meldet die fehlende Pflichtdatei bereits
711
+ stand = read(vpfad).strip()
712
+ zeilen = read(pfad).split("\n")
713
+
714
+ # --- Den Anker suchen, und sein Fehlen als Fehler melden (D-23) -----------------
715
+ kopf = None
716
+ spalte = None
717
+ projekt = 0
718
+ for i, roh in enumerate(zeilen):
719
+ if not roh.lstrip().startswith("|"):
720
+ continue
721
+ zellen = tabellenzellen(roh)
722
+ if P82_SPALTE in zellen:
723
+ kopf = i
724
+ spalte = zellen.index(P82_SPALTE)
725
+ if P82_PROJEKTSPALTE in zellen:
726
+ projekt = zellen.index(P82_PROJEKTSPALTE)
727
+ break
728
+ if kopf is None:
729
+ err(f"{P82_LISTE}: keine Tabelle mit der Spalte `{P82_SPALTE}`. Prüfung 82 "
730
+ f"findet ihren Gegenstand über diese Überschrift; geht sie verloren, "
731
+ f"bestünde die Prüfung leise (D-23)")
732
+ return
733
+
734
+ # --- Die Zeilen des Bestands gegen VERSION halten -------------------------------
735
+ gemessen = 0
736
+ for roh in zeilen[kopf + 2:]:
737
+ if not roh.lstrip().startswith("|"):
738
+ break # eine Leerzeile beendet die Tabelle (D-264)
739
+ zellen = tabellenzellen(roh)
740
+ if len(zellen) <= spalte:
741
+ continue
742
+ wert = zellen[spalte].strip().strip("*`").strip()
743
+ if not wert:
744
+ continue
745
+ gemessen += 1
746
+ if wert == stand:
747
+ continue
748
+ name = zellen[projekt].strip() if len(zellen) > projekt else "(ohne Namen)"
749
+ err(f"{P82_LISTE}: {name} steht auf Framework-Version {wert}, "
750
+ f"{KERN}/VERSION führt {stand}. Das Heben der übernehmenden Projekte gehört "
751
+ f"VOR den Release-Commit (D-330); eine Liste, die erst danach "
752
+ f"fortgeschrieben wird, ist beim Release falsch. ⚠️ Diese Prüfung mißt die "
753
+ f"Behauptung der Zeile, nicht den Stand des Projekts (D-331)")
754
+ if not gemessen:
755
+ warn(f"{P82_LISTE}: die Tabelle führt keine Zeile mit einem Wert in der Spalte "
756
+ f"`{P82_SPALTE}`. Prüfung 82 hat nichts gemessen – das ist KEIN "
757
+ f"Messergebnis (D-23)")
758
+
759
+
760
+
761
+ # Pruefung 83: die Chronik zaehlt ihr eigenes Release zu Ende (CR-2026-131, D-335)
762
+ # ---------------------------------------------------------------------------
763
+ #
764
+ # ANLASS, GEMESSEN IM VORBEDINGUNGSDURCHGANG VON 1.2.0. `docs/ROADMAP.md` fuehrt je
765
+ # Release eine Zeile mit der SPANNE der Entscheidungen, die es vergeben hat. Ueber
766
+ # vierzehn Releases mit Spannenschreibweise ist sie lueckenlos - und die letzte war um
767
+ # zwei zu niedrig: `1.1.0` stand auf "D-329 bis D-332", vergeben sind D-329 bis D-334.
768
+ #
769
+ # DIE URSACHE IST GEMESSEN UND STEHT IM EIGENEN RELEASE. D-333 und D-334 sind BEIM
770
+ # UMSETZEN gefallen; die Uebergabe zu 1.1.0 sagt es woertlich. Die ROADMAP-Zeile war zu
771
+ # diesem Zeitpunkt laengst geschrieben.
772
+ # ➡️ EINE ZAHL, DIE VOR IHREM GEGENSTAND GESCHRIEBEN WIRD, IST ZUM ZEITPUNKT IHRER
773
+ # NIEDERSCHRIFT RICHTIG UND DANACH NICHT MEHR. Das ist die Bauform von Pruefung 40 -
774
+ # hier INNERHALB eines einzigen Releases statt ueber zweiundvierzig.
775
+ #
776
+ # 🔴 DER SCHWERERE TEIL: KEIN BESCHREIBENDER TRAEGER NENNT ALLE SECHS. Vier Traeger
777
+ # beschreiben 1.1.0 - ROADMAP vier, CHANGELOG fuenf, Antrag vier, Protokoll vier. D-333
778
+ # steht in keinem davon. Es steht im Register, im Protokoll und in den beiden normativen
779
+ # Traegern, die es GEAENDERT hat - also dort, wo es wirkt, und nirgends dort, wo das
780
+ # Release erklaert wird. Und D-333 ist die Entscheidung, die den SCHWERSTEN Befund von
781
+ # 1.1.0 behoben hat.
782
+ # 🟢 Der einzige Traeger, der die Menge vollstaendig nennt, ist die MARKE ("D-329 bis
783
+ # D-334") - und sie ist der einzige, den keine Pruefung erreichen kann: Der Markentext
784
+ # liegt im Tag-Objekt, nicht im Arbeitsbaum (`K-111`, `K-113`).
785
+ #
786
+ # WARUM PRUEFUNG 58 ES NICHT FAENGT. Sie haelt die Gegenrichtung - jede GENANNTE Kennung
787
+ # steht im Register. Der Befund hier ist eine VERGEBENE Kennung, die nirgends genannt
788
+ # wird.
789
+ # ➡️ PRUEFUNG 58 FAENGT DIE VERWAISTE NENNUNG, NICHT DIE VERWAISTE KENNUNG.
790
+ #
791
+ # D-299-PROBE, GEFUEHRT UND BESTANDEN: Beide Traeger liegen im Kern und werden
792
+ # byte-gleich ausgeliefert. Ein uebernehmendes Projekt, das Releases zurueckliegt, traegt
793
+ # dieselbe ROADMAP und dasselbe Register aus demselben Release - die Pruefung ist dort
794
+ # gruen und braucht keine Ausnahme.
795
+ #
796
+ # 🔴 GRENZE, BENANNT: SIE MISST DIE OBERGRENZE, NICHT DIE VOLLSTAENDIGKEIT DER
797
+ # NENNUNGEN. Ein Traeger, der die Spanne richtig fuehrt und D-333 im Fliesstext nicht
798
+ # nennt, kommt durch. Das ist dieselbe Bauform wie bei Pruefung 77 (Version, nicht
799
+ # Inhalt) und 82 (Behauptung, nicht Tatsache) - und sie steht hier, weil eine Grenze,
800
+ # die man nicht nennt, wie eine Zusage aussieht. `K-112` fuehrt die Frage weiter.
801
+ #
802
+ # ⚠️ PREIS, BENANNT: Jedes Release fasst diese Zeile an - derselbe Preis wie bei
803
+ # Pruefung 67, 77 und 82.
804
+ P83_ROADMAP = KERN + "/docs/ROADMAP.md"
805
+ P83_REGISTER = KERN + "/governance/DECISION_LOG.md"
806
+ P83_SPANNE_RE = re.compile(r"\*\*D-(\d+)\*\*\s*bis\s*\*\*D-(\d+)\*\*")
807
+ P83_ZEILE_RE = re.compile(r"^\|\s*(?:\*\*)?D-(\d+)", re.M)
808
+ P83_SONDEN_ANKER = "**Reservierter Kennungsbereich für Sonden"
809
+ P83_GRENZE_RE = re.compile(r"ab (\d+) zählen nicht als Obergrenze")
810
+
811
+
812
+ def _p83_sondengrenze(logtext: str) -> int:
813
+ """Ab welcher D-Nummer eine Kennung den Sonden gehoert (D-340).
814
+
815
+ EIN BEREICH UND KEINE LISTE, und der Grund ist gemessen: Eine Liste muesste die
816
+ Kennung WOERTLICH nennen - und dann meldet Pruefung 58 genau diese Nennung, weil
817
+ sie in keiner Registerzeile steht. Eine Ausnahme, die ihren Gegenstand nennen muss,
818
+ um zu wirken, erzeugt den Befund, den sie verhindern soll.
819
+
820
+ NICHT dieselbe Sache wie `_synthetische_kennungen()`: Jene Menge wird nie im
821
+ Register gefuehrt und vom MELDEN ausgenommen; dieser Bereich wird von einer
822
+ Gegenprobe selbst eingetragen und muss weiterhin gemeldet werden koennen - nur als
823
+ OBERGRENZE zaehlt er nicht. Null heisst: der Anker ist verloren; der Aufrufer
824
+ meldet das.
825
+ """
826
+ for zeile in logtext.splitlines():
827
+ if zeile.lstrip().startswith(P83_SONDEN_ANKER):
828
+ m = P83_GRENZE_RE.search(zeile)
829
+ return int(m.group(1)) if m else 0
830
+ return 0
831
+
832
+
833
+ def check_chronikspanne(root: str) -> None:
834
+ """Pruefung 83 (D-335): Die letzte Release-Spanne endet bei der hoechsten Kennung."""
835
+ rpfad = os.path.join(root, *P83_ROADMAP.split("/"))
836
+ dpfad = os.path.join(root, *P83_REGISTER.split("/"))
837
+ if not os.path.isfile(rpfad):
838
+ err(f"{P83_ROADMAP}: fehlt - Pruefung 83 haette ihren Gegenstand verloren und "
839
+ f"bestuende sonst leise (D-23, D-335)")
840
+ return
841
+ if not os.path.isfile(dpfad):
842
+ return # Pruefung 50 und 58 melden den fehlenden Traeger bereits
843
+
844
+ dtext = read(dpfad)
845
+ # 🔴 DIESE PRUEFUNG BRAUCHT IHRE EIGENE AUSNAHMEMENGE, UND ZWAR EINE ANDERE ALS
846
+ # PRUEFUNG 50 UND 58 (D-340). Der Anlass ist gemessen und hat zwei Stufen:
847
+ #
848
+ # STUFE 1, gefunden beim ersten Abnahmelauf von 1.2.0: Die Gegenprobe 58b legt eine
849
+ # Registerzeile mit ihrer Sondenkennung an, und diese Pruefung las sie als hoechste -
850
+ # 654 gemeldete Luecken.
851
+ #
852
+ # STUFE 2, gefunden beim ZWEITEN Abnahmelauf, nachdem die Sondenkennung von Pruefung 58 in die Menge der
853
+ # belegten synthetischen Kennungen eingetragen war: SONDE 58a VERLOR IHREN
854
+ # GEGENSTAND. Pruefung 58 nimmt jene Menge vom Melden aus - und Sonde 58a prueft
855
+ # genau, dass diese Kennung GEMELDET wird. Das Decision Log sagt denselben Satz
856
+ # ueber die Sondenkennung von Pruefung 50: "sie soll ja gemeldet werden".
857
+ # ➡️ ZWEI PRUEFUNGEN, DIE DIESELBE KENNUNG ANSEHEN, STELLEN NICHT DIESELBE FRAGE -
858
+ # die eine fragt nach ZUGEHOERIGKEIT, die andere nach einer GRENZE. Das ist D-243
859
+ # (zwei Regeln, die einander die Voraussetzung entziehen) an einem Paar von SONDEN.
860
+ #
861
+ # Deshalb ein EIGENER Anker im selben Absatz des Decision Logs, mit eigener
862
+ # Begruendung. Leere Menge heisst: der Anker ist verloren - das wird gemeldet.
863
+ grenze = _p83_sondengrenze(dtext)
864
+ if not grenze:
865
+ err(f"{P83_REGISTER}: der Absatz '{P83_SONDEN_ANKER}…' fehlt oder nennt keine "
866
+ f"Bereichsgrenze. Pruefung 83 leitet ihre Ausnahme daraus ab; ohne ihn "
867
+ f"liest sie eine Sondenkennung als hoechste vergebene Entscheidung "
868
+ f"(D-23, D-340)")
869
+ return
870
+ vergeben = [int(n) for n in P83_ZEILE_RE.findall(dtext) if int(n) < grenze]
871
+ if not vergeben:
872
+ err(f"{P83_REGISTER}: keine Registerzeile '| D-NNN |' gefunden - Pruefung 83 "
873
+ f"findet ihren Vergleichswert dort; geht er verloren, bestuende sie leise "
874
+ f"(D-23, D-335)")
875
+ return
876
+ hoechste = max(vergeben)
877
+
878
+ spannen = P83_SPANNE_RE.findall(read(rpfad))
879
+ if not spannen:
880
+ err(f"{P83_ROADMAP}: keine Release-Spanne der Form '**D-NNN** bis **D-NNN**' "
881
+ f"gefunden. Pruefung 83 findet ihren Gegenstand ueber diese Schreibweise; "
882
+ f"geht sie verloren, bestuende die Pruefung leise (D-23, D-335)")
883
+ return
884
+
885
+ # Die hoechste Obergrenze aller Spannen - nicht die zuletzt geschriebene. Die
886
+ # Releasetabelle ist nicht garantiert sortiert: gemessen am 2026-09-23 stand `1.0.1`
887
+ # VOR `1.0.0`, und eine Pruefung, die sich auf die Reihenfolge verlaesst, misst dann
888
+ # die falsche Zeile (D-337).
889
+ obergrenze = max(int(b) for _a, b in spannen)
890
+ if obergrenze == hoechste:
891
+ return
892
+ if obergrenze > hoechste:
893
+ err(f"{P83_ROADMAP}: die hoechste Release-Spanne endet bei D-{obergrenze}, das "
894
+ f"Register fuehrt hoechstens D-{hoechste}. Die Chronik nennt eine "
895
+ f"Entscheidung, die es nicht gibt - Pruefung 58 meldet sie zusaetzlich als "
896
+ f"nicht gefuehrte Kennung (D-335)")
897
+ return
898
+ fehlend = ", ".join(f"D-{n}" for n in range(obergrenze + 1, hoechste + 1))
899
+ err(f"{P83_ROADMAP}: die hoechste Release-Spanne endet bei D-{obergrenze}, vergeben "
900
+ f"ist bis D-{hoechste}. In der Chronik fehlen {fehlend}. Eine Entscheidung, die "
901
+ f"beim Umsetzen faellt, faellt NACH der Zeile, die sie nennen soll - und eine "
902
+ f"Zahl, die vor ihrem Gegenstand geschrieben wird, ist danach nicht mehr richtig. "
903
+ f"⚠️ Diese Pruefung misst die OBERGRENZE, nicht die Vollstaendigkeit der "
904
+ f"Nennungen (D-335, `K-112`)")
905
+
906
+
907
+ # Pruefung 85: eine Zielangabe ueberlebt ihr eigenes Release nicht (CR-2026-132, D-342)
908
+ # ---------------------------------------------------------------------------
909
+ #
910
+ # ANLASS, GEMESSEN IM VORBEDINGUNGSDURCHGANG VON 1.3.0. `docs/ROADMAP.md` fuehrt neben
911
+ # der Releasetabelle Abschnitte der Form
912
+ # ### Geplant: <Posten> - Ziel-Release **<Version>**
913
+ # ALLE DREI vorhandenen nannten eine Version, die die Gegenwart ueberholt hatte:
914
+ # * die Umbenennung auf Koolie mit "~0.68.0" - erledigt mit 0.88.0, und der Abschnitt
915
+ # heisst seit fuenfzehn Releases "Geplant";
916
+ # * das Client Pack openai-codex mit "1.1.0" - waehrend die Releasetabelle DERSELBEN
917
+ # Datei ihn auf 1.3.0 fuehrt, rund 1.280 Zeilen entfernt;
918
+ # * die Projekt-Overlays als Installationsparameter mit "1.2.0" - ausgeliefert, und
919
+ # der Posten ist nicht gefahren.
920
+ #
921
+ # ➡️ EINE ZIELANGABE IST EINE ZAHL, DIE VOR IHREM GEGENSTAND GESCHRIEBEN WIRD. Das ist
922
+ # die Bauform von Pruefung 83, hier an der PLANSEITE derselben Datei statt an der
923
+ # Chronikseite - und der Beleg dafuer, dass dieselbe Bauform an zwei Enden eines
924
+ # Traegers auftreten kann, ohne dass die eine Pruefung die andere Stelle sieht.
925
+ #
926
+ # URSACHE GEMESSEN: Die Verschiebungen sind je einzeln ausgewiesen worden - D-127 hat
927
+ # den Posten auf 1.1.0 gesetzt, D-339 auf 1.3.0 -, und KEINE von ihnen hat die
928
+ # Ueberschrift angefasst, weil die Releasetabelle als der eine Ort galt.
929
+ # ➡️ WER EINE ZAHL AN ZWEI STELLEN FUEHRT, PFLEGT EINE.
930
+ #
931
+ # D-299-PROBE, GEFUEHRT UND BESTANDEN: Beide Traeger - ROADMAP und VERSION - liegen im
932
+ # Kern und werden byte-gleich ausgeliefert. Ein uebernehmendes Projekt, das Releases
933
+ # zurueckliegt, traegt beide aus demselben Release; die Pruefung ist dort gruen und
934
+ # braucht keine Ausnahme.
935
+ #
936
+ # 🔴 GRENZE, BENANNT: SIE MISST DIE ZIELANGABE, NICHT DEN STAND DES POSTENS. Ein
937
+ # Abschnitt, dessen Ziel in der Zukunft liegt, kann laengst erledigt sein und kommt
938
+ # durch - dieselbe Bauform wie Pruefung 77 (Version, nicht Inhalt) und 82 (Behauptung,
939
+ # nicht Tatsache). Sie steht hier, weil eine Grenze, die man nicht nennt, wie eine
940
+ # Zusage aussieht; `K-116` fuehrt die Frage weiter.
941
+ #
942
+ # ⚠️ PREIS, BENANNT: Wer einen Posten verschiebt, fasst die Ueberschrift an - und genau
943
+ # das ist der Zweck. Es ist derselbe Preis wie bei Pruefung 67, 77, 82 und 83, nur faellt
944
+ # er hier nicht je Release an, sondern je Verschiebung.
945
+ P85_ROADMAP = KERN + "/docs/ROADMAP.md"
946
+ P85_UEBERSCHRIFT = "### Geplant:"
947
+ P85_ZIEL_RE = re.compile(r"Ziel-Release\s*\**\s*`?~?(\d+)\.(\d+)\.(\d+)`?")
948
+
949
+
950
+ def _p85_version(text: str) -> tuple[int, int, int] | None:
951
+ """Die drei Zahlen einer Versionsangabe - oder None."""
952
+ m = re.match(r"\s*(\d+)\.(\d+)\.(\d+)\s*$", text)
953
+ return (int(m.group(1)), int(m.group(2)), int(m.group(3))) if m else None
954
+
955
+
956
+ def check_zielangabe(root: str) -> None:
957
+ """Pruefung 85 (D-342): Ein Planabschnitt nennt kein erreichtes Ziel-Release."""
958
+ rpfad = os.path.join(root, *P85_ROADMAP.split("/"))
959
+ vpfad = os.path.join(root, KERN, "VERSION")
960
+ if not os.path.isfile(rpfad):
961
+ err(f"{P85_ROADMAP}: fehlt - Pruefung 85 haette ihren Gegenstand verloren und "
962
+ f"bestuende sonst leise (D-23, D-342)")
963
+ return
964
+ if not os.path.isfile(vpfad):
965
+ return # Pruefung 1 meldet die fehlende Pflichtdatei bereits
966
+ stand = _p85_version(read(vpfad))
967
+ if stand is None:
968
+ return # Pruefung 67 meldet eine unlesbare VERSION bereits
969
+
970
+ ueberschriften = [z for z in read(rpfad).splitlines()
971
+ if z.startswith(P85_UEBERSCHRIFT)]
972
+ if not ueberschriften:
973
+ err(f"{P85_ROADMAP}: keine Ueberschrift '{P85_UEBERSCHRIFT} …' gefunden. "
974
+ f"Pruefung 85 findet ihren Gegenstand ueber diese Schreibweise; geht sie "
975
+ f"verloren, bestuende die Pruefung leise (D-23, D-342)")
976
+ return
977
+
978
+ # 🔴 DER ANKER IST ZWEITEILIG, UND BEIDE TEILE WERDEN GEMELDET. Eine Ueberschrift
979
+ # OHNE Zielangabe ist kein stiller Durchlauf, sondern der billigste Weg, diese
980
+ # Pruefung loszuwerden - genau die Bauform, die D-124 mit "ein Posten ohne Zahl
981
+ # bleibt lange liegen" bereits einmal entschieden hat.
982
+ ohne_ziel = [z for z in ueberschriften if not P85_ZIEL_RE.search(z)]
983
+ if ohne_ziel:
984
+ err(f"{P85_ROADMAP}: {len(ohne_ziel)} Planabschnitt(e) nennen kein "
985
+ f"Ziel-Release: {'; '.join(z[:70] for z in ohne_ziel)}. Ein Posten ohne "
986
+ f"Zahl bleibt in diesem Projekt erfahrungsgemaess lange liegen (D-124) - "
987
+ f"und er entzieht sich zugleich dieser Pruefung (D-342)")
988
+
989
+ for zeile in ueberschriften:
990
+ m = P85_ZIEL_RE.search(zeile)
991
+ if not m:
992
+ continue
993
+ ziel = (int(m.group(1)), int(m.group(2)), int(m.group(3)))
994
+ if ziel > stand:
995
+ continue
996
+ err(f"{P85_ROADMAP}: der Abschnitt '{zeile[:70]}…' nennt das Ziel-Release "
997
+ f"{m.group(1)}.{m.group(2)}.{m.group(3)}, und "
998
+ f"{'.'.join(str(n) for n in stand)} ist erreicht. Entweder ist der Posten "
999
+ f"gefahren - dann ist er nicht mehr 'Geplant' - oder er ist ueberfaellig "
1000
+ f"und braucht eine neue Zahl. ➡️ Eine Zielangabe ist eine Zahl, die vor "
1001
+ f"ihrem Gegenstand geschrieben wird. ⚠️ Diese Pruefung misst die "
1002
+ f"ZIELANGABE, nicht den STAND des Postens (D-342, `K-116`)")
1003
+
1004
+ # ---------------------------------------------------------------------------
1005
+ #
1006
+ # ANLASS. 1.9.0 ist das Qualitaetssicherungsrelease der Dokumentation (D-370). Seine
1007
+ # Vorbedingung war, VOR der Durchsicht festzulegen, welche Dokumente es gibt, was je
1008
+ # Dokument "in Ordnung" heisst - und was davon eine Maschine pruefen kann. Sonst ist
1009
+ # derselbe Befund in drei Releases wieder da: Die Standueberschrift der Roadmap stand
1010
+ # bei 1.8.0 vier Releases zurueck, direkt ueber dem Satz "Wird mit jedem Release
1011
+ # fortgeschrieben".
1012
+ #
1013
+ # DIE DOKUMENTKLASSEN STEHEN HIER, AN EINER STELLE (D-371), und
1014
+ # docs/DOCUMENTATION_STANDARD.md beschreibt sie, statt sie ein zweites Mal aufzuzaehlen:
1015
+ # A Einstieg - wer das Framework zum ersten Mal liest
1016
+ # B Regeln - was gilt (Core-Module, Laufzeit, Prozesse, Checklisten, Skills ...)
1017
+ # C Register - Belege und Verzeichnisse; ihr Inhalt wird nicht umgeschrieben
1018
+ # D Hauptdokument - die Kapitelquellen unter build/doc/
1019
+ # Die Nachweisschicht (clientmap.NACHWEIS_ABLAGEN) ist keine Klasse - mit einer
1020
+ # Ausnahme: build/doc/ liegt darin, weil build/ nicht zur Nutzung gehoert, ist aber
1021
+ # das Hauptdokument und damit Klasse D.
1022
+ #
1023
+ # D-299: Alle vier Pruefungen lesen Traeger, die byte-gleich in jedes Projekt
1024
+ # geliefert werden - dort sind sie so gruen wie hier. Die Wurzel-README gehoert dem
1025
+ # Quellrepositorium; in einem Projekt gehoert die Wurzel dem Projekt, und sie wird
1026
+ # dort nicht gelesen. Fehlt build/doc/ (Lieferumfang "nutzung"), fehlt Klasse D.
1027
+ DOK_REGISTER = frozenset({
1028
+ "CHANGELOG.md", "governance/DECISION_LOG.md", "docs/ROADMAP.md",
1029
+ "tests/TEST_CATALOG.md", "tests/EDGE_CASES.md", "docs/PLACEHOLDER_REGISTRY.md",
1030
+ "governance/ADOPTION_REGISTRY.md",
1031
+ })
1032
+ DOK_REGISTER_RE = re.compile(r"skills/[^/]+/(?:TESTS|EXAMPLES|CHANGELOG)\.md$")
1033
+ DOK_EINSTIEG = ("onboarding/", "examples/", "pilot/", "docs/ADOPTION_GUIDE.md",
1034
+ "docs/RUNTIME_GLOSSARY.md", "clients/README.md")
1035
+ DOK_EINSTIEG_RE = re.compile(r"clients/[^_/][^/]*/CLIENT_PACK\.md$")
1036
+ DOK_NACHWEIS = ("governance/change-requests/", "tests/protocols/", "tests/erhebungen/",
1037
+ "build/")
1038
+ def dokumentklasse(rel: str) -> str | None:
1039
+ """Die Klasse eines Dokuments nach D-371 - projektrelativ, mit '/'. None heisst:
1040
+ kein Dokument dieser Pruefungen (keine Markdown-Datei, Nachweisschicht, Projekt)."""
1041
+ if not rel.endswith(".md"):
1042
+ return None
1043
+ if not rel.startswith(KERN + "/"):
1044
+ return "A" if rel in DOK_WURZEL else None
1045
+ k = rel[len(KERN) + 1:]
1046
+ if k.startswith("build/doc/"):
1047
+ return "D"
1048
+ if k.startswith(DOK_NACHWEIS):
1049
+ return None
1050
+ if k in DOK_REGISTER or DOK_REGISTER_RE.search(k):
1051
+ return "C"
1052
+ if k.startswith(DOK_EINSTIEG) or DOK_EINSTIEG_RE.match(k):
1053
+ return "A"
1054
+ return "B"
1055
+
1056
+
1057
+ def _dokumente(root: str):
1058
+ """(rel, klasse) aller Dokumente dieses Baums, sortiert."""
1059
+ gefunden = []
1060
+ kern = os.path.join(root, KERN)
1061
+ for dirpath, dirnames, filenames in os.walk(kern):
1062
+ dirnames[:] = sorted(d for d in dirnames if d not in (".git", "__pycache__", "out"))
1063
+ for fn in filenames:
1064
+ rel = os.path.relpath(os.path.join(dirpath, fn), root).replace(os.sep, "/")
1065
+ k = dokumentklasse(rel)
1066
+ if k:
1067
+ gefunden.append((rel, k))
1068
+ if ist_quellrepositorium(root):
1069
+ gefunden += [(rel, "A") for rel in DOK_WURZEL if os.path.isfile(os.path.join(root, rel))]
1070
+ return sorted(gefunden)
1071
+
1072
+
1073
+ DOK_ZAUN_RE = re.compile(r"^\s*(`{3,}|~{3,})")
1074
+
1075
+
1076
+ def _ohne_code(text: str) -> tuple[list[str], bool]:
1077
+ """Die Zeilen ohne Codebloecke und Inline-Code - Zeilennummern bleiben erhalten.
1078
+ Zweiter Wert: Ist am Ende ein Codeblock offen?
1079
+
1080
+ Code ist ZITAT: ein Befehl, eine Ausgabe, eine Meldung, die woertlich so lautet.
1081
+ Eine Rechtschreib- oder Formpruefung darin pruefte den zitierten Gegenstand und
1082
+ nicht das Dokument."""
1083
+ zeilen, zaun = [], ""
1084
+ for z in text.split("\n"):
1085
+ m = DOK_ZAUN_RE.match(z)
1086
+ if m:
1087
+ if not zaun:
1088
+ zaun = m.group(1)[0] * 3
1089
+ elif m.group(1).startswith(zaun):
1090
+ zaun = ""
1091
+ zeilen.append("")
1092
+ continue
1093
+ zeilen.append("" if zaun else re.sub(r"`[^`\n]*`", "``", z))
1094
+ return zeilen, bool(zaun)
1095
+
1096
+
1097
+ def _frontmatter_ende(zeilen: list[str]) -> int:
1098
+ if zeilen and zeilen[0].strip() == "---":
1099
+ for i in range(1, len(zeilen)):
1100
+ if zeilen[i].strip() == "---":
1101
+ return i + 1
1102
+ return 0
1103
+
1104
+
1105
+ # --- Pruefung 92: die Rechtschreibung (D-373) ----------------------------------------
1106
+ #
1107
+ # ANLASS, gemessen im Vorbedingungsdurchgang von 1.9.0: 603 Woerter in der Schreibung
1108
+ # vor 1996 gegen 1.192 in der geltenden, und NEUNZEHN Dokumente mischten beide - oft im
1109
+ # selben Absatz ("dass" neben "daß"). Die Klassen A, B und D schreiben nach dem
1110
+ # geltenden Duden; die Register (C) bleiben, wie sie geschrieben wurden (D-371): Ein
1111
+ # Beleg wird nicht umgeschrieben, auch nicht orthographisch.
1112
+ # WIE: eine feste Liste der Staemme, in denen das "ß" nach kurzem Vokal steht und
1113
+ # deshalb heute "ss" ist. Ein allgemeines "ß nach kurzem Vokal" kann keine Maschine
1114
+ # ohne Woerterbuch entscheiden ("Maß" gegen "Meß"); eine Liste kann es fuer die Woerter,
1115
+ # die hier vorkommen.
1116
+ # GRENZE: Ein Wort, das nicht auf der Liste steht, kommt durch. Die Liste ist aus dem
1117
+ # gemessenen Bestand gezogen (308 verschiedene Woerter mit "ß", 2026-09-25), nicht aus
1118
+ # einem Woerterbuch. Code (Bloecke und Inline) ist Zitat und wird nicht geprueft.
1119
+ P92_ALT_RE = re.compile(
1120
+ r"\b(?:[Dd]aß|\w*[Mm]uß\w*|\w*[Mm]üßt\w*|\w*[Mm]iß(?:t|\w)\w*|\w*[Mm]eß\w*|\w*MEß\w*|"
1121
+ r"\w*[Ll]äßt\w*|\w*[Vv]erläßlich\w*|\w*[Ff]aß(?:t|te|ten|bar)\w*|\w*[Pp]aß(?:t|te|ten)?\b|"
1122
+ r"\w*[Ll]aß\b|\w*[Ll]aß(?:fall|t)\w*|\w*[Ww][uü]ß(?:t|te|ten)\w*|\w*[Ss]chluß\w*|"
1123
+ r"\w*[Vv]ergi?ß(?:t|lich\w*)|\w*[Vv]ergeß\w*|\w*[Pp]rozeß\w*|\b[Bb]iß\b|\b[Rr]iß\b|"
1124
+ r"\w*[Ff]luß\w*|\w*[Ss]chuß\w*|\w*[Ee]ngpaß)")
1125
+
1126
+
1127
+ def check_rechtschreibung(root: str) -> None:
1128
+ """Pruefung 92 (D-373): Klassen A, B und D ohne Schreibung vor 1996."""
1129
+ for rel, klasse in _dokumente(root):
1130
+ if klasse == "C":
1131
+ continue
1132
+ zeilen, _ = _ohne_code(read(os.path.join(root, *rel.split("/"))))
1133
+ funde = [f"{i + 1}: {w}" for i, z in enumerate(zeilen) for w in P92_ALT_RE.findall(z)]
1134
+ if funde:
1135
+ err(f"{rel}: {len(funde)} Wort/Wörter in der Schreibung vor 1996 "
1136
+ f"({'; '.join(funde[:6])}{' …' if len(funde) > 6 else ''}). Klasse "
1137
+ f"{klasse} schreibt nach dem geltenden Duden: 'dass', 'muss', 'misst', "
1138
+ f"'Messbaum' (D-373)")
1139
+
1140
+
1141
+ # --- Pruefung 93: die Form (D-374) ---------------------------------------------------
1142
+ #
1143
+ # Vier Gegenstaende, alle in jedem Dokument der Klassen A bis D:
1144
+ # (a) jeder Codeblock ist geschlossen - ein offener verschluckt den Rest der Datei,
1145
+ # und die Assemblierung des Hauptdokuments bricht an genau dieser Stelle;
1146
+ # (b) genau eine Hauptueberschrift ('# '); mit YAML-Frontmatter hoechstens eine,
1147
+ # denn dort traegt der Kopf den Namen;
1148
+ # (c) keine uebersprungene Ueberschriftenebene ('##' auf '####');
1149
+ # (d) jede Tabellenzeile hat die Spaltenzahl ihrer Kopfzeile - ein ungeschuetzter
1150
+ # senkrechter Strich verschiebt jede Zelle rechts davon, und der Leser sieht es
1151
+ # nicht, weil die Tabelle trotzdem rendert.
1152
+ # ANLASS: Gemessen im Vorbedingungsdurchgang war der Bestand bis auf EINE Datei sauber
1153
+ # (ein Agentenprofil mit drei Hauptueberschriften). Die Pruefung ist deshalb ein
1154
+ # Waechter und kein Aufraeumen - und genau so billig soll sie bleiben.
1155
+ # GRENZE: Sie prueft die Gestalt, nicht die Gliederung - ob die Ueberschriften das
1156
+ # Richtige gliedern, bleibt Durchsicht.
1157
+ def _tabellenzellen(zeile: str) -> int:
1158
+ z = re.sub(r"`[^`]*`", "C", zeile).replace("\\|", "x").strip()
1159
+ if z.startswith("|"):
1160
+ z = z[1:]
1161
+ if z.endswith("|"):
1162
+ z = z[:-1]
1163
+ return len(z.split("|"))
1164
+
1165
+
1166
+ def check_dokumentform(root: str) -> None:
1167
+ """Pruefung 93 (D-374): Codebloecke, Hauptueberschrift, Ebenen, Tabellenspalten."""
1168
+ for rel, _klasse in _dokumente(root):
1169
+ text = read(os.path.join(root, *rel.split("/")))
1170
+ roh = text.split("\n")
1171
+ zeilen, offen = _ohne_code(text)
1172
+ if offen:
1173
+ err(f"{rel}: ein Codeblock ist bis zum Dateiende nicht geschlossen (D-374)")
1174
+ continue
1175
+ fm = _frontmatter_ende(roh)
1176
+ h1 = sum(1 for i, z in enumerate(zeilen) if i >= fm and z.startswith("# "))
1177
+ if (fm and h1 > 1) or (not fm and h1 != 1):
1178
+ err(f"{rel}: {h1} Hauptüberschriften ('# '), erwartet "
1179
+ f"{'höchstens eine (mit Frontmatter)' if fm else 'genau eine'} (D-374)")
1180
+ ebene = 0
1181
+ for i, z in enumerate(zeilen):
1182
+ m = re.match(r"(#{1,6}) ", z)
1183
+ if not m:
1184
+ continue
1185
+ n = len(m.group(1))
1186
+ if ebene and n > ebene + 1:
1187
+ err(f"{rel}:{i + 1}: Überschrift der Ebene {n} direkt unter Ebene "
1188
+ f"{ebene} – eine Ebene ist übersprungen (D-374)")
1189
+ ebene = n
1190
+ i = 0
1191
+ while i < len(zeilen) - 1:
1192
+ if (zeilen[i].lstrip().startswith("|")
1193
+ and re.match(r"\s*\|?\s*:?-{3,}", zeilen[i + 1])):
1194
+ soll = _tabellenzellen(zeilen[i])
1195
+ j = i + 2
1196
+ while j < len(zeilen) and zeilen[j].lstrip().startswith("|"):
1197
+ ist = _tabellenzellen(zeilen[j])
1198
+ if ist != soll:
1199
+ err(f"{rel}:{j + 1}: Tabellenzeile mit {ist} statt {soll} "
1200
+ f"Spalten – ein ungeschützter senkrechter Strich verschiebt "
1201
+ f"jede Zelle rechts davon (D-374)")
1202
+ j += 1
1203
+ i = j
1204
+ else:
1205
+ i += 1
1206
+
1207
+
1208
+ # --- Pruefung 94: der Steckbrief (D-375) ---------------------------------------------
1209
+ #
1210
+ # Ein Dokument der Klassen A und B traegt vor seinem ersten Abschnitt einen Steckbrief
1211
+ # (Tabelle "| Attribut | Wert |") mit einer Kennung (ID oder <Art>-ID), einer Version und
1212
+ # einem Status. Pruefung 13 prueft die FORM eines Versionsfeldes, das da ist; dass es da
1213
+ # ist, prueft sie nicht - und 116 von 215 Dokumenten hatten keines.
1214
+ # AUSGENOMMEN, jeweils mit Grund:
1215
+ # - README.md: ein Verzeichnis, kein Dokument mit eigenem Stand; ebenso die
1216
+ # Einstiegsdokumente der Wurzel (DOK_WURZEL, D-437) - sie tragen ihren Stand ueber
1217
+ # VERSION und verweisen auf die Dokumente, die einen Steckbrief tragen;
1218
+ # - die Laufzeitschicht (framework/runtime/, role-packs/*/runtime/, root-template/):
1219
+ # sie wird so in die Sitzung geladen, und ihre Laenge ist begrenzt (Pruefung 4);
1220
+ # - Ausfuellvorlagen (templates/, Musterdokumente der Overlay-Muster, der
1221
+ # Antragsvorlage, die Abbildungsvorlage der Organisationsvorgaben): Sie werden
1222
+ # kopiert und gehoeren danach dem Projekt - ein Steckbrief des Frameworks darin
1223
+ # waere eine falsche Herkunftsangabe;
1224
+ # - examples/: Beispielausgaben, deren Gestalt die eines Ergebnisses ist.
1225
+ # GRENZE: Sie prueft die ANWESENHEIT der drei Zeilen; ihren Wert prueft Pruefung 13
1226
+ # (Version) und Pruefung 55 (Status).
1227
+ P94_AUSNAHMEN = ("framework/runtime/", "templates/", "examples/")
1228
+ P94_AUSNAHMEN_RE = re.compile(
1229
+ r"(?:^|/)README\.md$|^framework/role-packs/[^/]+/runtime/|^clients/[^/]+/root-template/|"
1230
+ r"^framework/overlay-patterns/[^/]+/documents/|^governance/CHANGE_REQUEST_TEMPLATE\.md$|"
1231
+ r"^framework/org-policies/MAPPING_CLASSIFICATION\.md$")
1232
+ P94_KOPF = "| Attribut | Wert |"
1233
+
1234
+
1235
+ def check_steckbrief(root: str) -> None:
1236
+ """Pruefung 94 (D-375): Klassen A und B tragen ID, Version und Status."""
1237
+ for rel, klasse in _dokumente(root):
1238
+ if klasse not in "AB" or rel in DOK_WURZEL:
1239
+ continue
1240
+ k = rel[len(KERN) + 1:]
1241
+ if k.startswith(P94_AUSNAHMEN) or P94_AUSNAHMEN_RE.search(k):
1242
+ continue
1243
+ zeilen = read(os.path.join(root, *rel.split("/"))).split("\n")
1244
+ kopf = []
1245
+ for i, z in enumerate(zeilen):
1246
+ if z.startswith("## "):
1247
+ break
1248
+ if re.sub(r"\s+", " ", z.strip()) == P94_KOPF:
1249
+ j = i + 2
1250
+ kopf = []
1251
+ while j < len(zeilen) and zeilen[j].startswith("|"):
1252
+ kopf.append(zeilen[j])
1253
+ j += 1
1254
+ break
1255
+ fehlt = [name for name, rx in (
1256
+ ("ID", r"^\|\s*(?:[\w-]+-)?ID\s*\|"), ("Version", r"^\|\s*Version\b[^|]*\|"),
1257
+ ("Status", r"^\|\s*Status\s*\|")) if not any(re.match(rx, z) for z in kopf)]
1258
+ if fehlt:
1259
+ err(f"{rel}: {'kein Steckbrief' if not kopf else 'Steckbrief ohne ' + ', '.join(fehlt)} "
1260
+ f"vor dem ersten Abschnitt. Klasse {klasse} trägt eine Tabelle "
1261
+ f"'{P94_KOPF}' mit Kennung, Version und Status – sonst unterscheidet das "
1262
+ f"Dokument keine zwei Stände (D-375)")