@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.
- package/.gitattributes +16 -0
- package/.gitignore +51 -0
- package/.koolie/QUELLREPOSITORIUM.md +18 -0
- package/.koolie/core/CHANGELOG.md +11832 -0
- package/.koolie/core/LICENSE +674 -0
- package/.koolie/core/LICENSE-HINWEIS.md +98 -0
- package/.koolie/core/OWNERS.md +36 -0
- package/.koolie/core/VERSION +1 -0
- package/.koolie/core/banner.py +397 -0
- package/.koolie/core/build/README.md +34 -0
- package/.koolie/core/build/assemble.py +354 -0
- package/.koolie/core/build/build-docx.py +194 -0
- package/.koolie/core/build/doc/00-kopf.md +18 -0
- package/.koolie/core/build/doc/01-executive-summary.md +24 -0
- package/.koolie/core/build/doc/02-zielbild.md +13 -0
- package/.koolie/core/build/doc/03-ziele-nichtziele.md +28 -0
- package/.koolie/core/build/doc/04-geltungsbereich.md +25 -0
- package/.koolie/core/build/doc/05-glossar.md +47 -0
- package/.koolie/core/build/doc/06-leitprinzipien.md +5 -0
- package/.koolie/core/build/doc/07-architektur.md +60 -0
- package/.koolie/core/build/doc/07a-abbildungsschicht.md +66 -0
- package/.koolie/core/build/doc/08-trennung.md +24 -0
- package/.koolie/core/build/doc/09-betriebsmodi.md +18 -0
- package/.koolie/core/build/doc/10-arbeitsablauf.md +5 -0
- package/.koolie/core/build/doc/11-datenschutz.md +5 -0
- package/.koolie/core/build/doc/12-sicherheit.md +5 -0
- package/.koolie/core/build/doc/13-risiko.md +5 -0
- package/.koolie/core/build/doc/14-rollen.md +9 -0
- package/.koolie/core/build/doc/15-referenzstruktur.md +124 -0
- package/.koolie/core/build/doc/16-agentenanweisung.md +8 -0
- package/.koolie/core/build/doc/17-overlay.md +29 -0
- package/.koolie/core/build/doc/18-skill-standard.md +5 -0
- package/.koolie/core/build/doc/19-skill-template.md +5 -0
- package/.koolie/core/build/doc/20-referenz-skills.md +70 -0
- package/.koolie/core/build/doc/21-prompts.md +53 -0
- package/.koolie/core/build/doc/22-checklisten.md +49 -0
- package/.koolie/core/build/doc/23-entscheidungsbaeume.md +29 -0
- package/.koolie/core/build/doc/24-onboarding.md +33 -0
- package/.koolie/core/build/doc/25-governance.md +35 -0
- package/.koolie/core/build/doc/26-qs-test.md +9 -0
- package/.koolie/core/build/doc/27-pilot.md +13 -0
- package/.koolie/core/build/doc/28-uebernahme.md +5 -0
- package/.koolie/core/build/doc/29-grenzen.md +87 -0
- package/.koolie/core/build/doc/30-roadmap.md +5 -0
- package/.koolie/core/build/doc/31-anhaenge.md +151 -0
- package/.koolie/core/build/doc/32-abschluss.md +92 -0
- package/.koolie/core/build/ref-a4.docx +0 -0
- package/.koolie/core/checklists/01-preflight.md +54 -0
- package/.koolie/core/checklists/02-privacy-context.md +51 -0
- package/.koolie/core/checklists/03-before-code-change.md +51 -0
- package/.koolie/core/checklists/04-review-ai-code.md +58 -0
- package/.koolie/core/checklists/05-testing.md +48 -0
- package/.koolie/core/checklists/06-security.md +50 -0
- package/.koolie/core/checklists/07-new-dependency.md +57 -0
- package/.koolie/core/checklists/08-merge-request.md +47 -0
- package/.koolie/core/checklists/09-onboarding.md +52 -0
- package/.koolie/core/checklists/10-project-adoption.md +74 -0
- package/.koolie/core/checklists/11-framework-release.md +71 -0
- package/.koolie/core/checklists/README.md +28 -0
- package/.koolie/core/clientmap.py +1358 -0
- package/.koolie/core/clients/README.md +113 -0
- package/.koolie/core/clients/_template/CLIENT_PACK.md +197 -0
- package/.koolie/core/clients/claude-code/CLIENT_PACK.md +383 -0
- package/.koolie/core/clients/claude-code/manifest.json +232 -0
- package/.koolie/core/clients/claude-code/root-template/.claude/README.md +127 -0
- package/.koolie/core/clients/cursor/CLIENT_PACK.md +229 -0
- package/.koolie/core/clients/cursor/manifest.json +330 -0
- package/.koolie/core/clients/cursor/root-template/.cursor/README.md +36 -0
- package/.koolie/core/clients/devin-desktop/CLIENT_PACK.md +251 -0
- package/.koolie/core/clients/devin-desktop/manifest.json +163 -0
- package/.koolie/core/clients/devin-desktop/root-template/.devin/README.md +79 -0
- package/.koolie/core/clients/kiro/CLIENT_PACK.md +223 -0
- package/.koolie/core/clients/kiro/manifest.json +300 -0
- package/.koolie/core/clients/kiro/root-template/.kiro/README.md +37 -0
- package/.koolie/core/clients/openai-codex/CLIENT_PACK.md +257 -0
- package/.koolie/core/clients/openai-codex/manifest.json +208 -0
- package/.koolie/core/clients/openai-codex/root-template/.codex/README.md +35 -0
- package/.koolie/core/decision-trees/01-context-allowed.md +46 -0
- package/.koolie/core/decision-trees/02-may-ai-do-task.md +47 -0
- package/.koolie/core/decision-trees/03-analyze-or-modify.md +50 -0
- package/.koolie/core/decision-trees/04-required-review.md +49 -0
- package/.koolie/core/decision-trees/05-stop-or-escalate.md +47 -0
- package/.koolie/core/decision-trees/06-rule-placement.md +58 -0
- package/.koolie/core/decision-trees/README.md +14 -0
- package/.koolie/core/docs/ADOPTION_GUIDE.md +582 -0
- package/.koolie/core/docs/DOCUMENTATION_STANDARD.md +91 -0
- package/.koolie/core/docs/PLACEHOLDER_REGISTRY.md +79 -0
- package/.koolie/core/docs/ROADMAP.md +848 -0
- package/.koolie/core/docs/RUNTIME_GLOSSARY.md +113 -0
- package/.koolie/core/examples/README.md +11 -0
- package/.koolie/core/examples/example-ergebnisbericht.md +31 -0
- package/.koolie/core/examples/example-mr-description.md +31 -0
- package/.koolie/core/examples/example-overlay-runtime.md +48 -0
- package/.koolie/core/framework/core/00-principles.md +93 -0
- package/.koolie/core/framework/core/01-governance.md +58 -0
- package/.koolie/core/framework/core/02-privacy.md +141 -0
- package/.koolie/core/framework/core/03-security.md +86 -0
- package/.koolie/core/framework/core/04-quality.md +48 -0
- package/.koolie/core/framework/core/05-working-model.md +199 -0
- package/.koolie/core/framework/core/06-prompting-rules.md +51 -0
- package/.koolie/core/framework/core/07-review-rules.md +52 -0
- package/.koolie/core/framework/core/08-skill-conventions.md +115 -0
- package/.koolie/core/framework/core/09-risk-model.md +87 -0
- package/.koolie/core/framework/core/10-error-escalation.md +53 -0
- package/.koolie/core/framework/org-policies/MAPPING_CLASSIFICATION.md +16 -0
- package/.koolie/core/framework/org-policies/README.md +29 -0
- package/.koolie/core/framework/overlay-patterns/general/documents/branching-strategy/muster-general.md +43 -0
- package/.koolie/core/framework/overlay-patterns/general/documents/coding-guidelines/muster-general.md +82 -0
- package/.koolie/core/framework/overlay-patterns/general/documents/definition-of-done/muster-general.md +52 -0
- package/.koolie/core/framework/overlay-patterns/general/documents/definition-of-ready/muster-general.md +54 -0
- package/.koolie/core/framework/overlay-patterns/general/documents/quality/muster-general.md +59 -0
- package/.koolie/core/framework/overlay-patterns/general/documents/security/muster-general.md +52 -0
- package/.koolie/core/framework/overlay-patterns/general.md +150 -0
- package/.koolie/core/framework/role-packs/README.md +63 -0
- package/.koolie/core/framework/role-packs/_template/ROLE_PACK.md +58 -0
- package/.koolie/core/framework/role-packs/requirements-engineering/ROLE_PACK.md +158 -0
- package/.koolie/core/framework/role-packs/requirements-engineering/runtime/30-role-requirements-engineering.md +88 -0
- package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/CHANGELOG.md +10 -0
- package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/EXAMPLES.md +148 -0
- package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/SKILL.md +195 -0
- package/.koolie/core/framework/role-packs/requirements-engineering/skills/role-re-ticket/TESTS.md +22 -0
- package/.koolie/core/framework/role-packs/software-development/ROLE_PACK.md +95 -0
- package/.koolie/core/framework/role-packs/software-development/runtime/30-role-software-development.md +26 -0
- package/.koolie/core/framework/runtime/agents/fw-reviewer.md +34 -0
- package/.koolie/core/framework/runtime/client-settings.json +3 -0
- package/.koolie/core/framework/runtime/hooks.json +20 -0
- package/.koolie/core/framework/runtime/mcp-config.example.json +4 -0
- package/.koolie/core/framework/runtime/permissions.json +106 -0
- package/.koolie/core/framework/runtime/root-instruction-local.example.md +19 -0
- package/.koolie/core/framework/runtime/root-instruction.md +116 -0
- package/.koolie/core/framework/runtime/rules/00-framework-core.md +49 -0
- package/.koolie/core/framework/runtime/rules/10-privacy-security.md +49 -0
- package/.koolie/core/framework/runtime/rules/15-development-rules.md +41 -0
- package/.koolie/core/framework/runtime/rules/16-plan-spezifikation.md +32 -0
- package/.koolie/core/framework/runtime/rules/20-project-overlay.md +60 -0
- package/.koolie/core/framework/skills/fw-bugfix-prepare/CHANGELOG.md +14 -0
- package/.koolie/core/framework/skills/fw-bugfix-prepare/EXAMPLES.md +57 -0
- package/.koolie/core/framework/skills/fw-bugfix-prepare/SKILL.md +177 -0
- package/.koolie/core/framework/skills/fw-bugfix-prepare/TESTS.md +15 -0
- package/.koolie/core/framework/skills/fw-change-analyze/CHANGELOG.md +12 -0
- package/.koolie/core/framework/skills/fw-change-analyze/EXAMPLES.md +66 -0
- package/.koolie/core/framework/skills/fw-change-analyze/SKILL.md +167 -0
- package/.koolie/core/framework/skills/fw-change-analyze/TESTS.md +16 -0
- package/.koolie/core/framework/skills/fw-change-small/CHANGELOG.md +13 -0
- package/.koolie/core/framework/skills/fw-change-small/EXAMPLES.md +68 -0
- package/.koolie/core/framework/skills/fw-change-small/SKILL.md +175 -0
- package/.koolie/core/framework/skills/fw-change-small/TESTS.md +13 -0
- package/.koolie/core/framework/skills/fw-code-explain/CHANGELOG.md +11 -0
- package/.koolie/core/framework/skills/fw-code-explain/EXAMPLES.md +61 -0
- package/.koolie/core/framework/skills/fw-code-explain/SKILL.md +163 -0
- package/.koolie/core/framework/skills/fw-code-explain/TESTS.md +11 -0
- package/.koolie/core/framework/skills/fw-docs-update/CHANGELOG.md +10 -0
- package/.koolie/core/framework/skills/fw-docs-update/EXAMPLES.md +59 -0
- package/.koolie/core/framework/skills/fw-docs-update/SKILL.md +160 -0
- package/.koolie/core/framework/skills/fw-docs-update/TESTS.md +12 -0
- package/.koolie/core/framework/skills/fw-error-analyze/CHANGELOG.md +10 -0
- package/.koolie/core/framework/skills/fw-error-analyze/EXAMPLES.md +67 -0
- package/.koolie/core/framework/skills/fw-error-analyze/SKILL.md +160 -0
- package/.koolie/core/framework/skills/fw-error-analyze/TESTS.md +12 -0
- package/.koolie/core/framework/skills/fw-mr-description/CHANGELOG.md +12 -0
- package/.koolie/core/framework/skills/fw-mr-description/EXAMPLES.md +69 -0
- package/.koolie/core/framework/skills/fw-mr-description/SKILL.md +170 -0
- package/.koolie/core/framework/skills/fw-mr-description/TESTS.md +12 -0
- package/.koolie/core/framework/skills/fw-overlay-pflege/CHANGELOG.md +7 -0
- package/.koolie/core/framework/skills/fw-overlay-pflege/EXAMPLES.md +48 -0
- package/.koolie/core/framework/skills/fw-overlay-pflege/SKILL.md +147 -0
- package/.koolie/core/framework/skills/fw-overlay-pflege/TESTS.md +13 -0
- package/.koolie/core/framework/skills/fw-plan/CHANGELOG.md +14 -0
- package/.koolie/core/framework/skills/fw-plan/EXAMPLES.md +69 -0
- package/.koolie/core/framework/skills/fw-plan/SKILL.md +168 -0
- package/.koolie/core/framework/skills/fw-plan/TESTS.md +14 -0
- package/.koolie/core/framework/skills/fw-refactor/CHANGELOG.md +10 -0
- package/.koolie/core/framework/skills/fw-refactor/EXAMPLES.md +70 -0
- package/.koolie/core/framework/skills/fw-refactor/SKILL.md +173 -0
- package/.koolie/core/framework/skills/fw-refactor/TESTS.md +14 -0
- package/.koolie/core/framework/skills/fw-repo-analyze/CHANGELOG.md +10 -0
- package/.koolie/core/framework/skills/fw-repo-analyze/EXAMPLES.md +52 -0
- package/.koolie/core/framework/skills/fw-repo-analyze/SKILL.md +149 -0
- package/.koolie/core/framework/skills/fw-repo-analyze/TESTS.md +11 -0
- package/.koolie/core/framework/skills/fw-review-support/CHANGELOG.md +12 -0
- package/.koolie/core/framework/skills/fw-review-support/EXAMPLES.md +69 -0
- package/.koolie/core/framework/skills/fw-review-support/SKILL.md +179 -0
- package/.koolie/core/framework/skills/fw-review-support/TESTS.md +13 -0
- package/.koolie/core/framework/skills/fw-tests/CHANGELOG.md +9 -0
- package/.koolie/core/framework/skills/fw-tests/EXAMPLES.md +65 -0
- package/.koolie/core/framework/skills/fw-tests/SKILL.md +169 -0
- package/.koolie/core/framework/skills/fw-tests/TESTS.md +12 -0
- package/.koolie/core/framework/tech-packs/README.md +15 -0
- package/.koolie/core/framework/tech-packs/_template/TECH_PACK.md +61 -0
- package/.koolie/core/governance/ADOPTION_REGISTRY.md +89 -0
- package/.koolie/core/governance/CHANGE_REQUEST_TEMPLATE.md +60 -0
- package/.koolie/core/governance/DECISION_LOG.md +779 -0
- package/.koolie/core/governance/EXCEPTION_PROCESS.md +31 -0
- package/.koolie/core/governance/FEEDBACK_PROCESS.md +34 -0
- package/.koolie/core/governance/FRAMEWORK_DEV_PROFILE.md +135 -0
- package/.koolie/core/governance/INCIDENT_HANDLING.md +47 -0
- package/.koolie/core/governance/PRIORITY_HIERARCHY.md +61 -0
- package/.koolie/core/governance/RACI.md +37 -0
- package/.koolie/core/governance/RELEASE_PROCESS.md +183 -0
- package/.koolie/core/governance/change-requests/CR-2026-001-definition-release-1-0-0.md +67 -0
- package/.koolie/core/governance/change-requests/CR-2026-002-client-packs.md +63 -0
- package/.koolie/core/governance/change-requests/CR-2026-003-install-client-schalter.md +70 -0
- package/.koolie/core/governance/change-requests/CR-2026-004-client-pack-claude-code.md +82 -0
- package/.koolie/core/governance/change-requests/CR-2026-005-kern-neutralisierung.md +75 -0
- package/.koolie/core/governance/change-requests/CR-2026-006-gemeinsame-skill-quelle.md +77 -0
- package/.koolie/core/governance/change-requests/CR-2026-007-geteilte-kernbestandteile.md +80 -0
- package/.koolie/core/governance/change-requests/CR-2026-008-berechtigungen-und-hooks.md +85 -0
- package/.koolie/core/governance/change-requests/CR-2026-009-umbenennung-leitwerk.md +77 -0
- package/.koolie/core/governance/change-requests/CR-2026-010-restduplikation.md +73 -0
- package/.koolie/core/governance/change-requests/CR-2026-011-hauptdokument.md +72 -0
- package/.koolie/core/governance/change-requests/CR-2026-012-schreibschutz-kernverzeichnis.md +106 -0
- package/.koolie/core/governance/change-requests/CR-2026-013-validator-blindstellen.md +128 -0
- package/.koolie/core/governance/change-requests/CR-2026-014-kurzform-langform.md +125 -0
- package/.koolie/core/governance/change-requests/CR-2026-015-versionskette.md +150 -0
- package/.koolie/core/governance/change-requests/CR-2026-016-ap2-claude-code.md +127 -0
- package/.koolie/core/governance/change-requests/CR-2026-017-ladebedingungen-claude-code.md +173 -0
- package/.koolie/core/governance/change-requests/CR-2026-018-quellenliste-claude-code.md +121 -0
- package/.koolie/core/governance/change-requests/CR-2026-019-strukturentscheidungen-fortschreiben.md +143 -0
- package/.koolie/core/governance/change-requests/CR-2026-020-akteursbezeichnung.md +118 -0
- package/.koolie/core/governance/change-requests/CR-2026-021-hook-interpreter.md +113 -0
- package/.koolie/core/governance/change-requests/CR-2026-022-artefaktversionen.md +73 -0
- package/.koolie/core/governance/change-requests/CR-2026-023-shell-lesesperre.md +128 -0
- package/.koolie/core/governance/change-requests/CR-2026-024-clientbindungen.md +78 -0
- package/.koolie/core/governance/change-requests/CR-2026-025-betriebsmodi.md +110 -0
- package/.koolie/core/governance/change-requests/CR-2026-026-fail-closed.md +134 -0
- package/.koolie/core/governance/change-requests/CR-2026-027-zeichenlimit-einstufung.md +105 -0
- package/.koolie/core/governance/change-requests/CR-2026-028-berechtigungsmodi.md +130 -0
- package/.koolie/core/governance/change-requests/CR-2026-029-hook-ablageort.md +115 -0
- package/.koolie/core/governance/change-requests/CR-2026-030-lesende-werkzeuge.md +143 -0
- package/.koolie/core/governance/change-requests/CR-2026-031-regelquellen-ausserhalb.md +182 -0
- package/.koolie/core/governance/change-requests/CR-2026-032-fremde-skill-ablage.md +151 -0
- package/.koolie/core/governance/change-requests/CR-2026-033-modusabhaengige-durchsetzung.md +131 -0
- package/.koolie/core/governance/change-requests/CR-2026-034-nachweisbedingungen.md +116 -0
- package/.koolie/core/governance/change-requests/CR-2026-035-readme-in-der-regelablage.md +115 -0
- package/.koolie/core/governance/change-requests/CR-2026-036-ortsangaben-hook-konfiguration.md +108 -0
- package/.koolie/core/governance/change-requests/CR-2026-037-meldender-hook-clientgebunden.md +133 -0
- package/.koolie/core/governance/change-requests/CR-2026-038-importsteuerung.md +159 -0
- package/.koolie/core/governance/change-requests/CR-2026-039-normative-saetze-in-kommentaren.md +124 -0
- package/.koolie/core/governance/change-requests/CR-2026-040-erh01-ist-clientgebunden.md +115 -0
- package/.koolie/core/governance/change-requests/CR-2026-041-s5-nicht-abbildbar.md +128 -0
- package/.koolie/core/governance/change-requests/CR-2026-042-abwesenheitsnachweis-suchwerkzeug.md +102 -0
- package/.koolie/core/governance/change-requests/CR-2026-043-validator-gibt-trefferwerte-aus.md +135 -0
- package/.koolie/core/governance/change-requests/CR-2026-044-aktivierungspruefung-clientgebunden.md +112 -0
- package/.koolie/core/governance/change-requests/CR-2026-045-update-verliert-die-clientwahl.md +116 -0
- package/.koolie/core/governance/change-requests/CR-2026-046-erstinstallation-ueberschreibt-projektdatei.md +125 -0
- package/.koolie/core/governance/change-requests/CR-2026-047-zusagen-je-zugriffskanal.md +133 -0
- package/.koolie/core/governance/change-requests/CR-2026-048-modusgrenze-ohne-durchsetzung.md +137 -0
- package/.koolie/core/governance/change-requests/CR-2026-049-sondenlauf-kodierungsabhaengig.md +126 -0
- package/.koolie/core/governance/change-requests/CR-2026-050-allowed-tools-und-verworfene-permissions.md +110 -0
- package/.koolie/core/governance/change-requests/CR-2026-051-quellenkarte-und-ueberholter-stand.md +110 -0
- package/.koolie/core/governance/change-requests/CR-2026-052-drei-regelkonflikte.md +166 -0
- package/.koolie/core/governance/change-requests/CR-2026-053-lesesperre-und-entwicklungsprofil.md +144 -0
- package/.koolie/core/governance/change-requests/CR-2026-054-aktivierung-verlangt-aktivitaet.md +124 -0
- package/.koolie/core/governance/change-requests/CR-2026-055-domain-ausnahme-ohne-mechanismus.md +173 -0
- package/.koolie/core/governance/change-requests/CR-2026-056-eingabeschema-und-pfadidentitaet.md +245 -0
- package/.koolie/core/governance/change-requests/CR-2026-057-disallowed-tools-und-s3.md +148 -0
- package/.koolie/core/governance/change-requests/CR-2026-058-unteragent-reichweite.md +203 -0
- package/.koolie/core/governance/change-requests/CR-2026-059-unteragent-tiefe-und-widerspruch.md +125 -0
- package/.koolie/core/governance/change-requests/CR-2026-060-stumme-brueche.md +170 -0
- package/.koolie/core/governance/change-requests/CR-2026-061-berechtigungsdatei-ohne-pruefung.md +169 -0
- package/.koolie/core/governance/change-requests/CR-2026-062-werkzeugabbildung-des-frontmatters.md +178 -0
- package/.koolie/core/governance/change-requests/CR-2026-063-skillaufruf-ohne-freigabe.md +207 -0
- package/.koolie/core/governance/change-requests/CR-2026-064-pruefregister-nicht-nachgezaehlt.md +141 -0
- package/.koolie/core/governance/change-requests/CR-2026-065-devin-suchwerkzeuge-erhoben.md +170 -0
- package/.koolie/core/governance/change-requests/CR-2026-066-schlitzdeckung-ohne-abgleich.md +153 -0
- package/.koolie/core/governance/change-requests/CR-2026-067-hookblock-und-praeparationsregister.md +140 -0
- package/.koolie/core/governance/change-requests/CR-2026-068-sondenlauf-nebenlaeufig.md +106 -0
- package/.koolie/core/governance/change-requests/CR-2026-069-bytecode-des-kerns.md +108 -0
- package/.koolie/core/governance/change-requests/CR-2026-070-d11-zaehler.md +114 -0
- package/.koolie/core/governance/change-requests/CR-2026-071-strukturentscheidungen-bestaetigen.md +322 -0
- package/.koolie/core/governance/change-requests/CR-2026-072-modulstatus-heben.md +315 -0
- package/.koolie/core/governance/change-requests/CR-2026-073-nicht-skill-traeger-heben.md +246 -0
- package/.koolie/core/governance/change-requests/CR-2026-074-restliche-nicht-skill-traeger-heben.md +271 -0
- package/.koolie/core/governance/change-requests/CR-2026-075-ap2-zielversion-devin-desktop.md +335 -0
- package/.koolie/core/governance/change-requests/CR-2026-076-erster-sitzungstest.md +455 -0
- package/.koolie/core/governance/change-requests/CR-2026-077-sitzungstest-schranken.md +425 -0
- package/.koolie/core/governance/change-requests/CR-2026-078-planung-nach-1-0-0.md +294 -0
- package/.koolie/core/governance/change-requests/CR-2026-079-umbenennung-vorziehen.md +233 -0
- package/.koolie/core/governance/change-requests/CR-2026-080-clientbindung-des-kerns.md +321 -0
- package/.koolie/core/governance/change-requests/CR-2026-081-produktnamen-im-kern.md +297 -0
- package/.koolie/core/governance/change-requests/CR-2026-082-sitzungstest-pi-ds-2.md +145 -0
- package/.koolie/core/governance/change-requests/CR-2026-083-sitzungstest-ne-sc.md +300 -0
- package/.koolie/core/governance/change-requests/CR-2026-084-trockenlauf-gegen-den-committeten-stand.md +117 -0
- package/.koolie/core/governance/change-requests/CR-2026-085-vorbedingungen-sitzungstest-5.md +182 -0
- package/.koolie/core/governance/change-requests/CR-2026-086-grenzfaelle-gegen-die-fassungen.md +164 -0
- package/.koolie/core/governance/change-requests/CR-2026-087-produktbeobachtung-quellenliste.md +206 -0
- package/.koolie/core/governance/change-requests/CR-2026-088-vorbedingungen-testblaetter.md +127 -0
- package/.koolie/core/governance/change-requests/CR-2026-089-herrichtung-uebungsrepositorium.md +226 -0
- package/.koolie/core/governance/change-requests/CR-2026-090-vorbedingungen-sitzungstest-5-zweiter-durchgang.md +255 -0
- package/.koolie/core/governance/change-requests/CR-2026-091-sitzungstest-5.md +163 -0
- package/.koolie/core/governance/change-requests/CR-2026-092-pruefmittelwort-und-buendelschnitt.md +226 -0
- package/.koolie/core/governance/change-requests/CR-2026-093-sechzehnte-praeparation.md +151 -0
- package/.koolie/core/governance/change-requests/CR-2026-094-testblaetter-buendel-1.md +113 -0
- package/.koolie/core/governance/change-requests/CR-2026-095-pruefapparat-filter.md +65 -0
- package/.koolie/core/governance/change-requests/CR-2026-096-vorbedingungen-buendel-2.md +225 -0
- package/.koolie/core/governance/change-requests/CR-2026-097-testblaetter-buendel-2.md +162 -0
- package/.koolie/core/governance/change-requests/CR-2026-098-anforderungen-auslieferung.md +93 -0
- package/.koolie/core/governance/change-requests/CR-2026-099-k74-und-vorbedingungen-buendel-3.md +119 -0
- package/.koolie/core/governance/change-requests/CR-2026-100-testblaetter-buendel-3.md +120 -0
- package/.koolie/core/governance/change-requests/CR-2026-101-k77-eigener-posten.md +165 -0
- package/.koolie/core/governance/change-requests/CR-2026-102-k77-entschieden.md +118 -0
- package/.koolie/core/governance/change-requests/CR-2026-103-vorbedingungen-buendel-4.md +76 -0
- package/.koolie/core/governance/change-requests/CR-2026-104-herrichtung-buendel-4.md +82 -0
- package/.koolie/core/governance/change-requests/CR-2026-105-messapparat-buendel-4.md +227 -0
- package/.koolie/core/governance/change-requests/CR-2026-106-uebergabe-einchecken.md +91 -0
- package/.koolie/core/governance/change-requests/CR-2026-107-uebergabe-im-release-commit.md +111 -0
- package/.koolie/core/governance/change-requests/CR-2026-108-testblaetter-buendel-4.md +118 -0
- package/.koolie/core/governance/change-requests/CR-2026-109-berichtsweg-des-aufraeumers.md +108 -0
- package/.koolie/core/governance/change-requests/CR-2026-110-vorbedingungen-des-nachlaufs.md +184 -0
- package/.koolie/core/governance/change-requests/CR-2026-111-der-apparat-auf-dem-weg-der-wiederaufnahme.md +127 -0
- package/.koolie/core/governance/change-requests/CR-2026-112-der-arbeitsplatz-im-kern.md +103 -0
- package/.koolie/core/governance/change-requests/CR-2026-113-nachlauf-buendel-4.md +103 -0
- package/.koolie/core/governance/change-requests/CR-2026-114-vorbedingungen-buendel-5.md +317 -0
- package/.koolie/core/governance/change-requests/CR-2026-115-herrichtung-buendel-5.md +244 -0
- package/.koolie/core/governance/change-requests/CR-2026-116-messtag-buendel-5.md +178 -0
- package/.koolie/core/governance/change-requests/CR-2026-117-sammelzellen-zentraler-katalog.md +251 -0
- package/.koolie/core/governance/change-requests/CR-2026-118-quellenzuordnung-matrixzeilen.md +115 -0
- package/.koolie/core/governance/change-requests/CR-2026-119-umbenennung-koolie.md +204 -0
- package/.koolie/core/governance/change-requests/CR-2026-120-ap2-rest-devin-desktop.md +96 -0
- package/.koolie/core/governance/change-requests/CR-2026-121-verify-marker-abschaffen.md +185 -0
- package/.koolie/core/governance/change-requests/CR-2026-122-umbenennung-koolie-lauf.md +131 -0
- package/.koolie/core/governance/change-requests/CR-2026-123-vortrag-und-namensabsatz.md +170 -0
- package/.koolie/core/governance/change-requests/CR-2026-124-hauptdokument-ap11.md +237 -0
- package/.koolie/core/governance/change-requests/CR-2026-125-word-fassung-und-blinder-fleck.md +204 -0
- package/.koolie/core/governance/change-requests/CR-2026-126-lizenz-gpl3.md +112 -0
- package/.koolie/core/governance/change-requests/CR-2026-127-gegenzeichnung-rollenfrage.md +96 -0
- package/.koolie/core/governance/change-requests/CR-2026-128-freigabelauf-1.0.0.md +278 -0
- package/.koolie/core/governance/change-requests/CR-2026-129-archiv-zeilenenden.md +90 -0
- package/.koolie/core/governance/change-requests/CR-2026-130-reihenfolge-des-hebens.md +204 -0
- package/.koolie/core/governance/change-requests/CR-2026-131-chronik-und-vorlage.md +194 -0
- package/.koolie/core/governance/change-requests/CR-2026-132-erhebung-openai-codex.md +185 -0
- package/.koolie/core/governance/change-requests/CR-2026-133-bau-openai-codex.md +97 -0
- package/.koolie/core/governance/change-requests/CR-2026-134-uebergabe-lokal.md +117 -0
- package/.koolie/core/governance/change-requests/CR-2026-135-pfadlisten-nul-getrennt.md +139 -0
- package/.koolie/core/governance/change-requests/CR-2026-136-overlay-werte-einordnung.md +156 -0
- package/.koolie/core/governance/change-requests/CR-2026-137-kopierweg-kern.md +144 -0
- package/.koolie/core/governance/change-requests/CR-2026-138-overlay-muster-general.md +118 -0
- package/.koolie/core/governance/change-requests/CR-2026-139-overlay-muster-dokumente.md +105 -0
- package/.koolie/core/governance/change-requests/CR-2026-140-installer-je-zielsystem.md +120 -0
- package/.koolie/core/governance/change-requests/CR-2026-141-lieferumfang.md +123 -0
- package/.koolie/core/governance/change-requests/CR-2026-142-dokumentationsstandard.md +90 -0
- package/.koolie/core/governance/change-requests/CR-2026-143-klasse-b-core-laufzeit.md +89 -0
- package/.koolie/core/governance/change-requests/CR-2026-144-klasse-b-governance.md +90 -0
- package/.koolie/core/governance/change-requests/CR-2026-145-klasse-b-skills.md +81 -0
- package/.koolie/core/governance/change-requests/CR-2026-146-code-pack-posten.md +84 -0
- package/.koolie/core/governance/change-requests/CR-2026-147-regel-register-posten.md +78 -0
- package/.koolie/core/governance/change-requests/CR-2026-148-mehrprojekt-tokenlast.md +70 -0
- package/.koolie/core/governance/change-requests/CR-2026-149-regelablage-pfadtoken.md +71 -0
- package/.koolie/core/governance/change-requests/CR-2026-150-client-pack-kiro.md +76 -0
- package/.koolie/core/governance/change-requests/CR-2026-151-skill-anweisungen.md +71 -0
- package/.koolie/core/governance/change-requests/CR-2026-152-testblaetter-modellwechsel.md +78 -0
- package/.koolie/core/governance/change-requests/CR-2026-153-attributionszeile.md +88 -0
- package/.koolie/core/governance/change-requests/CR-2026-154-auffindbarkeit.md +102 -0
- package/.koolie/core/governance/change-requests/CR-2026-155-client-pack-cursor.md +80 -0
- package/.koolie/core/governance/change-requests/CR-2026-156-mandat-und-reibung.md +100 -0
- package/.koolie/core/governance/change-requests/CR-2026-157-mcp-anbindung.md +93 -0
- package/.koolie/core/governance/change-requests/CR-2026-158-registerpflege.md +69 -0
- package/.koolie/core/governance/change-requests/CR-2026-159-powershell.md +49 -0
- package/.koolie/core/governance/change-requests/CR-2026-160-messapparat.md +59 -0
- package/.koolie/core/governance/change-requests/CR-2026-161-pruefwerkzeuge.md +52 -0
- package/.koolie/core/governance/change-requests/CR-2026-162-schutzschicht.md +65 -0
- package/.koolie/core/governance/change-requests/CR-2026-163-pfadsemantik.md +61 -0
- package/.koolie/core/governance/change-requests/CR-2026-164-modi-ausnahmen.md +65 -0
- package/.koolie/core/governance/change-requests/CR-2026-165-installer-banner.md +406 -0
- package/.koolie/core/governance/change-requests/CR-2026-166-nachlauf-aufzeichnungen.md +59 -0
- package/.koolie/core/governance/change-requests/CR-2026-167-einsatzarchitektur.md +60 -0
- package/.koolie/core/governance/change-requests/CR-2026-168-paketquellen.md +64 -0
- package/.koolie/core/governance/change-requests/CR-2026-169-modusbindung-messfragen.md +58 -0
- package/.koolie/core/governance/change-requests/CR-2026-170-erste-veroeffentlichung.md +62 -0
- package/.koolie/core/governance/change-requests/CR-2026-171-befehl-im-projektverzeichnis.md +57 -0
- package/.koolie/core/governance/change-requests/CR-2026-172-auftritt-npm-suche.md +58 -0
- package/.koolie/core/install.py +2402 -0
- package/.koolie/core/install_dialog.py +227 -0
- package/.koolie/core/koexistenz.py +112 -0
- package/.koolie/core/mandat.py +605 -0
- package/.koolie/core/onboarding/COMPLETION_CRITERIA.md +37 -0
- package/.koolie/core/onboarding/GUIDE.md +103 -0
- package/.koolie/core/onboarding/KNOWLEDGE_CHECK.md +48 -0
- package/.koolie/core/onboarding/MENTOR_CHECKLIST.md +40 -0
- package/.koolie/core/onboarding/QUICKSTART.md +50 -0
- package/.koolie/core/onboarding/REFERENCE.md +69 -0
- package/.koolie/core/onboarding/exercises/EXERCISES.md +78 -0
- package/.koolie/core/onboarding/exercises/README.md +120 -0
- package/.koolie/core/pilot/METRICS.md +59 -0
- package/.koolie/core/pilot/PILOT_CONCEPT.md +42 -0
- package/.koolie/core/prompts/01-understand-codebase.md +99 -0
- package/.koolie/core/prompts/02-impact-analysis.md +104 -0
- package/.koolie/core/prompts/03-implementation-planning.md +102 -0
- package/.koolie/core/prompts/04-code-generation.md +100 -0
- package/.koolie/core/prompts/05-test-generation.md +103 -0
- package/.koolie/core/prompts/06-refactoring.md +102 -0
- package/.koolie/core/prompts/07-debugging.md +97 -0
- package/.koolie/core/prompts/08-security-review.md +92 -0
- package/.koolie/core/prompts/09-performance-analysis.md +89 -0
- package/.koolie/core/prompts/10-documentation.md +91 -0
- package/.koolie/core/prompts/11-merge-request-review.md +94 -0
- package/.koolie/core/prompts/12-developer-training.md +90 -0
- package/.koolie/core/prompts/README.md +88 -0
- package/.koolie/core/templates/MR_AI_DISCLOSURE.md +37 -0
- package/.koolie/core/templates/PLAN_TEMPLATE.md +57 -0
- package/.koolie/core/templates/SKILL_TEMPLATE.md +159 -0
- package/.koolie/core/templates/project-overlay/OVERLAY.md +315 -0
- package/.koolie/core/templates/project-overlay/documents/README.md +43 -0
- package/.koolie/core/templates/project-overlay/documents/ai-governance/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/ai-process-model/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/architecture/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/architecture/decisions/README.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/branching-strategy/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/coding-guidelines/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/definition-of-done/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/definition-of-ready/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/deployment/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/glossary/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/quality/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/roadmap/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/roles/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/documents/security/.gitkeep.md +3 -0
- package/.koolie/core/templates/project-overlay/exceptions/EXCEPTIONS.md +9 -0
- package/.koolie/core/templates/project-overlay/forbidden-terms.txt +8 -0
- package/.koolie/core/templates/project-overlay/overlay-manifest.yaml +73 -0
- package/.koolie/core/templates/rules/21-overlay-TEMPLATE.md.template +28 -0
- package/.koolie/core/templates/rules/40-tech-TEMPLATE.md.template +33 -0
- package/.koolie/core/tests/EDGE_CASES.md +67 -0
- package/.koolie/core/tests/TEST_CATALOG.md +170 -0
- package/.koolie/core/tests/erhebungen/README.md +326 -0
- package/.koolie/core/tests/erhebungen/ablage.py +357 -0
- package/.koolie/core/tests/erhebungen/apparat/__init__.py +22 -0
- package/.koolie/core/tests/erhebungen/apparat/baum.py +214 -0
- package/.koolie/core/tests/erhebungen/apparat/belege.py +91 -0
- package/.koolie/core/tests/erhebungen/apparat/clients.py +321 -0
- package/.koolie/core/tests/erhebungen/apparat/freigabe.py +74 -0
- package/.koolie/core/tests/erhebungen/apparat/kontingent.py +64 -0
- package/.koolie/core/tests/erhebungen/apparat/laeufer.py +155 -0
- package/.koolie/core/tests/erhebungen/apparat/reihe.py +202 -0
- package/.koolie/core/tests/erhebungen/apparat/selbsttest.py +280 -0
- package/.koolie/core/tests/erhebungen/apparat/stand.py +30 -0
- package/.koolie/core/tests/erhebungen/apparat/vorpruefung.py +70 -0
- package/.koolie/core/tests/erhebungen/auswerten-b4.py +468 -0
- package/.koolie/core/tests/erhebungen/auswerten-b5.py +517 -0
- package/.koolie/core/tests/erhebungen/auswerten-dd.py +172 -0
- package/.koolie/core/tests/erhebungen/baeume-b4.py +334 -0
- package/.koolie/core/tests/erhebungen/baeume-b5.py +339 -0
- package/.koolie/core/tests/erhebungen/baeume_loeschen.py +138 -0
- package/.koolie/core/tests/erhebungen/cc-overlay-fuellen.py +277 -0
- package/.koolie/core/tests/erhebungen/dossier-b4.py +178 -0
- package/.koolie/core/tests/erhebungen/dossier-b5.py +202 -0
- package/.koolie/core/tests/erhebungen/historie-bauen-b4.py +642 -0
- package/.koolie/core/tests/erhebungen/k-bauen-b3.py +841 -0
- package/.koolie/core/tests/erhebungen/lauf-dd.py +146 -0
- package/.koolie/core/tests/erhebungen/lauf.py +114 -0
- package/.koolie/core/tests/erhebungen/mcp-waechter.py +278 -0
- package/.koolie/core/tests/erhebungen/messbaum-schnitt.py +256 -0
- package/.koolie/core/tests/erhebungen/messen.py +72 -0
- package/.koolie/core/tests/erhebungen/node-waechter.py +74 -0
- package/.koolie/core/tests/erhebungen/packaktivierung.py +403 -0
- package/.koolie/core/tests/erhebungen/prompts-schreiben-b4.py +210 -0
- package/.koolie/core/tests/erhebungen/prompts-schreiben-b5.py +251 -0
- package/.koolie/core/tests/erhebungen/reihe-b4.py +171 -0
- package/.koolie/core/tests/erhebungen/reihe-b5.py +172 -0
- package/.koolie/core/tests/erhebungen/stand-b4.py +135 -0
- package/.koolie/core/tests/erhebungen/stand-b5.py +137 -0
- package/.koolie/core/tests/erhebungen/trust-b4.py +56 -0
- package/.koolie/core/tests/erhebungen/trust-b5.py +56 -0
- package/.koolie/core/tests/erhebungen/turn2-schreiben-b4.py +105 -0
- package/.koolie/core/tests/erhebungen/umgebungen-bauen-b4.py +377 -0
- package/.koolie/core/tests/erhebungen/umgebungen-bauen-b5.py +363 -0
- package/.koolie/core/tests/erhebungen/zaehlen46.py +74 -0
- package/.koolie/core/tests/erhebungen/zustand-b4.py +82 -0
- package/.koolie/core/tests/erhebungen/zustand-b5.py +82 -0
- package/.koolie/core/tests/protocols/2026-09-10-AP2-claude-code-wirkungsnachweise.md +194 -0
- package/.koolie/core/tests/protocols/2026-09-10-AP2-claude-code.md +468 -0
- package/.koolie/core/tests/protocols/2026-09-10-CR-2026-020-akteursbezeichnung.md +99 -0
- package/.koolie/core/tests/protocols/2026-09-10-CR-2026-021-hook-interpreter.md +138 -0
- package/.koolie/core/tests/protocols/2026-09-10-CR-2026-022-artefaktversionen.md +87 -0
- package/.koolie/core/tests/protocols/2026-09-10-CR-2026-023-shell-lesesperre.md +135 -0
- package/.koolie/core/tests/protocols/2026-09-10-CR-2026-024-clientbindungen.md +98 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-DS-03.md +90 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-KO-01.md +105 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-KO-02.md +123 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-KO-04.md +73 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-RE-02.md +99 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-VN-01-wiederholung.md +108 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-VN-01.md +249 -0
- package/.koolie/core/tests/protocols/2026-09-10-FW-ZA-05.md +99 -0
- package/.koolie/core/tests/protocols/2026-09-11-AP2-devin-desktop.md +257 -0
- package/.koolie/core/tests/protocols/2026-09-11-CR-2026-026-fail-closed.md +113 -0
- package/.koolie/core/tests/protocols/2026-09-11-erhebungen-K21-K26.md +243 -0
- package/.koolie/core/tests/protocols/2026-09-11-wirkungsnachweise-0.26.0.md +101 -0
- package/.koolie/core/tests/protocols/2026-09-12-B01-allowed-tools.md +152 -0
- package/.koolie/core/tests/protocols/2026-09-12-B04-B05-gegenpruefung.md +204 -0
- package/.koolie/core/tests/protocols/2026-09-12-erhebungen-K28-S5-B9-bypass.md +268 -0
- package/.koolie/core/tests/protocols/2026-09-12-mehrprojekt-arbeitsbereich.md +154 -0
- package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.26.1.md +126 -0
- package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.27.0.md +114 -0
- package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.28.0.md +129 -0
- package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.29.0.md +121 -0
- package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.30.0.md +112 -0
- package/.koolie/core/tests/protocols/2026-09-12-wirkungsnachweise-0.31.0.md +107 -0
- package/.koolie/core/tests/protocols/2026-09-13-B06-gegenpruefung.md +370 -0
- package/.koolie/core/tests/protocols/2026-09-13-B07-B09-gegenpruefung.md +211 -0
- package/.koolie/core/tests/protocols/2026-09-13-B08-B11-gegenpruefung.md +179 -0
- package/.koolie/core/tests/protocols/2026-09-13-erhebung-disallowed-tools.md +147 -0
- package/.koolie/core/tests/protocols/2026-09-13-erhebung-unteragent-tiefe.md +139 -0
- package/.koolie/core/tests/protocols/2026-09-13-erhebung-unteragent.md +214 -0
- package/.koolie/core/tests/protocols/2026-09-13-gegenpruefung-berechtigungsdatei.md +253 -0
- package/.koolie/core/tests/protocols/2026-09-13-gegenpruefung-stumme-brueche.md +202 -0
- package/.koolie/core/tests/protocols/2026-09-13-gegenpruefung-werkzeugabbildung.md +219 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.32.0.md +133 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.33.0.md +169 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.34.0.md +187 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.35.0.md +187 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.36.0.md +137 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.37.0.md +112 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.38.0.md +154 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.39.0.md +188 -0
- package/.koolie/core/tests/protocols/2026-09-13-wirkungsnachweise-0.40.0.md +214 -0
- package/.koolie/core/tests/protocols/2026-09-14-erhebung-devin-werkzeuge.md +267 -0
- package/.koolie/core/tests/protocols/2026-09-14-erhebung-skillaufruf.md +238 -0
- package/.koolie/core/tests/protocols/2026-09-14-gegenpruefung-pruefregister.md +156 -0
- package/.koolie/core/tests/protocols/2026-09-14-gegenpruefung-schlitzdeckung.md +206 -0
- package/.koolie/core/tests/protocols/2026-09-14-gegenpruefung-skillwahl.md +127 -0
- package/.koolie/core/tests/protocols/2026-09-14-migrationslauf-pilot.md +134 -0
- package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.41.0.md +175 -0
- package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.42.0.md +180 -0
- package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.43.0.md +165 -0
- package/.koolie/core/tests/protocols/2026-09-14-wirkungsnachweise-0.44.0.md +130 -0
- package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-d11-zaehlregeln.md +311 -0
- package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-modulstatus.md +524 -0
- package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-nicht-skill-traeger.md +225 -0
- package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-restliche-nicht-skill-traeger.md +345 -0
- package/.koolie/core/tests/protocols/2026-09-15-gegenpruefung-strukturentscheidungen.md +392 -0
- package/.koolie/core/tests/protocols/2026-09-15-herrichtung-uebungsrepositorium.md +213 -0
- package/.koolie/core/tests/protocols/2026-09-15-migrationslauf-pilot-0.46.0.md +143 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.45.0.md +127 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.46.0.md +193 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.47.0.md +149 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.49.0.md +202 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.50.0.md +183 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.51.0.md +202 -0
- package/.koolie/core/tests/protocols/2026-09-15-wirkungsnachweise-0.52.0.md +168 -0
- package/.koolie/core/tests/protocols/2026-09-16-AP2-zielversion-devin-desktop.md +231 -0
- package/.koolie/core/tests/protocols/2026-09-16-wirkungsnachweise-0.53.0.md +350 -0
- package/.koolie/core/tests/protocols/2026-09-17-nachtrag-migrationshinweis-0.54.1.md +121 -0
- package/.koolie/core/tests/protocols/2026-09-17-sitzungstest-pi-ds.md +523 -0
- package/.koolie/core/tests/protocols/2026-09-17-sitzungstest-schranken.md +433 -0
- package/.koolie/core/tests/protocols/2026-09-17-wirkungsnachweise-0.54.0.md +159 -0
- package/.koolie/core/tests/protocols/2026-09-18-FW-AK-01.md +442 -0
- package/.koolie/core/tests/protocols/2026-09-18-FW-KO-05.md +231 -0
- package/.koolie/core/tests/protocols/2026-09-18-herrichtung-uebungsrepositorium.md +273 -0
- package/.koolie/core/tests/protocols/2026-09-18-nachtrag-preis-umbenennung-0.56.1.md +102 -0
- package/.koolie/core/tests/protocols/2026-09-18-sitzungstest-5.md +349 -0
- package/.koolie/core/tests/protocols/2026-09-18-sitzungstest-ne-sc.md +430 -0
- package/.koolie/core/tests/protocols/2026-09-18-sitzungstest-pi-ds-2.md +499 -0
- package/.koolie/core/tests/protocols/2026-09-18-vorbedingungen-sitzungstest-5-zweiter-durchgang.md +170 -0
- package/.koolie/core/tests/protocols/2026-09-18-vorbedingungen-testblaetter.md +184 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.55.0.md +202 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.56.0.md +135 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.56.2.md +154 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.57.0.md +228 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.57.1.md +262 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.58.0.md +135 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.59.0.md +150 -0
- package/.koolie/core/tests/protocols/2026-09-18-wirkungsnachweise-0.60.0.md +340 -0
- package/.koolie/core/tests/protocols/2026-09-19-herrichtung-buendel-4.md +235 -0
- package/.koolie/core/tests/protocols/2026-09-19-k77-zurechenbarkeit.md +236 -0
- package/.koolie/core/tests/protocols/2026-09-19-pruefapparat-filter.md +50 -0
- package/.koolie/core/tests/protocols/2026-09-19-pruefmittelwort-und-buendelschnitt.md +239 -0
- package/.koolie/core/tests/protocols/2026-09-19-sechzehnte-praeparation.md +135 -0
- package/.koolie/core/tests/protocols/2026-09-19-testblaetter-buendel-1.md +250 -0
- package/.koolie/core/tests/protocols/2026-09-19-testblaetter-buendel-2.md +229 -0
- package/.koolie/core/tests/protocols/2026-09-19-testblaetter-buendel-3.md +293 -0
- package/.koolie/core/tests/protocols/2026-09-19-vorbedingungen-buendel-2.md +236 -0
- package/.koolie/core/tests/protocols/2026-09-19-vorbedingungen-buendel-3.md +253 -0
- package/.koolie/core/tests/protocols/2026-09-19-vorbedingungen-buendel-4.md +138 -0
- package/.koolie/core/tests/protocols/2026-09-20-messapparat-buendel-4.md +433 -0
- package/.koolie/core/tests/protocols/2026-09-20-testblaetter-buendel-4.md +321 -0
- package/.koolie/core/tests/protocols/2026-09-20-uebergabe-im-release-commit.md +146 -0
- package/.koolie/core/tests/protocols/2026-09-20-vorbedingungen-nachlauf-b4.md +259 -0
- package/.koolie/core/tests/protocols/2026-09-21-arbeitsplatz-im-kern.md +127 -0
- package/.koolie/core/tests/protocols/2026-09-21-nachlauf-buendel-4.md +157 -0
- package/.koolie/core/tests/protocols/2026-09-21-vorbedingungen-buendel-5.md +247 -0
- package/.koolie/core/tests/protocols/2026-09-21-wiederaufnahme-nachlauf-b4.md +190 -0
- package/.koolie/core/tests/protocols/2026-09-22-ap2-rest-devin-desktop.md +614 -0
- package/.koolie/core/tests/protocols/2026-09-22-hauptdokument-ap11.md +443 -0
- package/.koolie/core/tests/protocols/2026-09-22-herrichtung-buendel-5.md +224 -0
- package/.koolie/core/tests/protocols/2026-09-22-messtag-buendel-5.md +327 -0
- package/.koolie/core/tests/protocols/2026-09-22-quellenzuordnung-matrixzeilen.md +230 -0
- package/.koolie/core/tests/protocols/2026-09-22-sammelzellen-zentraler-katalog.md +354 -0
- package/.koolie/core/tests/protocols/2026-09-22-umbenennung-koolie.md +296 -0
- package/.koolie/core/tests/protocols/2026-09-22-verify-marker-abschaffen.md +283 -0
- package/.koolie/core/tests/protocols/2026-09-22-vortrag-und-name.md +261 -0
- package/.koolie/core/tests/protocols/2026-09-22-wirkungsnachweise-0.86.0.md +158 -0
- package/.koolie/core/tests/protocols/2026-09-23-archiv-zeilenenden.md +113 -0
- package/.koolie/core/tests/protocols/2026-09-23-bau-openai-codex.md +168 -0
- package/.koolie/core/tests/protocols/2026-09-23-chronik-und-vorlage.md +224 -0
- package/.koolie/core/tests/protocols/2026-09-23-erhebung-openai-codex.md +309 -0
- package/.koolie/core/tests/protocols/2026-09-23-freigabelauf-1.0.0-vorbereitung.md +119 -0
- package/.koolie/core/tests/protocols/2026-09-23-freigabelauf-1.0.0.md +150 -0
- package/.koolie/core/tests/protocols/2026-09-23-gegenzeichnung-rollenfrage.md +198 -0
- package/.koolie/core/tests/protocols/2026-09-23-reihenfolge-des-hebens.md +220 -0
- package/.koolie/core/tests/protocols/2026-09-23-word-fassung-und-blinder-fleck.md +307 -0
- package/.koolie/core/tests/protocols/2026-09-24-kopierweg-kern.md +98 -0
- package/.koolie/core/tests/protocols/2026-09-24-overlay-werte-einordnung.md +111 -0
- package/.koolie/core/tests/protocols/2026-09-24-pfadlisten-nul-getrennt.md +128 -0
- package/.koolie/core/tests/protocols/2026-09-24-uebergabe-lokal.md +85 -0
- package/.koolie/core/tests/protocols/2026-09-25-code-pack-posten.md +86 -0
- package/.koolie/core/tests/protocols/2026-09-25-dokumentationsstandard.md +101 -0
- package/.koolie/core/tests/protocols/2026-09-25-installer-je-zielsystem.md +134 -0
- package/.koolie/core/tests/protocols/2026-09-25-klasse-b-core-laufzeit.md +126 -0
- package/.koolie/core/tests/protocols/2026-09-25-klasse-b-governance.md +133 -0
- package/.koolie/core/tests/protocols/2026-09-25-klasse-b-skills.md +91 -0
- package/.koolie/core/tests/protocols/2026-09-25-lieferumfang.md +76 -0
- package/.koolie/core/tests/protocols/2026-09-25-overlay-muster-dokumente.md +107 -0
- package/.koolie/core/tests/protocols/2026-09-25-overlay-muster-general.md +100 -0
- package/.koolie/core/tests/protocols/2026-09-25-regel-register-posten.md +87 -0
- package/.koolie/core/tests/protocols/2026-09-26-attributionszeile.md +83 -0
- package/.koolie/core/tests/protocols/2026-09-26-auffindbarkeit.md +85 -0
- package/.koolie/core/tests/protocols/2026-09-26-bau-cursor.md +68 -0
- package/.koolie/core/tests/protocols/2026-09-26-bau-kiro.md +75 -0
- package/.koolie/core/tests/protocols/2026-09-26-mehrprojekt-tokenlast.md +174 -0
- package/.koolie/core/tests/protocols/2026-09-26-regelablage-pfadtoken.md +133 -0
- package/.koolie/core/tests/protocols/2026-09-26-skill-anweisungen.md +111 -0
- package/.koolie/core/tests/protocols/2026-09-26-testblaetter-modellwechsel.md +106 -0
- package/.koolie/core/tests/protocols/2026-09-27-mandat-und-reibung.md +58 -0
- package/.koolie/core/tests/protocols/2026-09-28-mcp-anbindung.md +72 -0
- package/.koolie/core/tests/protocols/2026-09-29-messapparat.md +75 -0
- package/.koolie/core/tests/protocols/2026-09-29-powershell.md +58 -0
- package/.koolie/core/tests/protocols/2026-09-29-pruefwerkzeuge.md +64 -0
- package/.koolie/core/tests/protocols/2026-09-29-registerpflege.md +228 -0
- package/.koolie/core/tests/protocols/2026-09-29-schutzschicht.md +95 -0
- package/.koolie/core/tests/protocols/2026-09-30-einsatzarchitektur.md +104 -0
- package/.koolie/core/tests/protocols/2026-09-30-modi-ausnahmen.md +77 -0
- package/.koolie/core/tests/protocols/2026-09-30-nachlauf-banner.md +85 -0
- package/.koolie/core/tests/protocols/2026-09-30-paketquellen.md +65 -0
- package/.koolie/core/tests/protocols/2026-09-30-pfadsemantik.md +79 -0
- package/.koolie/core/tests/protocols/2026-10-01-auftritt-und-suche.md +58 -0
- package/.koolie/core/tests/protocols/2026-10-01-befehl-im-projektverzeichnis.md +47 -0
- package/.koolie/core/tests/protocols/2026-10-01-erste-veroeffentlichung.md +60 -0
- package/.koolie/core/tests/protocols/2026-10-01-modusbindung-messfragen.md +107 -0
- package/.koolie/core/tests/protocols/README.md +14 -0
- package/.koolie/core/tests/scripts/hook-check-secrets.py +1307 -0
- package/.koolie/core/tests/scripts/hook-overlay-status.py +120 -0
- package/.koolie/core/tests/scripts/mermaid_renderer.py +67 -0
- package/.koolie/core/tests/scripts/overlay_status.py +130 -0
- package/.koolie/core/tests/scripts/probe-pruefungen.py +110 -0
- package/.koolie/core/tests/scripts/pruefungen/__init__.py +4 -0
- package/.koolie/core/tests/scripts/pruefungen/berechtigungen.py +1559 -0
- package/.koolie/core/tests/scripts/pruefungen/bestand.py +1433 -0
- package/.koolie/core/tests/scripts/pruefungen/dokumente.py +1262 -0
- package/.koolie/core/tests/scripts/pruefungen/gemeinsam.py +784 -0
- package/.koolie/core/tests/scripts/pruefungen/hooks.py +1188 -0
- package/.koolie/core/tests/scripts/pruefungen/overlay.py +1251 -0
- package/.koolie/core/tests/scripts/pruefungen/packs.py +1314 -0
- package/.koolie/core/tests/scripts/pruefungen/register.py +950 -0
- package/.koolie/core/tests/scripts/pruefungen/testkatalog.py +1202 -0
- package/.koolie/core/tests/scripts/pruefungen/werkzeuge.py +957 -0
- package/.koolie/core/tests/scripts/sonden/__init__.py +4 -0
- package/.koolie/core/tests/scripts/sonden/apparat.py +653 -0
- package/.koolie/core/tests/scripts/sonden/teil01_grundbestand_und_installation.py +999 -0
- package/.koolie/core/tests/scripts/sonden/teil02_packs_mandat_mcp.py +942 -0
- package/.koolie/core/tests/scripts/sonden/teil03_pruefungen_26_bis_36.py +1184 -0
- package/.koolie/core/tests/scripts/sonden/teil04_pruefungen_37_bis_45.py +1491 -0
- package/.koolie/core/tests/scripts/sonden/teil05_pruefungen_46_bis_55.py +1447 -0
- package/.koolie/core/tests/scripts/sonden/teil06_pruefungen_57_bis_65.py +739 -0
- package/.koolie/core/tests/scripts/sonden/teil07_overlay_und_lieferung.py +1014 -0
- package/.koolie/core/tests/scripts/sonden/teil08_pruefungen_66_bis_80.py +1325 -0
- package/.koolie/core/tests/scripts/sonden/teil09_pruefmittel_81_und_82.py +505 -0
- package/.koolie/core/tests/scripts/sonden/teil10_pruefungen_83_bis_95.py +1218 -0
- package/.koolie/core/tests/scripts/sonden/teil11_pruefungen_104_und_105.py +149 -0
- package/.koolie/core/tests/scripts/sonden/teil12_pruefungen_106_und_107.py +136 -0
- package/.koolie/core/tests/scripts/sonden/teil13_pfad_und_mustersemantik.py +150 -0
- package/.koolie/core/tests/scripts/sonden/teil14_modi_ausnahmen_skills.py +204 -0
- package/.koolie/core/tests/scripts/sonden/teil15_banner_und_nachlauf.py +193 -0
- package/.koolie/core/tests/scripts/sonden/teil16_koexistenz.py +114 -0
- package/.koolie/core/tests/scripts/sonden/teil17_paketquellen.py +115 -0
- package/.koolie/core/tests/scripts/sonden/teil18_modusbindung_m3_m5.py +113 -0
- package/.koolie/core/tests/scripts/validate-framework.py +1104 -0
- package/.koolie/core/tests/scripts/validate-output.py +296 -0
- package/.koolie/core/wirksamkeit.py +430 -0
- package/CONTRIBUTING.md +119 -0
- package/LICENSE +674 -0
- package/QUICKSTART.en.md +135 -0
- package/QUICKSTART.md +129 -0
- package/README.en.md +108 -0
- package/README.md +107 -0
- package/install.cmd +44 -0
- package/install.command +46 -0
- package/package.json +21 -0
- package/paketquellen/README.md +41 -0
- package/paketquellen/bauen.py +411 -0
- package/paketquellen/koolie.cmd +23 -0
- package/paketquellen/koolie_befehl.py +82 -0
- package/paketquellen/npm/koolie.js +26 -0
|
@@ -0,0 +1,1314 @@
|
|
|
1
|
+
"""Die Client Packs als Dokumente: Quellenauskunft, Dokumenttabellen, normative Kommentare,
|
|
2
|
+
Regelablage, Ausfall mit Ersatz, Werkzeugabwesenheit, Zusagenfelder, Abwesenheitsbeleg,
|
|
3
|
+
Schlitzinhalte, Zusatzschluessel, Matrixzeilen, Vorlage und formatgebundene Pruefungen.
|
|
4
|
+
|
|
5
|
+
Pruefungen 19, 20, 23, 24, 25, 26, 27, 41, 42, 54, 74, 84, 87, 105, 108 und 109. Teil des Validators
|
|
6
|
+
validate-framework.py, seit 1.19.1 nach Gegenstand in Module geteilt (K-174). Das
|
|
7
|
+
Register aller Pruefungen steht im Kopfkommentar des Einstiegs, die Grenze jeder
|
|
8
|
+
einzelnen in ihrem Kopfkommentar hier."""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import re
|
|
14
|
+
|
|
15
|
+
from .gemeinsam import (
|
|
16
|
+
_client_packs, _walk_text_files, err, formatgebunden, FORMATGEBUNDENE_PRUEFUNGEN,
|
|
17
|
+
FRONTMATTER_BLOECKE, KERN, korb_zerlegung, P73_ZEILE_RE, PROJEKTPLATZHALTER, read,
|
|
18
|
+
skill_dirs, soll_korbregeln, tabellenzellen, warn)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def nicht_erreicht(man: dict) -> set:
|
|
22
|
+
"""Die Nummern, die die Ausgabeform dieses Packs nicht erreichen (Pruefung 87)."""
|
|
23
|
+
form = man.get("permissions_format", "json")
|
|
24
|
+
return {n for n, (_, formen) in FORMATGEBUNDENE_PRUEFUNGEN.items() if form not in formen}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Pruefungen 19 bis 24 (Release 0.26.0)
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
AUSKUNFT_UEBERSCHRIFT = "Anweisungs- und Konfigurationsquellen außerhalb des Projekts"
|
|
32
|
+
DATUM_RE = re.compile(r"\b\d{4}-\d{2}-\d{2}\b")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _abschnitt(text: str, ueberschrift: str) -> str | None:
|
|
36
|
+
"""Text eines Abschnitts bis zur naechsten gleichrangigen Ueberschrift."""
|
|
37
|
+
m = re.search(r"^(#{2,3})\s*\d*[a-z]?\.?\s*" + re.escape(ueberschrift) + r"\s*$",
|
|
38
|
+
text, re.M)
|
|
39
|
+
if not m:
|
|
40
|
+
return None
|
|
41
|
+
ebene = len(m.group(1))
|
|
42
|
+
rest = text[m.end():]
|
|
43
|
+
weiter = re.search(r"^#{1,%d}\s" % ebene, rest, re.M)
|
|
44
|
+
return rest[:weiter.start()] if weiter else rest
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def check_quellenauskunft(root: str) -> None:
|
|
48
|
+
"""Pruefung 19 (D-34, D-37): Jedes Client Pack gibt Auskunft ueber Quellen ausserhalb.
|
|
49
|
+
|
|
50
|
+
GRENZE DIESER PRUEFUNG - sie steht hier, weil sie im Antrag vorab benannt und nicht
|
|
51
|
+
spaeter gefunden werden soll (CR-2026-031 E4): Geprueft wird die **Anwesenheit** der
|
|
52
|
+
Auskunft, nicht ihre **Richtigkeit**. Eine falsche oder veraltete Zeile besteht sie.
|
|
53
|
+
Ein Abwesenheitsbeleg altert, und dieses Skript sieht den Unterschied nicht - "keine
|
|
54
|
+
bekannt, Stand 2026-09-11" ist am Tag der naechsten Clientversion eine Behauptung
|
|
55
|
+
ueber die Vergangenheit. Die Richtigkeit haengt an einer Erhebung, nicht an einem
|
|
56
|
+
Skript; dasselbe gilt fuer Pruefung 22.
|
|
57
|
+
"""
|
|
58
|
+
for kennung, pdir, _man in _client_packs(root):
|
|
59
|
+
rel = f"{KERN}/clients/{kennung}/CLIENT_PACK.md"
|
|
60
|
+
text = read(os.path.join(pdir, "CLIENT_PACK.md"))
|
|
61
|
+
abschnitt = _abschnitt(text, AUSKUNFT_UEBERSCHRIFT)
|
|
62
|
+
if abschnitt is None:
|
|
63
|
+
err(f"{rel}: Abschnitt '{AUSKUNFT_UEBERSCHRIFT}' fehlt. Jedes Pack fuehrt die "
|
|
64
|
+
f"bekannten Quellen ausserhalb des Repositoriums - Regeltexte, Skills, "
|
|
65
|
+
f"Agentenprofile, Berechtigungen, Hooks - oder einen datierten "
|
|
66
|
+
f"Abwesenheitsbeleg (D-34, D-37)")
|
|
67
|
+
continue
|
|
68
|
+
zeilen = [z for z in abschnitt.splitlines()
|
|
69
|
+
if z.strip().startswith("|") and not re.match(r"^\|[\s:|-]+\|?$", z.strip())]
|
|
70
|
+
inhalt = [z for z in zeilen
|
|
71
|
+
if not re.search(r"\|\s*(Quelle|Ladebedingung|Wirkung)\s*\|", z)]
|
|
72
|
+
abwesend = re.search(r"keine bekannt", abschnitt, re.I)
|
|
73
|
+
if not inhalt and not abwesend:
|
|
74
|
+
err(f"{rel}: Abschnitt '{AUSKUNFT_UEBERSCHRIFT}' ist leer. Er braucht je "
|
|
75
|
+
f"bekannter Quelle eine Zeile oder die ausdrueckliche Angabe 'keine "
|
|
76
|
+
f"bekannt' mit Datum und Erhebungsweg - ein leerer Abschnitt ist kein "
|
|
77
|
+
f"Abwesenheitsbeleg")
|
|
78
|
+
continue
|
|
79
|
+
if kennung.startswith("_"):
|
|
80
|
+
continue # Vorlage: Erhebungsstand steht als <TBD>
|
|
81
|
+
if not DATUM_RE.search(abschnitt):
|
|
82
|
+
err(f"{rel}: Abschnitt '{AUSKUNFT_UEBERSCHRIFT}' nennt keinen Erhebungsstand "
|
|
83
|
+
f"(JJJJ-MM-TT). Eine Quellenliste ohne Datum ist keine Auskunft, sondern "
|
|
84
|
+
f"eine Behauptung mit Fussnote")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# Begriff der Dokumenttabellen -> Platzhalter des Manifests. Ein Begriff ohne Eintrag
|
|
88
|
+
# bleibt unbeanstandet: "Nutzerlokale Ueberschreibung" hat kein Manifestfeld und ist
|
|
89
|
+
# zugleich die Gegenprobe dieser Pruefung.
|
|
90
|
+
GLOSSAR_ZU_PLATZHALTER = {
|
|
91
|
+
"Wurzel-Anweisungsdatei": "<ROOT_INSTRUCTION_FILE>",
|
|
92
|
+
"Laufzeitschicht": "<RUNTIME_DIR>",
|
|
93
|
+
"Berechtigungsdatei": "<PERMISSIONS_FILE>",
|
|
94
|
+
"Regelablage": "<RULES_DIR>",
|
|
95
|
+
"Skill-Ablage": "<SKILLS_DIR>",
|
|
96
|
+
"Agentenprofile": "<AGENTS_DIR>",
|
|
97
|
+
"Hook-Konfiguration": "<HOOKS_FILE>",
|
|
98
|
+
"MCP-Konfiguration": "<MCP_FILE>",
|
|
99
|
+
}
|
|
100
|
+
# <CORE_DIR> ist keine Eigenschaft eines Clients, sondern dieser Installation; ein Pack
|
|
101
|
+
# darf ihn nicht belegen (PLACEHOLDER_REGISTRY.md).
|
|
102
|
+
PLATZHALTER_OHNE_PACK = ("<CORE_DIR>",)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _tabellenzeilen(text: str):
|
|
106
|
+
for zeile in text.splitlines():
|
|
107
|
+
z = zeile.strip()
|
|
108
|
+
if not z.startswith("|") or re.match(r"^\|[\s:|-]+\|?$", z):
|
|
109
|
+
continue
|
|
110
|
+
yield tabellenzellen(z)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _spalten_je_pack(felder: list[str], packs: list[str]) -> dict[str, int]:
|
|
114
|
+
zuordnung: dict[str, int] = {}
|
|
115
|
+
for i, feld in enumerate(felder):
|
|
116
|
+
nackt = feld.strip("`* ")
|
|
117
|
+
if nackt in packs:
|
|
118
|
+
zuordnung[nackt] = i
|
|
119
|
+
return zuordnung
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _wert_passt(zelle: str, erwartet: str) -> bool:
|
|
123
|
+
"""Der Manifestwert steht in der Zelle - Schreibvarianten zugelassen.
|
|
124
|
+
|
|
125
|
+
Die Tabellen schreiben ein Verzeichnis mal mit und mal ohne abschliessenden
|
|
126
|
+
Schraegstrich und setzen den Wert gelegentlich in einen erklaerenden Satz. Geprueft
|
|
127
|
+
wird deshalb Enthaltensein, nicht Gleichheit: Diese Pruefung soll eine **abweichende**
|
|
128
|
+
Angabe finden, nicht eine anders formulierte.
|
|
129
|
+
"""
|
|
130
|
+
zelle = zelle.replace("\\", "/")
|
|
131
|
+
kandidaten = {erwartet, erwartet.rstrip("/") + "/", erwartet.rstrip("/")}
|
|
132
|
+
return any(k and k in zelle for k in kandidaten)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _spalten_vollstaendig(traeger: str, gefunden: dict, namen: list) -> None:
|
|
136
|
+
"""Jedes Pack mit Manifest hat in der Tabelle eine Spalte (1.14.0, D-421).
|
|
137
|
+
|
|
138
|
+
ANLASS: Die Pruefung verglich nur die Spalten, die es gab. Ein Pack ohne Spalte
|
|
139
|
+
fiel still heraus - mit 1.13.0 fuehrten beide Tabellen kiro nicht, und keine
|
|
140
|
+
Meldung sagte es. Eine Uebereinstimmung ueber die Haelfte der Packs ist keine.
|
|
141
|
+
"""
|
|
142
|
+
for pack in sorted(set(namen) - set(gefunden)):
|
|
143
|
+
err(f"{traeger}: die Tabelle fuehrt keine Spalte fuer '{pack}' - ein Pack ohne "
|
|
144
|
+
f"Spalte faellt aus dem Abgleich mit seinem Manifest still heraus (D-421)")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def check_dokumenttabellen(root: str) -> None:
|
|
148
|
+
"""Pruefung 20 (CR-2026-036): Dokumenttabellen und Manifest sagen dasselbe.
|
|
149
|
+
|
|
150
|
+
GRENZE: Sie prueft **Uebereinstimmung, nicht Richtigkeit.** Steht im Manifest ein
|
|
151
|
+
falscher Pfad, sind Tabelle und Manifest danach einig - und beide falsch. Was den
|
|
152
|
+
Manifestwert prueft, ist die Installation selbst und der Nachweis an ihr (D-23).
|
|
153
|
+
Abgedeckt sind nur Zeilen mit Manifestentsprechung; Begriffe ohne Feld altern weiter
|
|
154
|
+
still.
|
|
155
|
+
"""
|
|
156
|
+
packs = {k: m for k, _p, m in _client_packs(root) if m}
|
|
157
|
+
if not packs:
|
|
158
|
+
return
|
|
159
|
+
namen = sorted(packs)
|
|
160
|
+
|
|
161
|
+
registry = f"{KERN}/docs/PLACEHOLDER_REGISTRY.md"
|
|
162
|
+
pfad = os.path.join(root, *registry.split("/"))
|
|
163
|
+
if os.path.isfile(pfad):
|
|
164
|
+
text = read(pfad)
|
|
165
|
+
spalten: dict[str, int] = {}
|
|
166
|
+
for felder in _tabellenzeilen(text):
|
|
167
|
+
gefunden = _spalten_je_pack(felder, namen)
|
|
168
|
+
if gefunden:
|
|
169
|
+
spalten = gefunden
|
|
170
|
+
_spalten_vollstaendig(registry, gefunden, namen)
|
|
171
|
+
continue
|
|
172
|
+
platzhalter = felder[0].strip("`* ")
|
|
173
|
+
if not spalten or not platzhalter.startswith("<"):
|
|
174
|
+
continue
|
|
175
|
+
if platzhalter in PLATZHALTER_OHNE_PACK:
|
|
176
|
+
continue
|
|
177
|
+
for pack, i in spalten.items():
|
|
178
|
+
erwartet = (packs[pack].get("runtime_placeholders") or {}).get(platzhalter)
|
|
179
|
+
if erwartet is None or i >= len(felder):
|
|
180
|
+
continue
|
|
181
|
+
if not _wert_passt(felder[i].strip("`* "), erwartet):
|
|
182
|
+
err(f"{registry}: {platzhalter} steht fuer '{pack}' als "
|
|
183
|
+
f"'{felder[i]}', das Manifest fuehrt '{erwartet}'. Die "
|
|
184
|
+
f"maschinenlesbare Quelle gilt; die Tabelle wird nachgezogen")
|
|
185
|
+
|
|
186
|
+
glossar = f"{KERN}/docs/RUNTIME_GLOSSARY.md"
|
|
187
|
+
pfad = os.path.join(root, *glossar.split("/"))
|
|
188
|
+
if os.path.isfile(pfad):
|
|
189
|
+
text = read(pfad)
|
|
190
|
+
spalten = {}
|
|
191
|
+
for felder in _tabellenzeilen(text):
|
|
192
|
+
gefunden = _spalten_je_pack(felder, namen)
|
|
193
|
+
if gefunden:
|
|
194
|
+
spalten = gefunden
|
|
195
|
+
_spalten_vollstaendig(glossar, gefunden, namen)
|
|
196
|
+
continue
|
|
197
|
+
if not spalten:
|
|
198
|
+
continue
|
|
199
|
+
begriff = felder[0].strip("`* ")
|
|
200
|
+
platzhalter = GLOSSAR_ZU_PLATZHALTER.get(begriff)
|
|
201
|
+
if not platzhalter:
|
|
202
|
+
continue # Begriff ohne Manifestfeld - siehe Kommentar oben
|
|
203
|
+
for pack, i in spalten.items():
|
|
204
|
+
erwartet = (packs[pack].get("runtime_placeholders") or {}).get(platzhalter)
|
|
205
|
+
if erwartet is None or i >= len(felder):
|
|
206
|
+
continue
|
|
207
|
+
if not _wert_passt(felder[i], erwartet):
|
|
208
|
+
err(f"{glossar}: '{begriff}' steht fuer '{pack}' als '{felder[i]}', "
|
|
209
|
+
f"das Manifest fuehrt '{erwartet}'. Die maschinenlesbare Quelle "
|
|
210
|
+
f"gilt; die Tabelle wird nachgezogen")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# Normative Schluesselwoerter. Ein Kommentar traegt Herkunft - Ebene, Version, Owner,
|
|
214
|
+
# Ladeverhalten -, keine Anweisung: Er erreicht nicht jede Sitzung (ERH-01, K-28, D-38).
|
|
215
|
+
NORMATIVE_WOERTER = (
|
|
216
|
+
re.compile(r"\bMUSS\b"),
|
|
217
|
+
re.compile(r"\bMUESSEN\b"),
|
|
218
|
+
re.compile(r"\bDARF NICHT\b"),
|
|
219
|
+
re.compile(r"\bDUERFEN NICHT\b"),
|
|
220
|
+
re.compile(r"\bSOLL\b"),
|
|
221
|
+
re.compile(r"\bNICHT ZULAESSIG\b"),
|
|
222
|
+
re.compile(r"(?i)nur über den änderungsprozess"),
|
|
223
|
+
re.compile(r"(?i)darf nur über"),
|
|
224
|
+
)
|
|
225
|
+
HTML_KOMMENTAR_RE = re.compile(r"<!--(.*?)-->", re.S)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _laufzeitartefakte(root: str, man: dict):
|
|
229
|
+
"""Die Artefakte, die in einer Sitzung geladen werden - Quelle und Installation."""
|
|
230
|
+
for unterbau in (f"{KERN}/framework/runtime", f"{KERN}/templates/rules"):
|
|
231
|
+
basis = os.path.join(root, *unterbau.split("/"))
|
|
232
|
+
if os.path.isdir(basis):
|
|
233
|
+
for pfad in _walk_text_files(basis):
|
|
234
|
+
if pfad.endswith((".md", ".template")):
|
|
235
|
+
yield pfad
|
|
236
|
+
orte = [man.get("root_instruction_file"),
|
|
237
|
+
(man.get("runtime_placeholders") or {}).get("<RULES_DIR>")]
|
|
238
|
+
for ort in orte:
|
|
239
|
+
if not ort:
|
|
240
|
+
continue
|
|
241
|
+
ziel = os.path.join(root, *ort.split("/"))
|
|
242
|
+
if os.path.isfile(ziel):
|
|
243
|
+
yield ziel
|
|
244
|
+
elif os.path.isdir(ziel):
|
|
245
|
+
for pfad in _walk_text_files(ziel):
|
|
246
|
+
if pfad.endswith((".md", ".template")):
|
|
247
|
+
yield pfad
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def check_normative_kommentare(root: str, man: dict) -> None:
|
|
251
|
+
"""Pruefung 23 (D-38): Kein normatives Schluesselwort in einem HTML-Kommentar.
|
|
252
|
+
|
|
253
|
+
Gemessen an **beiden** Clients, mit entgegengesetztem Ergebnis. Bei 'claude-code' blieb
|
|
254
|
+
dieselbe Messmarke unsichtbar, solange sie in Kommentarklammern stand, und war im
|
|
255
|
+
Klartext sofort im Kontext - in der Wurzel-Anweisungsdatei und in der Regelablage
|
|
256
|
+
(2026-09-11, ERH-01). Bei 'devin-desktop' steht der Kommentar **woertlich** in dem
|
|
257
|
+
Regelblock, den der Client bildet; die Sitzung gab die Marke aus dem Kommentar zurueck,
|
|
258
|
+
ohne eine Datei zu lesen (2026-09-12, K-28).
|
|
259
|
+
|
|
260
|
+
Genau das traegt die Pruefung. Ein Ablageort, an dem eine Aussage bei dem einen Client
|
|
261
|
+
verschwindet und beim anderen mitlaeuft, ist fuer eine normative Aussage untauglich:
|
|
262
|
+
Was gilt, darf nicht davon abhaengen, mit welchem Werkzeug gearbeitet wird. Die
|
|
263
|
+
Begruendung ist damit belastbarer als vorher - die alte ('erreicht die Sitzung nicht')
|
|
264
|
+
waere mit einem Client, der Kommentare durchreicht, hinfaellig gewesen; genau so einer
|
|
265
|
+
ist gemessen worden. Was gilt, steht im Fliesstext.
|
|
266
|
+
|
|
267
|
+
GRENZE: Eine Wortlistenpruefung meldet auch eine zutreffende Erwaehnung - etwa den
|
|
268
|
+
Verweis auf eine Regel, die 'MUSS' enthaelt. Fehlalarme sind moeglich und beim
|
|
269
|
+
Beschluss ausdruecklich in Kauf genommen (CR-2026-039 E3). Die strengere Lesart gilt
|
|
270
|
+
fuer beide Packs - nicht mehr vorsorglich, weil das Verhalten des zweiten unerhoben
|
|
271
|
+
waere, sondern weil es erhoben ist und **abweicht** (K-28, CR-2026-040).
|
|
272
|
+
"""
|
|
273
|
+
gesehen = set()
|
|
274
|
+
for pfad in _laufzeitartefakte(root, man):
|
|
275
|
+
if pfad in gesehen:
|
|
276
|
+
continue
|
|
277
|
+
gesehen.add(pfad)
|
|
278
|
+
rel = os.path.relpath(pfad, root).replace(os.sep, "/")
|
|
279
|
+
text = read(pfad)
|
|
280
|
+
for m in HTML_KOMMENTAR_RE.finditer(text):
|
|
281
|
+
inhalt = m.group(1)
|
|
282
|
+
zeile = text.count("\n", 0, m.start()) + 1
|
|
283
|
+
for muster in NORMATIVE_WOERTER:
|
|
284
|
+
treffer = muster.search(inhalt)
|
|
285
|
+
if not treffer:
|
|
286
|
+
continue
|
|
287
|
+
err(f"{rel}:{zeile}: '{treffer.group(0)}' steht in einem HTML-Kommentar. "
|
|
288
|
+
f"Ein Kommentar erreicht nicht jede Sitzung (ERH-01, K-28); eine normative "
|
|
289
|
+
f"Aussage gehoert in den Fliesstext, im Kommentar bleibt die Herkunft "
|
|
290
|
+
f"(D-38)")
|
|
291
|
+
break
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
# Was in der Vorlage einer Regelablage liegen darf: Regeltexte nach dem Nummernschema
|
|
295
|
+
# und Vorlagen. Alles andere ist ein Nicht-Regeltext - der Client registriert, was in
|
|
296
|
+
# der Ablage liegt, und macht es damit ladbar (AP2-DD-17, K-26).
|
|
297
|
+
REGELDATEI_RE = re.compile(r"^\d[\dN]-[A-Za-z0-9._-]+\.md(\.template)?$")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def check_regelablage_sauber(root: str) -> None:
|
|
301
|
+
"""Pruefung 24 (D-36): In der Vorlage der Regelablage liegt nur, was Regel ist.
|
|
302
|
+
|
|
303
|
+
Bis 0.25.0 lag dort je Pack eine erklaerende README. Bei einem Pack fuehrte der
|
|
304
|
+
Client sie als Regel mit Trigger 'manual' (AP2-DD-17), beim anderen stand sie sogar
|
|
305
|
+
unbedingt im Kontext (K-26) - mit Belegvorbehalten, die damit den Rang eines
|
|
306
|
+
Regeltexts trugen. Der erklaerende Text steht seither eine Ebene hoeher, in der
|
|
307
|
+
Laufzeit-README.
|
|
308
|
+
|
|
309
|
+
GRENZE: Geprueft wird die **Vorlage** des Packs, nicht die installierte Ablage. Was
|
|
310
|
+
ein Projekt dort selbst ablegt, sieht diese Pruefung nicht.
|
|
311
|
+
"""
|
|
312
|
+
for kennung, pdir, man in _client_packs(root):
|
|
313
|
+
if not man:
|
|
314
|
+
continue
|
|
315
|
+
regelablage = (man.get("runtime_placeholders") or {}).get("<RULES_DIR>")
|
|
316
|
+
if not regelablage:
|
|
317
|
+
continue
|
|
318
|
+
basis = os.path.join(pdir, "root-template", *regelablage.split("/"))
|
|
319
|
+
if not os.path.isdir(basis):
|
|
320
|
+
continue
|
|
321
|
+
for name in sorted(os.listdir(basis)):
|
|
322
|
+
if os.path.isdir(os.path.join(basis, name)):
|
|
323
|
+
err(f"{KERN}/clients/{kennung}/root-template/{regelablage}/{name}: "
|
|
324
|
+
f"Unterverzeichnis in der Vorlage der Regelablage")
|
|
325
|
+
continue
|
|
326
|
+
if REGELDATEI_RE.match(name):
|
|
327
|
+
continue
|
|
328
|
+
err(f"{KERN}/clients/{kennung}/root-template/{regelablage}/{name}: kein "
|
|
329
|
+
f"Regeltext. Die Regelablage enthaelt ausschliesslich Regeln (D-36) - "
|
|
330
|
+
f"der Client registriert, was dort liegt, und macht es ladbar. "
|
|
331
|
+
f"Erklaerender Text gehoert in die Laufzeit-README eine Ebene hoeher")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# Pruefung 25: Eine Zeile auf [NICHT ABBILDBAR] nennt den Ersatz.
|
|
335
|
+
#
|
|
336
|
+
# Die Einstufungsklasse sagt "der Client bietet keinen Mechanismus". Sie sagt nicht, was an
|
|
337
|
+
# seine Stelle tritt - und genau dort entsteht die stillschweigende Verschlechterung: Ein
|
|
338
|
+
# Ausfall wird eingetragen, niemand widerspricht, und die Zusage ist weg, ohne dass eine
|
|
339
|
+
# Entscheidung darueber gefallen waere.
|
|
340
|
+
#
|
|
341
|
+
# D-41 unterscheidet deshalb Kernzusagen von Faehigkeitszusagen. Eine Kernzusage auf
|
|
342
|
+
# [NICHT ABBILDBAR] sperrt die Inbetriebnahme; eine Faehigkeitszusage sperrt nicht, MUSS aber
|
|
343
|
+
# den Ersatz benennen. Diese Pruefung setzt den zweiten Teil durch - den ersten kann kein
|
|
344
|
+
# Skript durchsetzen, er ist eine Freigabe durch einen Menschen.
|
|
345
|
+
NICHT_ABBILDBAR_RE = re.compile(r"`\[NICHT ABBILDBAR\]`")
|
|
346
|
+
# Die Zeilenkennung der Matrix: ein oder zwei Grossbuchstaben und eine Zahl. Bis
|
|
347
|
+
# 1.19.1 stand dieses Muster unter demselben Namen wie das von Pruefung 31, und die
|
|
348
|
+
# spaetere Bindung galt fuer beide - Pruefung 25 sah zweibuchstabige Kennungen nicht,
|
|
349
|
+
# ohne dass ein Lauf es zeigte (D-480). Jede der beiden hat jetzt ihr eigenes.
|
|
350
|
+
P25_MATRIXZEILE_RE = re.compile(r"^\|\s*([A-Z]{1,2}\d+)\s*\|")
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def check_ausfall_mit_ersatz(root: str) -> None:
|
|
354
|
+
"""Pruefung 25 (D-41): Kein Ausfall ohne benannten Ersatz.
|
|
355
|
+
|
|
356
|
+
Geprueft werden die Matrixzeilen der Client Packs - erkennbar an der Zeilenkennung am
|
|
357
|
+
Zeilenanfang (B3, S5, X2). Die Zusammenfassungstabellen desselben Dokuments fuehren
|
|
358
|
+
dieselbe Klasse als Zeilenbeschriftung; sie sind keine Zusagen und bleiben unberuehrt.
|
|
359
|
+
|
|
360
|
+
Verlangt wird das Wort 'Ersatz' in der Zeile. Das ist bewusst grob: Die Pruefung kann
|
|
361
|
+
nicht beurteilen, ob ein Ersatz taugt - sie kann nur erzwingen, dass jemand die Frage
|
|
362
|
+
gestellt und beantwortet hat. Ein 'kein Ersatz' genuegt ihr, und das ist richtig so:
|
|
363
|
+
Der Satz 'hierfuer gibt es keinen Ersatz' ist eine Aussage, die jemand verantwortet.
|
|
364
|
+
|
|
365
|
+
GRENZE: Eine Wortpruefung. Wer 'Ersatz' hinschreibt, ohne einen zu nennen, kommt durch.
|
|
366
|
+
Sie faengt das Vergessen, nicht die Absicht - wie Pruefung 23 (CR-2026-041 E3).
|
|
367
|
+
"""
|
|
368
|
+
basis = os.path.join(root, KERN, "clients")
|
|
369
|
+
if not os.path.isdir(basis):
|
|
370
|
+
return
|
|
371
|
+
for pack in sorted(os.listdir(basis)):
|
|
372
|
+
if pack.startswith("_"):
|
|
373
|
+
continue
|
|
374
|
+
pfad = os.path.join(basis, pack, "CLIENT_PACK.md")
|
|
375
|
+
if not os.path.isfile(pfad):
|
|
376
|
+
continue
|
|
377
|
+
rel = os.path.relpath(pfad, root).replace(os.sep, "/")
|
|
378
|
+
for i, zeile in enumerate(read(pfad).splitlines(), 1):
|
|
379
|
+
if not NICHT_ABBILDBAR_RE.search(zeile):
|
|
380
|
+
continue
|
|
381
|
+
m = P25_MATRIXZEILE_RE.match(zeile)
|
|
382
|
+
if not m:
|
|
383
|
+
continue
|
|
384
|
+
if "ersatz" in zeile.lower():
|
|
385
|
+
continue
|
|
386
|
+
err(f"{rel}:{i}: Zeile {m.group(1)} steht auf [NICHT ABBILDBAR], ohne einen "
|
|
387
|
+
f"Ersatz zu benennen. Ein Ausfall, der nur eingetragen und nicht ersetzt "
|
|
388
|
+
f"wird, ist eine stillschweigende Verschlechterung: Die Zeile nennt den "
|
|
389
|
+
f"Ersatz oder haelt fest, dass es keinen gibt (D-41)")
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
# Pruefung 26: Eine erklaerte Werkzeugabwesenheit muss belegt und folgerichtig sein.
|
|
393
|
+
#
|
|
394
|
+
# Seit CR-2026-047 darf ein Pack im Feld hook_tools_absent erklaeren, dass sein Client
|
|
395
|
+
# eine Werkzeugklasse gar nicht kennt; die Abbildung laesst den Hook-Matcher dann ohne
|
|
396
|
+
# diese Klasse durchlaufen, statt abzubrechen. Das ist noetig - ein Client ohne eigenes
|
|
397
|
+
# Suchwerkzeug kann keines abbilden - und es ist zugleich ein Schlupfloch: Wer 'write'
|
|
398
|
+
# dort eintraege, naehme das schreibende Werkzeug aus der Durchsetzung, und nichts
|
|
399
|
+
# meldete es.
|
|
400
|
+
#
|
|
401
|
+
# Diese Pruefung schliesst es an zwei Stellen:
|
|
402
|
+
# 1. Die Abwesenheit muss folgerichtig sein - ein Verb, das hier steht, muss auch in
|
|
403
|
+
# permission_tools leer sein. Ein Client, fuer den die Berechtigungsschicht ein
|
|
404
|
+
# Werkzeug dieser Klasse kennt, hat eines.
|
|
405
|
+
# 2. Die Abwesenheit muss erklaert sein - ein nicht leerer Begleitsatz. Dieselbe
|
|
406
|
+
# Begruendung wie bei Pruefung 25: Ein Ausfall, den jemand verantwortet, ist etwas
|
|
407
|
+
# anderes als einer, der eingetragen wurde.
|
|
408
|
+
#
|
|
409
|
+
# GRENZE: Die Pruefung belegt nicht, dass der Client das Werkzeug wirklich nicht hat -
|
|
410
|
+
# das kann nur eine Erhebung. Sie belegt, dass die beiden Felder, die es behaupten,
|
|
411
|
+
# einander nicht widersprechen und dass jemand den Satz dazu geschrieben hat.
|
|
412
|
+
def check_werkzeugabwesenheit(root: str) -> None:
|
|
413
|
+
"""Pruefung 26 (D-47): hook_tools_absent ist folgerichtig und erklaert."""
|
|
414
|
+
basis = os.path.join(root, KERN, "clients")
|
|
415
|
+
if not os.path.isdir(basis):
|
|
416
|
+
return
|
|
417
|
+
for pack in sorted(os.listdir(basis)):
|
|
418
|
+
if pack.startswith("_"):
|
|
419
|
+
continue
|
|
420
|
+
pfad = os.path.join(basis, pack, "manifest.json")
|
|
421
|
+
if not os.path.isfile(pfad):
|
|
422
|
+
continue
|
|
423
|
+
rel = os.path.relpath(pfad, root).replace(os.sep, "/")
|
|
424
|
+
try:
|
|
425
|
+
man = json.loads(read(pfad))
|
|
426
|
+
except ValueError:
|
|
427
|
+
continue
|
|
428
|
+
_p26_unerhoben_und_mcp(rel, man)
|
|
429
|
+
abwesend = man.get("hook_tools_absent") or []
|
|
430
|
+
if not abwesend:
|
|
431
|
+
continue
|
|
432
|
+
if not isinstance(abwesend, list):
|
|
433
|
+
err(f"{rel}: hook_tools_absent ist keine Liste")
|
|
434
|
+
continue
|
|
435
|
+
notiz = str(man.get("_hook_tools_absent_note") or "").strip()
|
|
436
|
+
if not notiz:
|
|
437
|
+
err(f"{rel}: hook_tools_absent nennt {sorted(abwesend)}, aber "
|
|
438
|
+
f"_hook_tools_absent_note fehlt oder ist leer. Eine Werkzeugklasse aus "
|
|
439
|
+
f"der Durchsetzung zu nehmen, ist eine Aussage ueber den Client - sie "
|
|
440
|
+
f"gehoert begruendet, nicht bloss eingetragen (D-47)")
|
|
441
|
+
werkzeuge = man.get("permission_tools") or {}
|
|
442
|
+
for verb in sorted(abwesend):
|
|
443
|
+
if werkzeuge.get(verb):
|
|
444
|
+
err(f"{rel}: hook_tools_absent erklaert das Verb '{verb}' fuer abwesend, "
|
|
445
|
+
f"permission_tools nennt dafuer aber {werkzeuge[verb]}. Beides "
|
|
446
|
+
f"zugleich geht nicht: Kennt die Berechtigungsschicht ein Werkzeug "
|
|
447
|
+
f"dieser Klasse, hat der Client eines, und der Hook muss es "
|
|
448
|
+
f"erreichen (D-47)")
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _p26_unerhoben_und_mcp(rel: str, man: dict) -> None:
|
|
452
|
+
"""Pruefung 26, seit 1.20.0 (K-184, D-486): der dritte Zustand und das Verb mcp.
|
|
453
|
+
|
|
454
|
+
'unerhoben' nimmt ein Verb aus dem Matcher wie 'abwesend' - deshalb dieselben zwei
|
|
455
|
+
Auflagen: begruendet und nicht zugleich abgebildet. Dazu das Verb mcp: Sein Matcher
|
|
456
|
+
und das Praefix, an dem der Hook es erkennt, stehen nur zusammen, und der Matcher
|
|
457
|
+
trifft das Praefix. Sonst liefe der Hook an, erkennte das Werkzeug aber nicht (dann
|
|
458
|
+
gaelte die strengste Lesart), oder er erkennte es und liefe nie an.
|
|
459
|
+
"""
|
|
460
|
+
unerhoben = man.get("hook_tools_unerhoben") or []
|
|
461
|
+
abbildung = man.get("hook_tools") or {}
|
|
462
|
+
if unerhoben:
|
|
463
|
+
if not isinstance(unerhoben, list):
|
|
464
|
+
err(f"{rel}: hook_tools_unerhoben ist keine Liste")
|
|
465
|
+
return
|
|
466
|
+
if not str(man.get("_hook_tools_unerhoben_note") or "").strip():
|
|
467
|
+
err(f"{rel}: hook_tools_unerhoben nennt {sorted(unerhoben)}, aber "
|
|
468
|
+
f"_hook_tools_unerhoben_note fehlt oder ist leer - ein ungemessenes Verb "
|
|
469
|
+
f"gehoert begruendet, nicht bloss eingetragen (D-486)")
|
|
470
|
+
for verb in sorted(unerhoben):
|
|
471
|
+
if abbildung.get(verb):
|
|
472
|
+
err(f"{rel}: hook_tools_unerhoben fuehrt das Verb '{verb}', hook_tools bildet "
|
|
473
|
+
f"es zugleich ab ({abbildung[verb]}) - gemessen oder nicht, beides geht "
|
|
474
|
+
f"nicht (D-486)")
|
|
475
|
+
matcher = abbildung.get("mcp") or []
|
|
476
|
+
praefixe = man.get("hook_mcp_prefixes") or []
|
|
477
|
+
if bool(matcher) != bool(praefixe):
|
|
478
|
+
err(f"{rel}: hook_tools.mcp und hook_mcp_prefixes stehen nur zusammen - ohne "
|
|
479
|
+
f"Praefix erkennt der Hook das Werkzeug nicht, ohne Matcher laeuft er nie an "
|
|
480
|
+
f"(K-184, D-486)")
|
|
481
|
+
return
|
|
482
|
+
for praefix in praefixe:
|
|
483
|
+
probe = praefix + "server__werkzeug"
|
|
484
|
+
try:
|
|
485
|
+
getroffen = any(re.fullmatch(m, probe) for m in matcher)
|
|
486
|
+
except re.error:
|
|
487
|
+
getroffen = False
|
|
488
|
+
if not getroffen:
|
|
489
|
+
err(f"{rel}: Kein Matcher in hook_tools.mcp trifft ein Werkzeug mit dem Praefix "
|
|
490
|
+
f"'{praefix}' (D-486)")
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
# Pruefung 27: Ein verworfenes Zusagenfeld eines Skills nennt seinen Ersatz.
|
|
494
|
+
#
|
|
495
|
+
# Dieselbe Bauform wie Pruefung 26, eine Ebene weiter: Ein Pack darf ein Frontmatter-Feld
|
|
496
|
+
# verwerfen, das sein Client nicht kennt - aber nicht eines, das eine Zusage traegt, ohne
|
|
497
|
+
# zu sagen, was an seine Stelle tritt.
|
|
498
|
+
#
|
|
499
|
+
# Zwei Faelle gab es dafuer schon: 'triggers' verfiel beim Rendern, bis AP2-CC-01 es fand
|
|
500
|
+
# und eine Abbildung bekam (model_invocation_field). 'permissions' verfiel bis 0.30.0 auf
|
|
501
|
+
# genau dieselbe Weise - im Manifest unter drop_fields deklariert, in der Wirkung
|
|
502
|
+
# unbemerkt, und dabei trug es das 'deny: edit, exec' von zwoelf Skills (B01, D-50).
|
|
503
|
+
#
|
|
504
|
+
# install.py bricht seit 0.31.0 ab, wenn das passiert. Diese Pruefung findet denselben
|
|
505
|
+
# Fehler **ohne** Installation - im Repositorium, wo ein neues Pack entsteht.
|
|
506
|
+
#
|
|
507
|
+
# GRENZE: Eine Anwesenheitspruefung auf den Begleitsatz. Ob der genannte Ersatz taugt,
|
|
508
|
+
# kann kein Skript beurteilen - wie bei Pruefung 25 wird erzwungen, dass jemand die Frage
|
|
509
|
+
# gestellt und beantwortet hat.
|
|
510
|
+
ZUSAGENFELDER_ERSATZ = {
|
|
511
|
+
"permissions": "skill_permissions_ersatz",
|
|
512
|
+
"triggers": "model_invocation_field",
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def check_zusagenfelder(root: str) -> None:
|
|
517
|
+
"""Pruefung 27 (D-50): Kein verworfenes Zusagenfeld ohne benannten Ersatz."""
|
|
518
|
+
basis = os.path.join(root, KERN, "clients")
|
|
519
|
+
if not os.path.isdir(basis):
|
|
520
|
+
return
|
|
521
|
+
for pack in sorted(os.listdir(basis)):
|
|
522
|
+
if pack.startswith("_"):
|
|
523
|
+
continue
|
|
524
|
+
pfad = os.path.join(basis, pack, "manifest.json")
|
|
525
|
+
if not os.path.isfile(pfad):
|
|
526
|
+
continue
|
|
527
|
+
rel = os.path.relpath(pfad, root).replace(os.sep, "/")
|
|
528
|
+
try:
|
|
529
|
+
man = json.loads(read(pfad))
|
|
530
|
+
except ValueError:
|
|
531
|
+
continue
|
|
532
|
+
fmt = (man.get("skill_frontmatter") or {})
|
|
533
|
+
entfallend = set(fmt.get("drop_fields") or [])
|
|
534
|
+
if fmt.get("tools_format") == "csv":
|
|
535
|
+
entfallend.add("allowed-tools")
|
|
536
|
+
for feld, ersatzfeld in sorted(ZUSAGENFELDER_ERSATZ.items()):
|
|
537
|
+
if feld not in entfallend:
|
|
538
|
+
continue
|
|
539
|
+
if str(fmt.get(ersatzfeld) or "").strip():
|
|
540
|
+
continue
|
|
541
|
+
err(f"{rel}: Das Skill-Frontmatter-Feld '{feld}' traegt eine Zusage und steht "
|
|
542
|
+
f"in skill_frontmatter.drop_fields - es entfaellt damit ersatzlos. Das Pack "
|
|
543
|
+
f"MUSS in skill_frontmatter.{ersatzfeld} benennen, was an seine Stelle "
|
|
544
|
+
f"tritt, oder festhalten, dass es keinen Ersatz gibt und was stattdessen "
|
|
545
|
+
f"traegt. Ein folgenlos verworfenes Feld ist der Befund AP2-CC-01, und bei "
|
|
546
|
+
f"'permissions' war es B01 (D-18, D-50)")
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
# ---------------------------------------------------------------------------
|
|
550
|
+
# Pruefung 41: Eine Abwesenheitserklaerung ist Enthaltung oder Beleg
|
|
551
|
+
# ---------------------------------------------------------------------------
|
|
552
|
+
#
|
|
553
|
+
# ANLASS. D-47 wurde gebaut, damit ein Pack eine Werkzeugklasse nicht dadurch aus der
|
|
554
|
+
# Durchsetzung nehmen kann, dass es sie weglaesst: Eine Abwesenheit MUSS erklaert werden.
|
|
555
|
+
# Pruefung 26 erzwingt seither, dass die Erklaerung DA ist und mit permission_tools
|
|
556
|
+
# widerspruchsfrei. Am 2026-09-14 war beides erfuellt und die Erklaerung trotzdem falsch
|
|
557
|
+
# (tests/protocols/2026-09-14-erhebung-devin-werkzeuge.md, CR-2026-065):
|
|
558
|
+
# _hook_tools_absent_note sagte "Dieser Client fuehrt kein eigenes Suchwerkzeug" - und der
|
|
559
|
+
# Client fuehrt zwei, grep und find_file_by_name. Der erzeugte Hook-Matcher kannte die
|
|
560
|
+
# Suchklasse deshalb nicht, und in einer Umgebung mit nur diesem Hook kam derselbe
|
|
561
|
+
# Secret-Wert, den ein read-Aufruf nicht bekam, ueber einen grep-Aufruf woertlich heraus.
|
|
562
|
+
#
|
|
563
|
+
# WAS PRUEFBAR IST. Nicht die Wahrheit einer Aussage - das kann kein Skript. Wohl aber,
|
|
564
|
+
# ob die Aussage sich als das ausweist, was sie ist. Zwei Bauformen sind redlich:
|
|
565
|
+
# * die ENTHALTUNG - "UNERHOBEN, nicht abwesend" (agent_start_tools_absent seit D-70);
|
|
566
|
+
# * der BELEG - ein Datum UND eine Fundstelle unter tests/protocols/. Ein
|
|
567
|
+
# blosses Wort wie "gemessen" genuegt nicht - "nicht gemessen" enthaelt es
|
|
568
|
+
# auch, und genau diese Falle stand im Entwurf dieser Pruefung.
|
|
569
|
+
# Die dritte Bauform ist die, die hier Schaden angerichtet hat: eine BEHAUPTUNG, die wie
|
|
570
|
+
# eine Feststellung klingt und weder das eine noch das andere trägt.
|
|
571
|
+
#
|
|
572
|
+
# GRENZE. Sie prueft eine FORM, nicht eine Tatsache. Wer ein Datum und einen Protokollpfad
|
|
573
|
+
# in die Note schreibt, besteht sie - auch wenn das Protokoll etwas anderes sagt. Und sie
|
|
574
|
+
# faengt nach 0.43.0 nichts mehr: Beide Packs sind in Ordnung. Ihr Wert haengt an ihren
|
|
575
|
+
# Sonden und daran, dass die naechste Abwesenheitserklaerung nicht mehr unbelegt bleibt.
|
|
576
|
+
ABWESENHEITSFELDER = (
|
|
577
|
+
("hook_tools_absent", "_hook_tools_absent_note", None),
|
|
578
|
+
("agent_start_tools_absent", "_agent_start_tools_absent_note", None),
|
|
579
|
+
("tool_names_unmapped", "_tool_names_unmapped_note", FRONTMATTER_BLOECKE),
|
|
580
|
+
("skill_deny_unmapped", "_skill_deny_unmapped_note", ("skill_frontmatter",)),
|
|
581
|
+
)
|
|
582
|
+
ENTHALTUNG = re.compile(r"unerhoben", re.I)
|
|
583
|
+
DATUM = re.compile(r"\b20\d\d-\d\d-\d\d\b")
|
|
584
|
+
FUNDSTELLE = re.compile(r"tests/protocols/")
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _abwesenheitserklaerungen(man: dict):
|
|
588
|
+
"""(Feldname, Notizname, Wert, Notiz) je erklaerter Abwesenheit dieses Manifests."""
|
|
589
|
+
for feld, notiz, bloecke in ABWESENHEITSFELDER:
|
|
590
|
+
if bloecke is None:
|
|
591
|
+
wert = man.get(feld)
|
|
592
|
+
if wert:
|
|
593
|
+
yield feld, notiz, wert, man.get(notiz)
|
|
594
|
+
continue
|
|
595
|
+
for block in bloecke:
|
|
596
|
+
fmt = man.get(block) or {}
|
|
597
|
+
wert = fmt.get(feld)
|
|
598
|
+
if wert:
|
|
599
|
+
yield f"{block}.{feld}", f"{block}.{notiz}", wert, fmt.get(notiz)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def check_abwesenheitsbeleg(root: str) -> None:
|
|
603
|
+
"""Pruefung 41 (D-88): Eine erklaerte Abwesenheit weist sich aus - oder sie belegt sich."""
|
|
604
|
+
basis = os.path.join(root, KERN, "clients")
|
|
605
|
+
if not os.path.isdir(basis):
|
|
606
|
+
return
|
|
607
|
+
manifeste = 0
|
|
608
|
+
erklaerungen = 0
|
|
609
|
+
for pack in sorted(os.listdir(basis)):
|
|
610
|
+
if pack.startswith("_"):
|
|
611
|
+
continue
|
|
612
|
+
pfad = os.path.join(basis, pack, "manifest.json")
|
|
613
|
+
if not os.path.isfile(pfad):
|
|
614
|
+
continue
|
|
615
|
+
rel = os.path.relpath(pfad, root).replace(os.sep, "/")
|
|
616
|
+
try:
|
|
617
|
+
man = json.loads(read(pfad))
|
|
618
|
+
except ValueError:
|
|
619
|
+
continue
|
|
620
|
+
manifeste += 1
|
|
621
|
+
for feld, notizname, _wert, notiz in _abwesenheitserklaerungen(man):
|
|
622
|
+
erklaerungen += 1
|
|
623
|
+
text = str(notiz or "").strip()
|
|
624
|
+
if not text:
|
|
625
|
+
continue # Die Pruefungen 26 und 38 melden die fehlende Notiz bereits
|
|
626
|
+
if ENTHALTUNG.search(text):
|
|
627
|
+
continue
|
|
628
|
+
if DATUM.search(text) and FUNDSTELLE.search(text):
|
|
629
|
+
continue
|
|
630
|
+
err(f"{rel}: {notizname} erklärt eine Abwesenheit, weist sie aber weder als "
|
|
631
|
+
f"Enthaltung aus noch belegt sie sie. Redlich sind zwei Bauformen: das "
|
|
632
|
+
f"Wort 'unerhoben' – oder ein Datum zusammen mit einer Fundstelle "
|
|
633
|
+
f"('tests/protocols/…'). Eine "
|
|
634
|
+
f"Behauptung ohne beides nimmt eine Werkzeugklasse aus der Durchsetzung "
|
|
635
|
+
f"und begründet es – genau so ist am 2026-09-14 der Schutz-Hook um die "
|
|
636
|
+
f"Suchklasse gekommen (D-88)")
|
|
637
|
+
if not manifeste:
|
|
638
|
+
err(f"{KERN}/clients: kein Manifest gefunden. Prüfung 41 misst die "
|
|
639
|
+
f"Abwesenheitserklärungen der Packs; ohne sie prüft sie nichts und bestünde "
|
|
640
|
+
f"leise (D-23)")
|
|
641
|
+
return
|
|
642
|
+
if not erklaerungen:
|
|
643
|
+
err(f"{KERN}/clients: kein Pack führt noch eine Abwesenheitserklärung. Prüfung "
|
|
644
|
+
f"41 hat ihren Gegenstand verloren und würde leise bestehen – fällt das Feld "
|
|
645
|
+
f"weg, gehört die Prüfung ausgebaut und nicht stillgelegt (D-23, D-88)")
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
# ---------------------------------------------------------------------------
|
|
649
|
+
# Pruefung 42: Ein gefuellter Schlitz traegt, was das Overlay erklaert
|
|
650
|
+
# ---------------------------------------------------------------------------
|
|
651
|
+
#
|
|
652
|
+
# ANLASS. Gemessen am 2026-09-14
|
|
653
|
+
# (tests/protocols/2026-09-14-gegenpruefung-schlitzdeckung.md, CR-2026-066): Die
|
|
654
|
+
# Kernquelle haelt im ask-Korb DREI Befehlsschlitze bereit. Pruefung 37 vergleicht
|
|
655
|
+
# Mengen und zaehlt den Ueberschuss gegen die Zahl der offenen Schlitze - WELCHER
|
|
656
|
+
# Eintrag WELCHER Schlitz ist, steht dort nicht und kann dort nicht stehen. Gemessen
|
|
657
|
+
# laufen deshalb drei beliebige Befehlsfreigaben durch, darunter eine mit Fernwirkung,
|
|
658
|
+
# und zwar in beiden Packs und auch dann, wenn das Overlay dreimal <TBD> sagt, also gar
|
|
659
|
+
# nichts erklaert. Am Piloten steht der Fall seit dem Heben auf 0.37.0:
|
|
660
|
+
# Bash(mvn -B -q compile) im ask-Korb, waehrend Abschnitt 6 <LINT_COMMAND> als "nicht
|
|
661
|
+
# vorhanden" erklaert.
|
|
662
|
+
#
|
|
663
|
+
# DIE LUECKE WAR ERKLAERT - AN EINER STELLE. CR-2026-061 Abschnitt 4 nimmt genau diesen
|
|
664
|
+
# Fall ausdruecklich aus, und D-76 wie D-77 bleiben genau. DREI ausgelieferte Texte aus
|
|
665
|
+
# DEMSELBEN Commit (34d850e, Release 0.39.0) taten es nicht: der Absatz zum Wirkungsort
|
|
666
|
+
# in templates/project-overlay/OVERLAY.md, der Kommentarkopf JEDER erzeugten
|
|
667
|
+
# Berechtigungsdatei in clientmap._kommentar und framework/core/03-security.md. Alle drei
|
|
668
|
+
# sind mit diesem Release berichtigt. Die Lehre ist die von 0.42.0, eine Ebene tiefer:
|
|
669
|
+
# Eine Enthaltung, die nur in einem Antrag steht, haelt nicht einmal bis zum Ende
|
|
670
|
+
# desselben Patches.
|
|
671
|
+
#
|
|
672
|
+
# WOHER DIE ZUORDNUNG KOMMT (CR-2026-066 E1). Es gibt sie genau einmal, im Overlay:
|
|
673
|
+
# Jeder der drei Platzhalter steht dort in genau EINER Tabellenzeile, und der Wert steht
|
|
674
|
+
# in der Zelle RECHTS DANEBEN - gemessen in allen vier geprueften Overlays, auch im
|
|
675
|
+
# Overlay des Piloten, dessen Abschnitt 6 aus einer aelteren Vorlage stammt und eine
|
|
676
|
+
# andere Spaltenueberschrift fuehrt. docs/PLACEHOLDER_REGISTRY.md weist die Herkunft
|
|
677
|
+
# ohnehin aus (Overlay 5 beziehungsweise Overlay 6); diese Pruefung setzt eine
|
|
678
|
+
# Behauptung durch, die das Register seit jeher macht. Die Zeile wird ueber die
|
|
679
|
+
# PLATZHALTERZELLE gefunden, nie ueber eine Spaltennummer - ein Overlay, das eine Spalte
|
|
680
|
+
# ergaenzt, bricht sie deshalb nicht.
|
|
681
|
+
#
|
|
682
|
+
# WARUM NICHT DIE LAUFZEITFASSUNG (E2). 20-project-overlay.md fuehrt dieselben drei
|
|
683
|
+
# Werte, aber in einer Fliesszeile ohne Schluesselspalte - und der Pilot hat sie bereits
|
|
684
|
+
# umgebaut (zwei Zeilen statt einer, ein zusaetzliches Feld). Eine Pruefung darueber
|
|
685
|
+
# fiele beim ersten echten Projekt an der FORM, nicht an der Sache.
|
|
686
|
+
#
|
|
687
|
+
# WARUM EINE EIGENE NUMMER (E6). Pruefung 37 haengt allein an der Kernquelle und laeuft
|
|
688
|
+
# ohne Overlay; diese hier enthaelt sich ohne Overlay. Zwei Gegenstaende, zwei Nummern -
|
|
689
|
+
# und 37 behaelt ihre Arithmetik, damit ihr Ergebnis nicht von der Anwesenheit eines
|
|
690
|
+
# Nachbardokuments abhaengt. Am Piloten meldet 37 deshalb weiterhin nichts, waehrend 42
|
|
691
|
+
# meldet.
|
|
692
|
+
#
|
|
693
|
+
# GRENZE, UND SIE STEHT HIER UND NICHT NUR IM ANTRAG (E5). Geprueft werden die drei
|
|
694
|
+
# BEFEHLSSCHLITZE. Die vier PFADSCHLITZE (<EXCLUDED_PATHS> zweimal, <CI_CONFIG_PATHS>,
|
|
695
|
+
# <QUALITY_GATE_CONFIG_PATHS>) bleiben ungeprueft: Sie stehen saemtlich im deny-Korb, wo
|
|
696
|
+
# Ueberzaehliges ohnehin zulaessig ist, und ihr Vergleich waere n:1 - eine Liste im
|
|
697
|
+
# Overlay gegen eine Regel in der Datei. Ein zu ENG gefuellter <EXCLUDED_PATHS>-Schlitz
|
|
698
|
+
# ist damit weiterhin eine stille Lockerung. Das ist eine Enthaltung, keine Stille.
|
|
699
|
+
#
|
|
700
|
+
# ZWEITE GRENZE. Diese Pruefung vergleicht Zeichenketten. Ob der erklaerte Befehl
|
|
701
|
+
# fachlich der richtige ist, ob er tut, was Abschnitt 6 von ihm behauptet, und ob ein
|
|
702
|
+
# Client die Regel so auswertet, wie sie gemeint ist, sagt sie nicht.
|
|
703
|
+
OVERLAY_QUELLE = ".koolie/project-overlay/OVERLAY.md"
|
|
704
|
+
# Werte, die kein Befehl sind. Die Schreibweisen stammen aus der Vorlage und aus dem
|
|
705
|
+
# Overlay des Piloten; der Vergleich laeuft in Kleinschreibung und ohne umschliessende
|
|
706
|
+
# Auszeichnung. Ein Wert mit <TBD faellt ohnehin darunter.
|
|
707
|
+
KEIN_BEFEHL = ("nicht vorhanden", "nicht erforderlich", "keine", "keiner", "keines",
|
|
708
|
+
"entfaellt", "entfällt", "nur in ci", "-", "–", "—", "")
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def overlay_befehlswert(text: str, platzhalter: str) -> tuple:
|
|
712
|
+
"""Wie oft nennt eine Tabellenzeile den Platzhalter, und was steht rechts daneben?
|
|
713
|
+
|
|
714
|
+
Rueckgabe (anzahl, wert). wert ist None, wenn die Zelle keinen Befehl traegt - ein
|
|
715
|
+
offener <TBD>-Wert, ein "nicht vorhanden" oder ein Gedankenstrich. Steht in der Zelle
|
|
716
|
+
ein Abschnitt zwischen Gegenstrichen, gilt dieser als der Wert; so ueberlebt die
|
|
717
|
+
Auswertung einen erlaeuternden Zusatz hinter dem Befehl.
|
|
718
|
+
"""
|
|
719
|
+
treffer = []
|
|
720
|
+
for felder in _tabellenzeilen(text):
|
|
721
|
+
for i, feld in enumerate(felder):
|
|
722
|
+
if feld.strip("` *") == platzhalter and i + 1 < len(felder):
|
|
723
|
+
treffer.append(felder[i + 1])
|
|
724
|
+
if len(treffer) != 1:
|
|
725
|
+
return len(treffer), None
|
|
726
|
+
zelle = treffer[0]
|
|
727
|
+
ausgezeichnet = re.search(r"`([^`]+)`", zelle)
|
|
728
|
+
wert = (ausgezeichnet.group(1) if ausgezeichnet else zelle).strip(" *")
|
|
729
|
+
if "<TBD" in wert or wert.strip().lower() in KEIN_BEFEHL:
|
|
730
|
+
return 1, None
|
|
731
|
+
return 1, wert
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def check_schlitzinhalte(root: str, man: dict) -> None:
|
|
735
|
+
"""Pruefung 42 (D-90, D-91): Der Schlitz traegt den Befehl, den das Overlay erklaert.
|
|
736
|
+
|
|
737
|
+
Enthaltung ohne Overlay: Eine Installation ohne .koolie/project-overlay/OVERLAY.md ist ein
|
|
738
|
+
zulaessiger Zustand (Kandidatenphase), und eine Pruefung, die ihn beanstandet, waere
|
|
739
|
+
eine Pruefung ueber die Reihenfolge der Uebernahme. Fehlt dagegen ein PLATZHALTER in
|
|
740
|
+
einem vorhandenen Overlay, ist das der verlorene Anker und ein Fehler (D-23).
|
|
741
|
+
"""
|
|
742
|
+
if formatgebunden(man, 42):
|
|
743
|
+
return # D-346, D-416: diese Ausgabeform erreicht die Pruefung nicht
|
|
744
|
+
overlay_pfad = os.path.join(root, ".koolie/project-overlay", "OVERLAY.md")
|
|
745
|
+
rel = man["permissions_file"]
|
|
746
|
+
pfad = os.path.join(root, *rel.split("/"))
|
|
747
|
+
if not os.path.exists(overlay_pfad) or not os.path.exists(pfad):
|
|
748
|
+
return
|
|
749
|
+
try:
|
|
750
|
+
cfg = json.loads(read(pfad))
|
|
751
|
+
except json.JSONDecodeError:
|
|
752
|
+
return # check_config hat das bereits gemeldet
|
|
753
|
+
soll = soll_korbregeln(root, man)
|
|
754
|
+
if soll is None:
|
|
755
|
+
return
|
|
756
|
+
overlay = read(overlay_pfad)
|
|
757
|
+
perms = cfg.get("permissions", {})
|
|
758
|
+
exec_werkzeuge = tuple(man.get("permission_tools", {}).get("exec", ()))
|
|
759
|
+
if not exec_werkzeuge:
|
|
760
|
+
return # Ein Pack ohne Befehlswerkzeug hat keine Befehlsschlitze.
|
|
761
|
+
|
|
762
|
+
for korb in ("ask", "allow"):
|
|
763
|
+
ist = [r for r in perms.get(korb, []) if isinstance(r, str)]
|
|
764
|
+
_, schlitze, _, zusatz = korb_zerlegung(ist, soll[korb])
|
|
765
|
+
befehlsschlitze = [s for s in schlitze if s.split("(", 1)[0] in exec_werkzeuge]
|
|
766
|
+
if not befehlsschlitze:
|
|
767
|
+
continue
|
|
768
|
+
erklaert: dict = {}
|
|
769
|
+
ohne_befehl: list = []
|
|
770
|
+
for schlitz in befehlsschlitze:
|
|
771
|
+
namen = PROJEKTPLATZHALTER.findall(schlitz)
|
|
772
|
+
if len(namen) != 1:
|
|
773
|
+
continue
|
|
774
|
+
platzhalter = namen[0]
|
|
775
|
+
anzahl, wert = overlay_befehlswert(overlay, platzhalter)
|
|
776
|
+
if anzahl == 0:
|
|
777
|
+
err(f"{OVERLAY_QUELLE}: keine Tabellenzeile nennt {platzhalter} – "
|
|
778
|
+
f"Prüfung 42 liest dort den Befehl, den dieser Schlitz der "
|
|
779
|
+
f"Berechtigungsdatei tragen darf, und hat ihren Gegenstand verloren. "
|
|
780
|
+
f"Der Platzhalter gehört in die Platzhalterspalte von Abschnitt 5 "
|
|
781
|
+
f"oder 6, der Befehl in die Zelle rechts daneben (D-91)")
|
|
782
|
+
continue
|
|
783
|
+
if anzahl > 1:
|
|
784
|
+
err(f"{OVERLAY_QUELLE}: {anzahl} Tabellenzeilen nennen {platzhalter}. "
|
|
785
|
+
f"Prüfung 42 braucht genau eine – bei mehreren ist nicht "
|
|
786
|
+
f"entschieden, welcher Befehl für den Schlitz gilt (D-91)")
|
|
787
|
+
continue
|
|
788
|
+
if wert is None:
|
|
789
|
+
ohne_befehl.append(platzhalter)
|
|
790
|
+
continue # Kein erklaerter Befehl: Dieser Schlitz deckt nichts.
|
|
791
|
+
regel = schlitz.replace(platzhalter, wert)
|
|
792
|
+
erklaert[regel] = platzhalter
|
|
793
|
+
if regel in ist:
|
|
794
|
+
continue
|
|
795
|
+
if schlitz in ist:
|
|
796
|
+
err(f"{rel}: das Overlay erklärt für {platzhalter} den Befehl "
|
|
797
|
+
f"'{wert}'; der {korb}-Korb trägt aber noch den offenen Schlitz "
|
|
798
|
+
f"'{schlitz}'. Entweder wird der Wert dort eingetragen ('{regel}'), "
|
|
799
|
+
f"oder Abschnitt 5 beziehungsweise 6 nimmt ihn zurück – zwei "
|
|
800
|
+
f"Träger derselben Freigabe dürfen nicht auseinanderlaufen "
|
|
801
|
+
f"(D-90)")
|
|
802
|
+
else:
|
|
803
|
+
err(f"{rel}: das Overlay erklärt für {platzhalter} den Befehl "
|
|
804
|
+
f"'{wert}'; der {korb}-Korb trägt weder '{regel}' noch den offenen "
|
|
805
|
+
f"Schlitz '{schlitz}'. Eine erklärte Freigabe, die die Datei nicht "
|
|
806
|
+
f"gewährt, ist eine Abweichung – gleich in welche Richtung sie "
|
|
807
|
+
f"aufgelöst wird (D-90)")
|
|
808
|
+
for regel in zusatz:
|
|
809
|
+
if regel.split("(", 1)[0] not in exec_werkzeuge:
|
|
810
|
+
continue
|
|
811
|
+
if regel in erklaert:
|
|
812
|
+
continue
|
|
813
|
+
err(f"{rel}: '{regel}' steht im {korb}-Korb, und kein Platzhalter des "
|
|
814
|
+
f"Overlays erklärt diesen Befehl. Ein Platzhalterschlitz darf gefüllt "
|
|
815
|
+
f"sein – aber mit dem Wert, den Abschnitt 5 oder 6 für ihn nennt. Ohne "
|
|
816
|
+
f"erklärten Befehl deckt ein Schlitz keine zusätzliche Freigabe; ohne "
|
|
817
|
+
f"diesen Satz decken drei offene Schlitze drei beliebige Befehle, gemessen "
|
|
818
|
+
f"bis hin zu einem mit Fernwirkung (D-90). Ohne erklärten Befehl sind "
|
|
819
|
+
f"hier: {', '.join(ohne_befehl) if ohne_befehl else 'keiner'}")
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
# --- Pruefung 54 -------------------------------------------------------------------
|
|
823
|
+
# Das Framework liefert die Berechtigungsdatei eines Packs erzeugt aus, und ein Pack darf
|
|
824
|
+
# darin Schluessel setzen, die nicht aus der neutralen Regelmenge stammen. Bis 0.61.0 gab
|
|
825
|
+
# es dafuer genau ein Feld: permissions_extra - und es landet INNERHALB des
|
|
826
|
+
# permissions-Objekts. Fuer einen Schluessel, den der Client auf der obersten Ebene liest,
|
|
827
|
+
# war das die falsche Stelle, und es gab keine richtige.
|
|
828
|
+
#
|
|
829
|
+
# DIE EBENE IST KEINE KOSMETIK. Ein Schluessel an der falschen Stelle wird nicht gelesen,
|
|
830
|
+
# die Datei bleibt gueltiges JSON, der Validator meldete nichts - und die Verschaerfung,
|
|
831
|
+
# die das Pack auszuliefern glaubt, wirkt nicht. Das ist die Bauform "die Zusage, die mehr
|
|
832
|
+
# verspricht als ihr Mechanismus haelt", angewandt auf eine Dateiebene.
|
|
833
|
+
#
|
|
834
|
+
# ANLASS IST EINE FREMDE MESSUNG, KEINE EIGENE (FW-AK-01, CR-2026-087, 2026-09-18): Beim
|
|
835
|
+
# Client des Schwesterpacks ist genau das als CVE-2026-81376 aufgetreten - der Restricted
|
|
836
|
+
# Mode setzte eine eingeschraenkte Arbeitsbereichseinstellung in GEPUNKTETER Schreibweise
|
|
837
|
+
# durch und dieselbe Einstellung in VERSCHACHTELTER Form nicht. Behoben in 3.10.31 vom
|
|
838
|
+
# 2026-09-16; die verbindliche Zielspanne des Packs devin-desktop (3.9.x) liegt
|
|
839
|
+
# vollstaendig davor. Dieselbe Regel, zwei Ausdrucksformen, durchgesetzt nur in einer -
|
|
840
|
+
# die Bauform von Pruefung 51, diesmal im Produkt statt im eigenen Bestand.
|
|
841
|
+
#
|
|
842
|
+
# WAS SIE NICHT LEISTET: Sie prueft, ob ein DEKLARIERTER Schluessel ankommt, nicht ob er
|
|
843
|
+
# der richtige ist. Ob autoMemoryEnabled der Schluessel ist, den dieser Client liest, sagt
|
|
844
|
+
# die Herstellerdokumentation - und dafuer gibt es FW-AK-01, nicht den Validator.
|
|
845
|
+
P54_FELDER = ("settings_extra", "permissions_extra")
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def check_zusatzschluessel(root: str, man: dict) -> None:
|
|
849
|
+
"""Pruefung 54 (D-155): Deklarierte Zusatzschluessel stehen auf ihrer Ebene."""
|
|
850
|
+
if formatgebunden(man, 54):
|
|
851
|
+
return # D-346, D-416: diese Ausgabeform erreicht die Pruefung nicht
|
|
852
|
+
rel = man.get("permissions_file")
|
|
853
|
+
if not rel:
|
|
854
|
+
return
|
|
855
|
+
pfad = os.path.join(root, *rel.split("/"))
|
|
856
|
+
if not os.path.isfile(pfad):
|
|
857
|
+
return
|
|
858
|
+
try:
|
|
859
|
+
cfg = json.loads(read(pfad))
|
|
860
|
+
except json.JSONDecodeError:
|
|
861
|
+
return # Pruefung 3 meldet das bereits; hier waere es eine zweite Meldung
|
|
862
|
+
if not isinstance(cfg, dict):
|
|
863
|
+
return
|
|
864
|
+
pack = man.get("client", "?")
|
|
865
|
+
for feld in P54_FELDER:
|
|
866
|
+
erklaert = man.get(feld)
|
|
867
|
+
if erklaert is None:
|
|
868
|
+
err(f"clients/{pack}/manifest.json: Feld {feld} fehlt. Ein Pack führt "
|
|
869
|
+
f"beide Felder, notfalls leer - der Unterschied zwischen „nicht "
|
|
870
|
+
f"abgebildet“ und „gibt es nicht“ gehört "
|
|
871
|
+
f"deklariert und nicht aus einem fehlenden Feld erraten (D-155)")
|
|
872
|
+
continue
|
|
873
|
+
if not isinstance(erklaert, dict):
|
|
874
|
+
err(f"clients/{pack}/manifest.json: Feld {feld} ist kein Objekt (D-155)")
|
|
875
|
+
continue
|
|
876
|
+
eigen = cfg if feld == "settings_extra" else cfg.get("permissions", {})
|
|
877
|
+
fremd = cfg.get("permissions", {}) if feld == "settings_extra" else cfg
|
|
878
|
+
ebene = ("der obersten Ebene" if feld == "settings_extra"
|
|
879
|
+
else "dem Objekt permissions")
|
|
880
|
+
gegen = ("dem Objekt permissions" if feld == "settings_extra"
|
|
881
|
+
else "der obersten Ebene")
|
|
882
|
+
if not isinstance(eigen, dict):
|
|
883
|
+
continue
|
|
884
|
+
for schluessel, wert in erklaert.items():
|
|
885
|
+
if schluessel in eigen:
|
|
886
|
+
if eigen[schluessel] != wert:
|
|
887
|
+
err(f"{rel}: Schlüssel „{schluessel}“ trägt "
|
|
888
|
+
f"{json.dumps(eigen[schluessel], ensure_ascii=False)}, das "
|
|
889
|
+
f"Manifest deklariert in {feld} aber "
|
|
890
|
+
f"{json.dumps(wert, ensure_ascii=False)} (D-155)")
|
|
891
|
+
continue
|
|
892
|
+
if isinstance(fremd, dict) and schluessel in fremd:
|
|
893
|
+
err(f"{rel}: Schlüssel „{schluessel}“ steht in {gegen} "
|
|
894
|
+
f"statt in {ebene}. Das Manifest deklariert ihn in {feld}; auf der "
|
|
895
|
+
f"falschen Ebene wird er stillschweigend nicht gelesen (D-155)")
|
|
896
|
+
continue
|
|
897
|
+
err(f"{rel}: Schlüssel „{schluessel}“ aus {feld} fehlt in "
|
|
898
|
+
f"{ebene}. Das Pack liefert eine Verschärfung aus, die in der "
|
|
899
|
+
f"erzeugten Datei nicht ankommt (D-155)")
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
# --- Pruefung 74: Eine Matrixzeile steht in ihrer Tabelle ---------------------------
|
|
903
|
+
#
|
|
904
|
+
# ANLASS, UND ER STAND 73 RELEASES DA (gemessen 2026-09-22, D-264). Das Pack
|
|
905
|
+
# devin-desktop fuehrt sieben Modus-Zeilen. Zwischen M5 und M6 stand eine LEERZEILE -
|
|
906
|
+
# seit 0.26.0, dem Release, das M6 und M7 ueberhaupt erst angelegt hat, weil AP2-DD-03
|
|
907
|
+
# gefunden hatte, dass zwei geregelte Modi keine Matrixzeile haben. Die Abhilfe gab
|
|
908
|
+
# ihnen eine Zeile, die KEINE Tabellenzeile ist: Nach einer Leerzeile beginnt in
|
|
909
|
+
# Markdown ein neuer Block, und ein Block aus "| M6 | ... |" ohne Kopf- und Trennzeile
|
|
910
|
+
# ist ein Absatz. Beide Zeilen erscheinen im Hauptdokument als Fliesstext mit Strichen.
|
|
911
|
+
#
|
|
912
|
+
# UND DIE BUCHFUEHRUNG ZAEHLT SIE MIT: Die Zusammenfassung desselben Packs fuehrt
|
|
913
|
+
# "20 von 36" - 36 ist die Zeilenzahl EINSCHLIESSLICH M6 und M7. Die Zahl stimmte, die
|
|
914
|
+
# Tabelle zeigte sie nicht. Das ist der wiederkehrende Befundtyp mit umgekehrtem
|
|
915
|
+
# Vorzeichen: nicht der Text verspricht mehr als der Mechanismus haelt, sondern die
|
|
916
|
+
# Buchfuehrung sagt etwas ueber eine Zeile, die der Leser gar nicht als Zeile sieht.
|
|
917
|
+
#
|
|
918
|
+
# WARUM EINE EIGENE PRUEFUNG UND NICHT EIN TEIL VON 73. Der Gegenstand ist ein anderer:
|
|
919
|
+
# 73 fragt, WAS in einer Zeile steht, 74 fragt, ob sie ueberhaupt eine ist. Eine
|
|
920
|
+
# gebrochene Tabelle laesst 73 unberuehrt - sie liest die Zeile weiterhin, weil sie sie
|
|
921
|
+
# am Muster erkennt und nicht am Block. Genau deshalb hat 73 Releases lang niemand
|
|
922
|
+
# etwas gemerkt.
|
|
923
|
+
P74_TRENNZEILE_RE = re.compile(r"^\|\s*:?-{3,}")
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
def check_matrixzeile_in_tabelle(root: str) -> None:
|
|
927
|
+
"""Pruefung 74 (D-264): Jede Matrixzeile steht in ihrer Tabelle."""
|
|
928
|
+
if P74_TRENNZEILE_RE.match("| M6 | synthetisch |"):
|
|
929
|
+
err("Pruefung 74: das eigene Muster haelt eine Datenzeile fuer eine "
|
|
930
|
+
"Trennzeile - sie meldete nichts mehr und bestuende leise (D-23)")
|
|
931
|
+
return
|
|
932
|
+
verz = os.path.join(root, KERN, "clients")
|
|
933
|
+
if not os.path.isdir(verz):
|
|
934
|
+
return
|
|
935
|
+
packs = sorted(n for n in os.listdir(verz)
|
|
936
|
+
if os.path.isfile(os.path.join(verz, n, "CLIENT_PACK.md")))
|
|
937
|
+
if not packs:
|
|
938
|
+
err(f"{KERN}/clients/: kein Client Pack mit CLIENT_PACK.md - Pruefung 74 "
|
|
939
|
+
f"haette keinen Gegenstand (D-23)")
|
|
940
|
+
return
|
|
941
|
+
for pack in packs:
|
|
942
|
+
rel = f"clients/{pack}/CLIENT_PACK.md"
|
|
943
|
+
zeilen = read(os.path.join(verz, pack, "CLIENT_PACK.md")) \
|
|
944
|
+
.replace("\r\n", "\n").split("\n")
|
|
945
|
+
drin = False
|
|
946
|
+
for i, zeile in enumerate(zeilen):
|
|
947
|
+
if zeile.startswith("## 2. "):
|
|
948
|
+
drin = True
|
|
949
|
+
continue
|
|
950
|
+
if drin and re.match(r"^## \d", zeile):
|
|
951
|
+
drin = False
|
|
952
|
+
if not drin or not P73_ZEILE_RE.match(zeile):
|
|
953
|
+
continue
|
|
954
|
+
# Rueckwaerts bis zur Trennzeile - ohne Leerzeile und ohne Fremdzeile.
|
|
955
|
+
j, getragen = i - 1, False
|
|
956
|
+
while j >= 0:
|
|
957
|
+
vor = zeilen[j].strip()
|
|
958
|
+
if not vor or not vor.startswith("|"):
|
|
959
|
+
break
|
|
960
|
+
if P74_TRENNZEILE_RE.match(vor):
|
|
961
|
+
getragen = True
|
|
962
|
+
break
|
|
963
|
+
j -= 1
|
|
964
|
+
if not getragen:
|
|
965
|
+
kennung = P73_ZEILE_RE.match(zeile).group(1)
|
|
966
|
+
err(f"{rel}:{i + 1}: Zeile {kennung} sieht aus wie eine Matrixzeile "
|
|
967
|
+
f"und steht in keiner Tabelle - zwischen ihr und der Trennzeile "
|
|
968
|
+
f"liegt eine Leerzeile oder Fremdtext. Markdown rendert sie als "
|
|
969
|
+
f"Absatz mit Strichen, im Pack wie im Hauptdokument, waehrend die "
|
|
970
|
+
f"Zusammenfassung desselben Packs sie mitzaehlt (D-264)")
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
# Pruefung 84: die Vorlage ist keine Client Pack (CR-2026-131, D-336)
|
|
974
|
+
# ---------------------------------------------------------------------------
|
|
975
|
+
#
|
|
976
|
+
# ANLASS, GEMESSEN MIT EINEM PROBEMANIFEST UND WIEDER ENTFERNT. `clients/README.md`
|
|
977
|
+
# Abschnitt 5 Schritt 1 sagt "`_template/` nach `<client-name>/` kopieren", Schritt 5
|
|
978
|
+
# sagt "`manifest.json` anlegen", und Abschnitt 3 sagt "Ohne Manifest ist ein Pack nicht
|
|
979
|
+
# installierbar". Die Vorlage traegt EINES VON DREI Bestandteilen.
|
|
980
|
+
#
|
|
981
|
+
# 🔴 UND SIE STEHT IN DER PACKMENGE DES PRUEFAPPARATS. `_client_packs()` nimmt jedes
|
|
982
|
+
# Verzeichnis unter `clients/` auf, das eine CLIENT_PACK.md traegt - `_template` erfuellt
|
|
983
|
+
# das. Der Docstring der Funktion hielt die Annahme fest, die sie trug: "_template ohne
|
|
984
|
+
# Manifest".
|
|
985
|
+
#
|
|
986
|
+
# GEMESSEN am 2026-09-23: Ein Probemanifest angelegt (eine Kopie des Manifests von
|
|
987
|
+
# `claude-code`, also eines, das einen FREMDEN Client beschreibt), Validator gefahren,
|
|
988
|
+
# Probemanifest entfernt. `_client_packs()` lieferte drei Packs MIT Manifest, und der
|
|
989
|
+
# Validator meldete 0 Fehler, 0 Warnungen.
|
|
990
|
+
# ➡️ EINE VORLAGE, DIE NUR DESHALB KEINE PRUEFUNG AUSLOEST, WEIL IHR EIN BESTANDTEIL
|
|
991
|
+
# FEHLT, IST NICHT AUSGENOMMEN - SIE IST UNVOLLSTAENDIG. Und der Tag, an dem jemand sie
|
|
992
|
+
# nach der eigenen Anleitung vervollstaendigt, ist der Tag, an dem sie geprueft wird,
|
|
993
|
+
# ohne dass es jemand entschieden hat.
|
|
994
|
+
#
|
|
995
|
+
# 🔴 DIE BAUFORM "ZWEI STELLEN, DIE EINANDER DECKEN" (0.57.0): Die unvollstaendige
|
|
996
|
+
# Vorlage verhindert, dass die fehlende Ausnahme je auffaellt. Einzeln waere jede
|
|
997
|
+
# aufgefallen; zusammen sahen sie aus wie ein Lauf ohne Befund.
|
|
998
|
+
#
|
|
999
|
+
# ⚠️ UND DIE AUSNAHME EXISTIERTE BEREITS - AN ZWEI ANDEREN STELLEN. Pruefung 73 schliesst
|
|
1000
|
+
# `_template` ausdruecklich aus, und LINK_PATH_EXCEPTIONS fuehrt den Pfad mit Begruendung.
|
|
1001
|
+
# Der zentrale Iterator tat es nicht.
|
|
1002
|
+
# ➡️ WER EINE AUSNAHME AN ZWEI STELLEN FUEHRT UND AN DER DRITTEN VERGISST, HAT SIE NICHT
|
|
1003
|
+
# VERGESSEN - ER HAT KEINE STELLE, AN DER SIE STEHT. Seit D-336 steht sie in
|
|
1004
|
+
# `_client_packs()`, also dort, wo die Packmenge ENTSTEHT.
|
|
1005
|
+
#
|
|
1006
|
+
# ZUSCHNITT: Diese Pruefung haelt fest, was die Ausnahme voraussetzt - dass die Vorlage
|
|
1007
|
+
# eine Vorlage BLEIBT. Traegt sie die Bestandteile eines vollstaendigen Packs, ist
|
|
1008
|
+
# entweder die Ausnahme falsch oder die Vorlage ein Pack; beides gehoert entschieden und
|
|
1009
|
+
# nicht stillschweigend gefahren.
|
|
1010
|
+
#
|
|
1011
|
+
# 🔴 GRENZE, BENANNT: Sie prueft die ANWESENHEIT von Bestandteilen, nicht deren Inhalt.
|
|
1012
|
+
# Eine `CLIENT_PACK.md`, die einen echten Client beschreibt statt Platzhalter zu fuehren,
|
|
1013
|
+
# kommt durch - das faengt Pruefung 7 ueber die Markerform.
|
|
1014
|
+
P84_VORLAGE = "_template"
|
|
1015
|
+
P84_VERBOTEN = ("manifest.json", "root-template")
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
def check_vorlage_kein_pack(root: str) -> None:
|
|
1019
|
+
"""Pruefung 84 (D-336): Die Client-Pack-Vorlage traegt keine Packbestandteile."""
|
|
1020
|
+
vdir = os.path.join(root, KERN, "clients", P84_VORLAGE)
|
|
1021
|
+
if not os.path.isdir(vdir):
|
|
1022
|
+
err(f"{KERN}/clients/{P84_VORLAGE}/: fehlt. `clients/README.md` Abschnitt 5 "
|
|
1023
|
+
f"Schritt 1 baut jedes neue Client Pack aus ihr, und `_client_packs()` "
|
|
1024
|
+
f"nimmt sie seit D-336 ausdruecklich NICHT auf - ohne den Gegenstand "
|
|
1025
|
+
f"bestuende Pruefung 84 leise (D-23, D-336)")
|
|
1026
|
+
return
|
|
1027
|
+
if not os.path.isfile(os.path.join(vdir, "CLIENT_PACK.md")):
|
|
1028
|
+
err(f"{KERN}/clients/{P84_VORLAGE}/CLIENT_PACK.md: fehlt. Sie ist der einzige "
|
|
1029
|
+
f"Bestandteil, den die Vorlage traegt (D-336)")
|
|
1030
|
+
for name in P84_VERBOTEN:
|
|
1031
|
+
pfad = os.path.join(vdir, name)
|
|
1032
|
+
if not os.path.exists(pfad):
|
|
1033
|
+
continue
|
|
1034
|
+
err(f"{KERN}/clients/{P84_VORLAGE}/{name}: die Vorlage traegt einen Bestandteil, "
|
|
1035
|
+
f"den nur ein vollstaendiges Client Pack traegt. `_client_packs()` nimmt "
|
|
1036
|
+
f"`{P84_VORLAGE}` seit D-336 nicht auf - damit liefe dieser Bestandteil "
|
|
1037
|
+
f"ungeprueft mit, waehrend er in jedem echten Pack geprueft wird. Gemessen "
|
|
1038
|
+
f"am 2026-09-23 mit einem Probemanifest: alle Pruefungen blieben gruen. "
|
|
1039
|
+
f"Entweder ist die Ausnahme falsch oder die Vorlage ein Pack - beides "
|
|
1040
|
+
f"gehoert entschieden (D-336)")
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
# ---------------------------------------------------------------------------
|
|
1044
|
+
# PRUEFUNG 87: DIE FORMATGEBUNDENEN PRUEFUNGEN STEHEN IM PACK
|
|
1045
|
+
# ---------------------------------------------------------------------------
|
|
1046
|
+
# ANLASS (CR-2026-133, D-346). Mit dem dritten Client Pack gibt es zum ersten Mal zwei
|
|
1047
|
+
# Ausgabeformen der Berechtigungsdatei. Zwoelf Pruefungen lesen die eine; fuer die
|
|
1048
|
+
# andere haben sie keinen Gegenstand. Sie still zu ueberspringen, waere die Bauform von
|
|
1049
|
+
# 0.57.0 - "zwei Stellen, die einander decken": Der Validator liefe gruen, und niemand
|
|
1050
|
+
# wuesste, dass sechs Pruefungen dieses Pack nicht erreichen.
|
|
1051
|
+
# ➡️ Eine Luecke, die erklaert ist, ist eine Aussage; eine, die nur besteht, ist ein
|
|
1052
|
+
# blinder Fleck.
|
|
1053
|
+
# Diese Pruefung verlangt deshalb: Ein Pack mit einer anderen Ausgabeform nennt in
|
|
1054
|
+
# Abschnitt 5 seines CLIENT_PACK.md JEDE Nummer aus FORMATGEBUNDENE_PRUEFUNGEN - und
|
|
1055
|
+
# keine Nummer mehr, die dort nicht steht.
|
|
1056
|
+
# ⚠️ GRENZE, BENANNT: Sie prueft die NENNUNG, nicht die Richtigkeit der Begruendung -
|
|
1057
|
+
# dieselbe Bauform wie Pruefung 19. Und sie haengt daran, dass jemand eine neu
|
|
1058
|
+
# guardete Pruefung in die Menge eintraegt; die Funktion `formatgebunden` erzwingt
|
|
1059
|
+
# das, indem sie eine unbekannte Nummer als Fehler wirft.
|
|
1060
|
+
def check_formatgebundene_pruefungen(root: str) -> None:
|
|
1061
|
+
"""Pruefung 87 (D-346): Ein Pack mit eigener Ausgabeform nennt die Pruefungen,
|
|
1062
|
+
die es damit nicht erreichen."""
|
|
1063
|
+
packs = os.path.join(root, KERN, "clients")
|
|
1064
|
+
if not os.path.isdir(packs):
|
|
1065
|
+
return
|
|
1066
|
+
for name in sorted(os.listdir(packs)):
|
|
1067
|
+
mf = os.path.join(packs, name, "manifest.json")
|
|
1068
|
+
if not os.path.exists(mf):
|
|
1069
|
+
continue
|
|
1070
|
+
try:
|
|
1071
|
+
man = json.loads(read(mf))
|
|
1072
|
+
except (OSError, ValueError):
|
|
1073
|
+
continue
|
|
1074
|
+
if man.get("permissions_format", "json") == "json":
|
|
1075
|
+
continue
|
|
1076
|
+
pack = os.path.join(packs, name, "CLIENT_PACK.md")
|
|
1077
|
+
if not os.path.exists(pack):
|
|
1078
|
+
continue
|
|
1079
|
+
text = read(pack)
|
|
1080
|
+
abschnitt = re.split(r"^## 5\.", text, maxsplit=1, flags=re.M)
|
|
1081
|
+
if len(abschnitt) < 2:
|
|
1082
|
+
err(f"{KERN}/clients/{name}/CLIENT_PACK.md: Abschnitt 5 fehlt; dort gehört "
|
|
1083
|
+
f"die Liste der Prüfungen, die dieses Pack wegen seiner Ausgabeform "
|
|
1084
|
+
f"'{man['permissions_format']}' nicht erreichen (D-346)")
|
|
1085
|
+
continue
|
|
1086
|
+
rumpf = re.split(r"^## 6\.", abschnitt[1], maxsplit=1, flags=re.M)[0]
|
|
1087
|
+
genannt = {int(n) for n in re.findall(r"Prüfung\s+(\d+)", rumpf)}
|
|
1088
|
+
erwartet = nicht_erreicht(man)
|
|
1089
|
+
fehlend = sorted(erwartet - genannt)
|
|
1090
|
+
if fehlend:
|
|
1091
|
+
err(f"{KERN}/clients/{name}/CLIENT_PACK.md Abschnitt 5: die Prüfung(en) "
|
|
1092
|
+
f"{', '.join(str(n) for n in fehlend)} sind an die Ausgabeform 'json' "
|
|
1093
|
+
f"gebunden und erreichen dieses Pack nicht – der Abschnitt nennt sie "
|
|
1094
|
+
f"nicht. Eine Lücke, die nirgends steht, ist ein blinder Fleck (D-346)")
|
|
1095
|
+
# Gegenrichtung, seit 1.13.0 je Form (D-416): Genannt werden darf nur, was
|
|
1096
|
+
# DIESE Form nicht erreicht - auch eine Nummer der Menge, die das Pack doch
|
|
1097
|
+
# erreicht, ist eine behauptete Luecke.
|
|
1098
|
+
zuviel = sorted(genannt - erwartet)
|
|
1099
|
+
if zuviel:
|
|
1100
|
+
err(f"{KERN}/clients/{name}/CLIENT_PACK.md Abschnitt 5: die Prüfung(en) "
|
|
1101
|
+
f"{', '.join(str(n) for n in zuviel)} werden als formatgebunden "
|
|
1102
|
+
f"genannt, erreichen die Ausgabeform '{man['permissions_format']}' "
|
|
1103
|
+
f"aber (FORMATGEBUNDENE_PRUEFUNGEN). Eine behauptete Lücke, die es nicht "
|
|
1104
|
+
f"gibt, ist so falsch wie eine verschwiegene (D-346, D-416)")
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
# ---------------------------------------------------------------------------
|
|
1108
|
+
# Pruefung 105: Die gepruefte Clientversion liegt in der Zielspanne (CR-2026-161, D-482)
|
|
1109
|
+
# ---------------------------------------------------------------------------
|
|
1110
|
+
#
|
|
1111
|
+
# ANLASS (K-40). Die Zielspanne ist eine Festlegung des Framework Owners (D-112, D-113),
|
|
1112
|
+
# und keine Pruefung hielt sie. Beim Pack claude-code stand der Punktwert 2.1.267 ueber
|
|
1113
|
+
# vierzig Releases unveraendert, bis er sechs Patchstaende alt war - der unbezahlte Preis
|
|
1114
|
+
# von D-113.
|
|
1115
|
+
#
|
|
1116
|
+
# WAS SIE PRUEFT, je Client Pack an den Steckbriefzeilen "Verbindliche Zielversion" und
|
|
1117
|
+
# "Gepruefte Clientversion":
|
|
1118
|
+
# (1) Die ERSTE Punktversion der geprueften Zeile liegt in einer Spanne der Zielzeile.
|
|
1119
|
+
# Weitere Punktversionen duerfen Nebenbestandteile sein (kiro nennt den
|
|
1120
|
+
# Agentenserver, cursor die installierte IDE).
|
|
1121
|
+
# (2) Jede Spanne der Zielzeile ist durch eine Punktversion der geprueften Zeile
|
|
1122
|
+
# belegt - eine gehobene Spanne ohne Messung faellt so auf.
|
|
1123
|
+
# Eine Spanne ist ein Wert wie `2.1.x` IN BACKTICKS; "liegt in" heisst: beginnt mit
|
|
1124
|
+
# "2.1.". Die Backticks sind der Unterschied zwischen einer Festlegung und einer
|
|
1125
|
+
# Erwaehnung: devin-desktop schreibt in dieselbe Zelle, die Spanne werde "ohne Messung
|
|
1126
|
+
# gegen 3.10.x nicht gehoben" - der erste Entwurf dieser Pruefung hielt das fuer eine
|
|
1127
|
+
# Zielspanne ohne Messung.
|
|
1128
|
+
#
|
|
1129
|
+
# GRENZE, UND SIE STEHT AUCH IN DER MELDUNG: Das ist eine Aussage ueber die
|
|
1130
|
+
# SCHREIBWEISE. Dass 3.9.19 in 3.9.x liegt, belegt nicht, dass der Mechanismus in der
|
|
1131
|
+
# ganzen Spanne gleich ist. Deshalb eine WARNUNG und kein Fehler (D-482).
|
|
1132
|
+
P105_ZIEL = "Verbindliche Zielversion"
|
|
1133
|
+
P105_GEPRUEFT = "Geprüfte Clientversion"
|
|
1134
|
+
P105_SPANNE_RE = re.compile(r"`(\d+(?:\.\d+)*)\.x`")
|
|
1135
|
+
P105_VERSION_RE = re.compile(r"(?<![\w.])(\d+(?:\.\d+)+)(?:-[0-9a-z]+)?(?![\w.])")
|
|
1136
|
+
P105_GRENZE = ("Ein Praefixvergleich der Schreibweise, kein Beleg, dass der Mechanismus "
|
|
1137
|
+
"in der ganzen Spanne gleich ist (K-40, D-482)")
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
def _p105_zelle(text: str, feld: str) -> str | None:
|
|
1141
|
+
"""Die Wertzelle der Steckbriefzeile `feld` - oder None."""
|
|
1142
|
+
for zeile in text.splitlines():
|
|
1143
|
+
if not zeile.lstrip().startswith("|"):
|
|
1144
|
+
continue
|
|
1145
|
+
zellen = tabellenzellen(zeile)
|
|
1146
|
+
if len(zellen) >= 2 and zellen[0] == feld:
|
|
1147
|
+
return zellen[1]
|
|
1148
|
+
return None
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def check_clientversion_in_spanne(root: str) -> None:
|
|
1152
|
+
"""Pruefung 105 (D-482): Die gepruefte Clientversion liegt in der Zielspanne."""
|
|
1153
|
+
basis = os.path.join(root, KERN, "clients")
|
|
1154
|
+
if not os.path.isdir(basis):
|
|
1155
|
+
return
|
|
1156
|
+
for pack in sorted(os.listdir(basis)):
|
|
1157
|
+
pfad = os.path.join(basis, pack, "CLIENT_PACK.md")
|
|
1158
|
+
if pack.startswith("_") or not os.path.isfile(pfad):
|
|
1159
|
+
continue
|
|
1160
|
+
rel = os.path.relpath(pfad, root).replace(os.sep, "/")
|
|
1161
|
+
text = read(pfad)
|
|
1162
|
+
ziel, geprueft = _p105_zelle(text, P105_ZIEL), _p105_zelle(text, P105_GEPRUEFT)
|
|
1163
|
+
if ziel is None or geprueft is None:
|
|
1164
|
+
warn(f"{rel}: Pruefung 105 findet die Steckbriefzeile "
|
|
1165
|
+
f"'{P105_ZIEL if ziel is None else P105_GEPRUEFT}' nicht und haelt die "
|
|
1166
|
+
f"Zielspanne dieses Packs nicht (K-40, D-482)")
|
|
1167
|
+
continue
|
|
1168
|
+
spannen = P105_SPANNE_RE.findall(ziel)
|
|
1169
|
+
versionen = [m.group(1) for m in P105_VERSION_RE.finditer(geprueft)]
|
|
1170
|
+
if not spannen or not versionen:
|
|
1171
|
+
warn(f"{rel}: Pruefung 105 findet in der Zeile "
|
|
1172
|
+
f"'{P105_ZIEL if not spannen else P105_GEPRUEFT}' keine "
|
|
1173
|
+
f"{'Spanne der Form N.N.x' if not spannen else 'Punktversion'} und haelt "
|
|
1174
|
+
f"die Zielspanne dieses Packs nicht (K-40, D-482)")
|
|
1175
|
+
continue
|
|
1176
|
+
if not any(versionen[0].startswith(s + ".") for s in spannen):
|
|
1177
|
+
warn(f"{rel}: die gepruefte Clientversion {versionen[0]} liegt in keiner "
|
|
1178
|
+
f"Zielspanne ({', '.join(s + '.x' for s in spannen)}). {P105_GRENZE}")
|
|
1179
|
+
for s in spannen:
|
|
1180
|
+
if not any(v.startswith(s + ".") for v in versionen):
|
|
1181
|
+
warn(f"{rel}: die Zielspanne {s}.x ist durch keine gepruefte Version "
|
|
1182
|
+
f"belegt - gehoben, ohne gemessen zu sein? {P105_GRENZE}")
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
# --- Pruefung 108: ein allow-Muster, das ein deny-Praefix umschliesst (CR-2026-163, D-494, K-47)
|
|
1186
|
+
#
|
|
1187
|
+
# ANLASS, GEMESSEN (D-123, 2026-09-17): Bei allow = Bash(git:*) und deny = Bash(git push:*)
|
|
1188
|
+
# wies der Client 'git push' ab und liess 'git -C <pfad> push' durch - der Commit stand am
|
|
1189
|
+
# Remote. Die ausgelieferte Fassung haelt, weil der allow-Korb nur fuenf lesende git-Befehle
|
|
1190
|
+
# fuehrt. Wer ihn verbreitert, verliert den Schutz ohne Meldung; keine Pruefung sah es.
|
|
1191
|
+
#
|
|
1192
|
+
# DIE BILLIGE ZWISCHENSTUFE, NICHT DIE SACHE: Gemeldet wird, wenn der Befehl eines
|
|
1193
|
+
# allow-Eintrags ein echtes Wortpraefix des Befehls eines deny-Eintrags desselben Werkzeugs
|
|
1194
|
+
# ist. Befehlsaequivalenz ('git -C x push' = 'git push') erkennt keine Zeichenkettenpruefung.
|
|
1195
|
+
# WARNUNG, kein Fehler: Ein Projekt darf den Korb bewusst weiten; es soll es wissen.
|
|
1196
|
+
#
|
|
1197
|
+
# GRENZEN, BENANNT: nur Befehlsregeln der Packs mit Praefixabgleich (permission_exec_match
|
|
1198
|
+
# 'prefix') und JSON-Korb - gemessen ist allein Bash(...). Read- und Edit-Pfadmuster sind
|
|
1199
|
+
# nicht gemessen und bleiben aussen vor. Ein leeres allow-Muster ('*') meldet die
|
|
1200
|
+
# Pruefung ebenfalls: Es umschliesst jedes deny-Praefix.
|
|
1201
|
+
P108_KERNQUELLE = "framework/runtime/permissions.json"
|
|
1202
|
+
P108_GRENZE = ("Die Prüfung vergleicht die Schreibweise, nicht die Befehlsäquivalenz "
|
|
1203
|
+
"(K-47, D-123)")
|
|
1204
|
+
P108_REGEL_RE = re.compile(r"^([A-Za-z_][\w-]*)\((.*)\)$")
|
|
1205
|
+
|
|
1206
|
+
|
|
1207
|
+
def _p108_befehl(muster: str, suffix: str) -> str:
|
|
1208
|
+
"""Der Befehl eines Praefixmusters ohne Suffix und Stern ('git:*' -> 'git')."""
|
|
1209
|
+
m = muster.strip()
|
|
1210
|
+
for ende in (suffix, ":*", " *", "*"):
|
|
1211
|
+
if ende and m.endswith(ende):
|
|
1212
|
+
m = m[:-len(ende)]
|
|
1213
|
+
break
|
|
1214
|
+
return m.strip()
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
def _p108_umschliesst(erlaubt: str, verboten: str) -> bool:
|
|
1218
|
+
return erlaubt != verboten and (not erlaubt or verboten.startswith(erlaubt + " "))
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def _p108_paare(allow: list, deny: list) -> list:
|
|
1222
|
+
"""(allow, deny) je Paar, dessen allow-Befehl das deny-Praefix umschliesst."""
|
|
1223
|
+
return [(a, d) for a in allow for d in deny if _p108_umschliesst(a, d)]
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
def check_allow_umschliesst_deny(root: str, man: dict) -> None:
|
|
1227
|
+
"""Pruefung 108 (D-494, K-47): kein allow-Befehl umschliesst ein deny-Praefix."""
|
|
1228
|
+
kq = os.path.join(root, KERN, *P108_KERNQUELLE.split("/"))
|
|
1229
|
+
if os.path.isfile(kq):
|
|
1230
|
+
try:
|
|
1231
|
+
quelle = json.loads(read(kq))
|
|
1232
|
+
except json.JSONDecodeError:
|
|
1233
|
+
quelle = {}
|
|
1234
|
+
|
|
1235
|
+
def befehle(korb: str) -> list:
|
|
1236
|
+
return [(r.get("prefix") or r.get("command") or "").strip()
|
|
1237
|
+
for r in quelle.get(korb, []) if isinstance(r, dict)
|
|
1238
|
+
and r.get("tool") == "exec" and r.get("command")
|
|
1239
|
+
and not PROJEKTPLATZHALTER.search(r.get("command"))]
|
|
1240
|
+
for a, d in _p108_paare(befehle("allow"), befehle("deny")):
|
|
1241
|
+
warn(f"{KERN}/{P108_KERNQUELLE}: der allow-Befehl '{a}' umschließt das "
|
|
1242
|
+
f"deny-Präfix '{d}' - bei Praefixabgleich läuft '{d}' in einer anderen "
|
|
1243
|
+
f"Schreibweise durch. {P108_GRENZE}")
|
|
1244
|
+
if man.get("permission_exec_match") != "prefix":
|
|
1245
|
+
return
|
|
1246
|
+
rel = man.get("permissions_file") or ""
|
|
1247
|
+
pfad = os.path.join(root, *rel.split("/")) if rel else ""
|
|
1248
|
+
if not rel.endswith(".json") or not os.path.isfile(pfad):
|
|
1249
|
+
return
|
|
1250
|
+
try:
|
|
1251
|
+
cfg = json.loads(read(pfad))
|
|
1252
|
+
except json.JSONDecodeError:
|
|
1253
|
+
return # check_config hat das bereits gemeldet
|
|
1254
|
+
koerbe = cfg.get("permissions") if isinstance(cfg.get("permissions"), dict) else cfg
|
|
1255
|
+
werkzeuge = set((man.get("permission_tools") or {}).get("exec") or ())
|
|
1256
|
+
suffix = man.get("permission_exec_suffix") or ""
|
|
1257
|
+
|
|
1258
|
+
def regeln(korb: str) -> dict:
|
|
1259
|
+
je: dict = {}
|
|
1260
|
+
for r in koerbe.get(korb, []) if isinstance(koerbe.get(korb), list) else []:
|
|
1261
|
+
m = P108_REGEL_RE.match(r.strip()) if isinstance(r, str) else None
|
|
1262
|
+
if m and m.group(1) in werkzeuge and not PROJEKTPLATZHALTER.search(m.group(2)):
|
|
1263
|
+
je.setdefault(m.group(1), []).append(_p108_befehl(m.group(2), suffix))
|
|
1264
|
+
return je
|
|
1265
|
+
erlaubt, verboten = regeln("allow"), regeln("deny")
|
|
1266
|
+
for werkzeug in sorted(werkzeuge):
|
|
1267
|
+
for a, d in _p108_paare(erlaubt.get(werkzeug, []), verboten.get(werkzeug, [])):
|
|
1268
|
+
warn(f"{rel}: {werkzeug}({a}{suffix}) im allow-Korb umschließt "
|
|
1269
|
+
f"{werkzeug}({d}{suffix}) im deny-Korb - gemessen lief 'git -C <pfad> push' "
|
|
1270
|
+
f"an Bash(git push:*) vorbei, als Bash(git:*) erlaubt war. {P108_GRENZE}")
|
|
1271
|
+
|
|
1272
|
+
# --- Pruefung 109: die beiden Werkzeugfelder eines Skills (CR-2026-164, D-505, K-93)
|
|
1273
|
+
#
|
|
1274
|
+
# ANLASS, GEMESSEN (D-287, 2026-09-22 an devin-desktop): Die Werkzeugbeschraenkung eines
|
|
1275
|
+
# Skills wirkt dort nur, wenn das Frontmatter BEIDE Felder traegt - 'allowed-tools' ohne das
|
|
1276
|
+
# Werkzeug UND einen 'permissions'-Block. Jedes Feld allein bleibt folgenlos, und der Client
|
|
1277
|
+
# meldet nichts. Alle ausgelieferten Skills tragen beide; der Validator verlangte nur
|
|
1278
|
+
# 'allowed-tools'. Ein neuer Skill mit nur einem Feld haette eine Beschraenkung behauptet,
|
|
1279
|
+
# die er nicht hat.
|
|
1280
|
+
#
|
|
1281
|
+
# GEPRUEFT: jede Ablage, in der das Feld 'permissions' die installierte Fassung erreicht -
|
|
1282
|
+
# die Quellablagen des Kerns und der Packs und die Skill-Ablage eines Packs, das
|
|
1283
|
+
# 'permissions' nicht verwirft. Bei claude-code wird es auf 'disallowed-tools' abgebildet
|
|
1284
|
+
# (D-64); dort haelt Pruefung 33 die Abbildung.
|
|
1285
|
+
# GRENZE: die Anwesenheit beider Felder, nicht ihre Wirkung - warum der Client beide
|
|
1286
|
+
# braucht, sagt keine Quelle des Herstellers.
|
|
1287
|
+
P109_FELDER = ("allowed-tools", "permissions")
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
def _p109_felder(text: str) -> set:
|
|
1291
|
+
"""Die Werkzeugfelder, die das Frontmatter auf oberster Ebene fuehrt."""
|
|
1292
|
+
m = re.match(r"^---[ \t]*\r?\n(.*?)\r?\n---[ \t]*(\r?\n|$)", text, re.S)
|
|
1293
|
+
if not m:
|
|
1294
|
+
return set()
|
|
1295
|
+
return {f for f in P109_FELDER if re.search(r"^" + re.escape(f) + r"[ \t]*:", m.group(1), re.M)}
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
def check_skill_werkzeugfelder(root: str, man: dict) -> None:
|
|
1299
|
+
"""Pruefung 109 (D-505, K-93): Fuehrt ein Skill ein Werkzeugfeld, fuehrt er beide."""
|
|
1300
|
+
runtime = man.get("skills_dir")
|
|
1301
|
+
verworfen = "permissions" in ((man.get("skill_frontmatter") or {}).get("drop_fields") or [])
|
|
1302
|
+
for skills_dir, prefix in skill_dirs(root, man):
|
|
1303
|
+
if prefix == runtime and verworfen:
|
|
1304
|
+
continue
|
|
1305
|
+
for name in sorted(os.listdir(skills_dir)):
|
|
1306
|
+
pfad = os.path.join(skills_dir, name, "SKILL.md")
|
|
1307
|
+
if not os.path.isfile(pfad):
|
|
1308
|
+
continue
|
|
1309
|
+
da = _p109_felder(read(pfad))
|
|
1310
|
+
if da and da != set(P109_FELDER):
|
|
1311
|
+
fehlt = [f for f in P109_FELDER if f not in da][0]
|
|
1312
|
+
err(f"{prefix}/{name}/SKILL.md: führt {sorted(da)[0]}, aber nicht {fehlt}. "
|
|
1313
|
+
f"Die Werkzeugbeschränkung eines Skills wirkt nur mit beiden Feldern; "
|
|
1314
|
+
f"eines allein bleibt ohne Meldung folgenlos (D-287, K-93)")
|