@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,1358 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
clientmap.py - Semantikabbildung der Berechtigungs- und Hook-Konfiguration.
|
|
4
|
+
|
|
5
|
+
Alle uebrigen Laufzeitartefakte unterscheiden sich zwischen zwei Client Packs nur in
|
|
6
|
+
der Form: ein Frontmatter-Feld heisst anders, eine Werkzeugliste ist kommagetrennt
|
|
7
|
+
statt eingerueckt. Berechtigungen und Hooks sind der Fall darueber hinaus. Hier
|
|
8
|
+
unterscheiden sich die Werkzeugnamen selbst, ein Client trennt Aendern und Anlegen in
|
|
9
|
+
zwei Werkzeuge, und Befehlsverbote greifen bei dem einen woertlich, bei dem anderen
|
|
10
|
+
ueber ein Praefix. Eine reine Formtransformation wuerde das nicht tragen.
|
|
11
|
+
|
|
12
|
+
Die Regelmenge ist Framework-Gut (Ebene 3) und liegt deshalb einmal unter
|
|
13
|
+
framework/runtime/permissions.json und framework/runtime/hooks.json. Wie sie beim
|
|
14
|
+
jeweiligen Client aussieht, steht in clients/<pack>/manifest.json.
|
|
15
|
+
|
|
16
|
+
Weil an dieser Abbildung die Kernzusagen B1 bis B6 haengen, erzwingt dieses Modul drei
|
|
17
|
+
Zusicherungen, statt sie nur zu behaupten:
|
|
18
|
+
|
|
19
|
+
1. Eine deny- oder ask-Regel, fuer die ein Client kein Werkzeug kennt, ist ein
|
|
20
|
+
Fehler. Sie stillschweigend wegzulassen waere eine Lockerung. Bei allow ist das
|
|
21
|
+
Weglassen zulaessig - es faellt dann auf den strengeren Standard zurueck.
|
|
22
|
+
2. Die Praefixform eines Befehlsverbots muss ein Praefix seiner woertlichen Form
|
|
23
|
+
sein. Damit ist die Praefixform nachweislich mindestens so breit; die Abweichung
|
|
24
|
+
ist eine Verschaerfung und keine Luecke.
|
|
25
|
+
3. Bei allow ist jede Verbreiterung unzulaessig: dort muessen beide Formen
|
|
26
|
+
uebereinstimmen.
|
|
27
|
+
4. Ein Werkzeugverb des Frontmatters, das ein Pack weder abbildet noch ausdruecklich
|
|
28
|
+
als nicht abgebildet deklariert, ist ein Fehler. Bis 0.39.0 wurde es woertlich
|
|
29
|
+
durchgereicht und fiel in permissions.deny lautlos aus (D-78).
|
|
30
|
+
|
|
31
|
+
_core_rules_integrity.deny_must_contain wird aus denselben Quellregeln erzeugt
|
|
32
|
+
(Kennzeichnung "core": true). Der Validator vergleicht die installierte Datei damit -
|
|
33
|
+
eine geloeschte Kernregel faellt dadurch auf, auch wenn das Projekt zugleich die
|
|
34
|
+
Integritaetsliste gekuerzt hat.
|
|
35
|
+
|
|
36
|
+
Seit 0.26.0 traegt die Berechtigungsdatei zusaetzlich die Importsteuerung des Clients
|
|
37
|
+
(D-37): Das Framework importiert keine Regel- und Skillquellen fremder Werkzeugformate.
|
|
38
|
+
Das Manifest fuehrt sie unter "import_control" als Schluessel und Wert; kennt ein Client
|
|
39
|
+
keinen solchen Mechanismus, fehlt das Feld und es bleibt bei der Auskunft im Client Pack.
|
|
40
|
+
Die Masznahme ist ein Standard, keine Schranke - die Benutzerkonfiguration hat Vorrang
|
|
41
|
+
(K-27 gemessen 2026-09-11). Pruefung 22 haelt die Abbildung fest, nicht ihre Wirkung.
|
|
42
|
+
"""
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
import json
|
|
46
|
+
import os
|
|
47
|
+
import subprocess
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AbbildungsFehler(ValueError):
|
|
51
|
+
"""Die Quellregeln lassen sich nicht verlustfrei auf diesen Client abbilden."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ---------------------------------------------------------------------------
|
|
55
|
+
# Platzhalter
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
def resolve_placeholders(text: str, man: dict) -> str:
|
|
59
|
+
"""Loest die Laufzeit-Platzhalter des Kerns in die Pfade dieses Client Packs auf.
|
|
60
|
+
|
|
61
|
+
Projektplatzhalter (<EXCLUDED_PATHS>, <TEST_COMMAND>, ...) bleiben unberuehrt - die
|
|
62
|
+
fuellt der Overlay Owner. Hier werden nur die Platzhalter aufgeloest, deren Wert
|
|
63
|
+
vom Client abhaengt und die deshalb im Manifest stehen.
|
|
64
|
+
"""
|
|
65
|
+
for platzhalter, pfad in man.get("runtime_placeholders", {}).items():
|
|
66
|
+
text = text.replace(platzhalter, pfad)
|
|
67
|
+
return text
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# DIE LAGE DES KERNS UNTER DER PROJEKTWURZEL - ZWEI SEGMENTE SEIT 0.88.0
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# Bis 0.87.0 hiess der Kern `leitwerk-core` und war EIN Verzeichnissegment. Vier
|
|
74
|
+
# Werkzeuge haben <CORE_DIR> deshalb mit os.path.basename() aus dem eigenen Ort
|
|
75
|
+
# gebunden - und genau dort steht in install.py der Satz, das stehe dort, "damit
|
|
76
|
+
# eine spaetere Umbenennung nur eine Stelle beruehrt".
|
|
77
|
+
#
|
|
78
|
+
# Mit `.koolie/core` liefert os.path.basename() den Wert "core". Jede gerendete
|
|
79
|
+
# Regeldatei truege dann einen Pfad, den es nicht gibt - und der Validator saehe
|
|
80
|
+
# es nicht, weil er denselben falschen Wert bindet (D-299).
|
|
81
|
+
#
|
|
82
|
+
# Deshalb steht die Lage hier als ZEICHENKETTE und wird nicht abgeleitet. Wer den
|
|
83
|
+
# Kern woanders hinlegt, bricht ohnehin die Hook-Kommandos und die Pruefungen 45,
|
|
84
|
+
# 59 und 75; ein Wert, der sich still anpasst, verdeckt das nur.
|
|
85
|
+
CORE_REL = ".koolie/core"
|
|
86
|
+
#: Wieviele Verzeichnisebenen zwischen Kern und Projektwurzel liegen.
|
|
87
|
+
CORE_TIEFE = CORE_REL.count("/") + 1
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def projektwurzel(kern: str) -> str:
|
|
91
|
+
"""Die Projektwurzel aus dem Pfad des Kerns - ueber CORE_TIEFE, nicht ueber
|
|
92
|
+
eine im Quelltext gezaehlte Zahl von dirname-Aufrufen."""
|
|
93
|
+
pfad = os.path.abspath(kern)
|
|
94
|
+
for _ in range(CORE_TIEFE):
|
|
95
|
+
pfad = os.path.dirname(pfad)
|
|
96
|
+
return pfad
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def core_dir_name(man: dict) -> str:
|
|
100
|
+
return man.get("runtime_placeholders", {}).get("<CORE_DIR>", CORE_REL)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# DER LIEFERUMFANG EINER INSTALLATION (D-367, CR-2026-141)
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
# Ein Projekt waehlt beim Installieren den ganzen Kern ("voll") oder nur das zur
|
|
107
|
+
# Nutzung Noetige ("nutzung"). Die Liste des Noetigen wird NICHT aufgezaehlt - sie
|
|
108
|
+
# fehlte nach dem ersten neuen Traeger (D-366). Aufgezaehlt ist das Gegenteil, und es
|
|
109
|
+
# ist nach seinem ABLAGEORT geschlossen: die Nachweisschicht aus Aenderungsantraegen,
|
|
110
|
+
# Abnahmeprotokollen, Erhebungen und dem Bau des Hauptdokuments. Ein neuer Traeger zur
|
|
111
|
+
# Nutzung ist damit von selbst dabei, ein neuer Nachweis von selbst draussen.
|
|
112
|
+
#
|
|
113
|
+
# ZWEI ABLEITUNGEN SIND GEMESSEN GESCHEITERT (2026-09-25, sechs frische
|
|
114
|
+
# Installationen): Die Lesespur taugt nicht - der Validator liest im Projekt alle 549
|
|
115
|
+
# Kerndateien. Die transitive Verweishuelle taugt nicht - mit Verzeichnisverweisen
|
|
116
|
+
# umfasst sie alles, ohne sie behaelt sie 39 Protokolle und verliert die Skillquellen,
|
|
117
|
+
# die --update braucht. OB die Nutzung ohne die Nachweisschicht auskommt, entscheidet
|
|
118
|
+
# deshalb der Validator an einer reduzierten Installation (Sonde L367).
|
|
119
|
+
#
|
|
120
|
+
# Die Wahl steht im Projekt in LIEFERUMFANG neben VERSION; install.py schreibt sie bei
|
|
121
|
+
# jedem --target. Fehlt die Datei, gilt "voll" - so liegt jede Installation vor 1.8.0.
|
|
122
|
+
LIEFERUMFANG_DATEI = "LIEFERUMFANG"
|
|
123
|
+
LIEFERUMFAENGE = ("voll", "nutzung")
|
|
124
|
+
NACHWEIS_ABLAGEN = (
|
|
125
|
+
"governance/change-requests",
|
|
126
|
+
"tests/protocols",
|
|
127
|
+
"tests/erhebungen",
|
|
128
|
+
"build",
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def ist_nachweis(rel: str) -> bool:
|
|
133
|
+
"""Liegt der Pfad (relativ zum Kern, mit Schraegstrichen) in der Nachweisschicht?"""
|
|
134
|
+
return any(rel == a or rel.startswith(a + "/") for a in NACHWEIS_ABLAGEN)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def lieferumfang(kern: str) -> str:
|
|
138
|
+
"""Der Lieferumfang, den der Kern unter diesem Pfad fuehrt - roh gelesen.
|
|
139
|
+
|
|
140
|
+
Ohne Datei "voll". Einen unbekannten Wert gibt die Funktion unveraendert zurueck;
|
|
141
|
+
ihn zu melden ist Sache des Aufrufers (Pruefung 90, install.py).
|
|
142
|
+
"""
|
|
143
|
+
try:
|
|
144
|
+
with open(os.path.join(kern, LIEFERUMFANG_DATEI), encoding="utf-8") as fh:
|
|
145
|
+
return fh.read().strip()
|
|
146
|
+
except OSError:
|
|
147
|
+
return "voll"
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
# Werkzeugverben des Frontmatters
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
# Das Vokabular, das ein Skill oder ein Agentenprofil des Kerns in seinem Frontmatter
|
|
154
|
+
# schreiben darf - in allowed-tools wie in permissions.deny. Es steht hier und nicht in
|
|
155
|
+
# install.py, weil drei Stellen es brauchen: die Abbildung beim Installieren, die
|
|
156
|
+
# Abbildung von permissions.deny auf die Werkzeugsperre je Skill (D-65) und Pruefung 38.
|
|
157
|
+
#
|
|
158
|
+
# WARUM ES UEBERHAUPT EINES BRAUCHT. Bis 0.39.0 hatte die Quelle kein deklariertes
|
|
159
|
+
# Vokabular, und der unbekannte Fall war an beiden Stellen STILL - gemessen am
|
|
160
|
+
# 2026-09-13 (tests/protocols/2026-09-13-gegenpruefung-werkzeugabbildung.md):
|
|
161
|
+
# * allowed-tools: ein Verb ohne Abbildung wurde WOERTLICH durchgereicht. Aus
|
|
162
|
+
# 'allowed-tools: banane' wurde in der installierten Fassung der Werkzeugname
|
|
163
|
+
# 'banane'; aus einer geleerten Abbildung wurde 'tools: read, grep, glob' im
|
|
164
|
+
# Agentenprofil fw-reviewer - drei Namen, die dieser Client nicht kennt (M16).
|
|
165
|
+
# * permissions.deny: dasselbe Verb fiel lautlos ganz aus. 'deny: glob' erzeugte
|
|
166
|
+
# keine Werkzeugsperre, und der Validator meldete 0 Fehler (M6).
|
|
167
|
+
# Die drei uebrigen Werkzeugabbildungen desselben Manifests - hook_tools,
|
|
168
|
+
# permission_tools und der Hook-Matcher - brechen im selben Fall ab (M17 bis M19).
|
|
169
|
+
# Diese eine war die unbewachte, und sie kommt zweimal vor.
|
|
170
|
+
FRONTMATTER_VERBEN = ("read", "grep", "glob", "edit", "exec")
|
|
171
|
+
|
|
172
|
+
# Die Bruecke zwischen dem Vokabular der Quelle und dem der Durchsetzungsschichten
|
|
173
|
+
# (hook_tools, permission_tools, framework/runtime/*.json). Die beiden Vokabulare sind
|
|
174
|
+
# getrennt gewachsen und nicht deckungsgleich: Die Quelle trennt das Suchen in 'grep'
|
|
175
|
+
# und 'glob', die Durchsetzung fasst beides als 'search'; die Quelle sagt 'edit', die
|
|
176
|
+
# Durchsetzung 'write'.
|
|
177
|
+
#
|
|
178
|
+
# DIE BRUECKE WIRD BEWACHT, NICHT AUFGELOEST (D-78). Die beiden Listen gleichzuziehen
|
|
179
|
+
# hiesse, eine der Seiten zu aendern - und die Vorabfreigabe auf die Sperrliste zu heben
|
|
180
|
+
# waere eine AUSWEITUNG: Aus 'allowed-tools: Edit, Write' wuerde 'Edit, Write,
|
|
181
|
+
# NotebookEdit'. Verlangt wird deshalb allein die RICHTUNG: Die Sperrliste darf fuer
|
|
182
|
+
# kein Verbpaar enger sein als die Vorabfreigabe. Heute ist sie es bei keinem
|
|
183
|
+
# (gemessen, M4); Pruefung 38 haelt es fest.
|
|
184
|
+
VERB_BRUECKE = {"read": "read", "grep": "search", "glob": "search",
|
|
185
|
+
"edit": "write", "exec": "exec"}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def frontmatter_werkzeuge(man: dict, block: str, verb: str) -> list[str]:
|
|
189
|
+
"""Die Werkzeugnamen dieses Clients fuer ein Verb aus dem Frontmatter der Quelle.
|
|
190
|
+
|
|
191
|
+
`block` ist "skill_frontmatter" oder "agent_frontmatter" - die beiden Stellen, an
|
|
192
|
+
denen dasselbe Vokabular abgebildet wird.
|
|
193
|
+
|
|
194
|
+
Drei Ausgaenge, und keiner davon ist still:
|
|
195
|
+
|
|
196
|
+
* in tool_names abgebildet -> die Werkzeugnamen dieses Clients;
|
|
197
|
+
* in tool_names_unmapped deklariert -> das Verb unveraendert. Ein Client, dessen
|
|
198
|
+
Frontmatter die Verben selbst als Werkzeugnamen fuehrt - oder fuer den das
|
|
199
|
+
unerhoben ist -, sagt das ausdruecklich. Bauform wie hook_tools_absent nach
|
|
200
|
+
D-47: Eine Abwesenheit wird erklaert, nie aus einer Luecke erraten;
|
|
201
|
+
* weder noch -> AbbildungsFehler.
|
|
202
|
+
|
|
203
|
+
Der dritte Ausgang ist die Aenderung gegenueber 0.39.0. Dort reichte derselbe Fall
|
|
204
|
+
das Verb woertlich durch, und niemand erfuhr davon.
|
|
205
|
+
"""
|
|
206
|
+
if verb not in FRONTMATTER_VERBEN:
|
|
207
|
+
raise AbbildungsFehler(
|
|
208
|
+
f"{man.get('client', '?')}: Das Werkzeugverb '{verb}' gehoert nicht zum "
|
|
209
|
+
f"Vokabular des Frontmatters {list(FRONTMATTER_VERBEN)}. Ein Skill oder "
|
|
210
|
+
f"Agentenprofil des Kerns nennt nur diese Verben - alles andere ist ein "
|
|
211
|
+
f"Schreibfehler oder ein Verb der Durchsetzungsschicht, das hier nicht "
|
|
212
|
+
f"hingehoert (D-78)")
|
|
213
|
+
fmt = man.get(block) or {}
|
|
214
|
+
abbildung = fmt.get("tool_names") or {}
|
|
215
|
+
if verb in abbildung:
|
|
216
|
+
return list(abbildung[verb])
|
|
217
|
+
if verb in (fmt.get("tool_names_unmapped") or []):
|
|
218
|
+
return [verb]
|
|
219
|
+
raise AbbildungsFehler(
|
|
220
|
+
f"{man.get('client', '?')}/manifest.json: {block}.tool_names kennt das "
|
|
221
|
+
f"Werkzeugverb '{verb}' nicht. Fuehrt dieser Client die Werkzeugnamen des "
|
|
222
|
+
f"Frontmatters unveraendert - oder sind sie unerhoben -, gehoert das Verb in "
|
|
223
|
+
f"{block}.tool_names_unmapped samt _tool_names_unmapped_note; eine Luecke "
|
|
224
|
+
f"allein ist keine Aussage (D-78, Bauform wie hook_tools_absent nach D-47)")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
# Berechtigungen
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
def _werkzeuge(man: dict, verb: str) -> list[str]:
|
|
232
|
+
abbildung = man.get("permission_tools")
|
|
233
|
+
if abbildung is None:
|
|
234
|
+
raise AbbildungsFehler(
|
|
235
|
+
f"{man.get('client', '?')}/manifest.json: Feld permission_tools fehlt")
|
|
236
|
+
if verb not in abbildung:
|
|
237
|
+
raise AbbildungsFehler(
|
|
238
|
+
f"{man.get('client', '?')}/manifest.json: permission_tools kennt das "
|
|
239
|
+
f"Werkzeugverb '{verb}' nicht")
|
|
240
|
+
return list(abbildung[verb])
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _muster(rohmuster: str, man: dict) -> str:
|
|
244
|
+
"""Pfadmuster in der Schreibweise dieses Clients.
|
|
245
|
+
|
|
246
|
+
Manche Clients verlangen fuer einen Namen ohne Verzeichnisanteil eine ausdrueckliche
|
|
247
|
+
Wurzelangabe, weil ein blosser Name sonst mehrdeutig ist. Aufgeloeste Muster mit
|
|
248
|
+
Verzeichnisanteil, Globs und offene Projektplatzhalter bleiben unberuehrt.
|
|
249
|
+
"""
|
|
250
|
+
muster = resolve_placeholders(rohmuster, man)
|
|
251
|
+
vorsatz = man.get("permission_path_prefix", "")
|
|
252
|
+
if not vorsatz or "/" in muster or muster.startswith(("<", "*")):
|
|
253
|
+
return muster
|
|
254
|
+
return vorsatz + muster
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _befehl(regel: dict, man: dict, korb: str) -> str:
|
|
258
|
+
"""Befehlsverbot in der Schreibweise dieses Clients (woertlich oder praefixbasiert)."""
|
|
259
|
+
woertlich = regel["command"]
|
|
260
|
+
praefix = regel.get("prefix", woertlich.strip())
|
|
261
|
+
if not woertlich.strip().startswith(praefix):
|
|
262
|
+
raise AbbildungsFehler(
|
|
263
|
+
f"permissions.json: prefix '{praefix}' ist kein Praefix von command "
|
|
264
|
+
f"'{woertlich.strip()}' - die Praefixform waere nicht nachweislich breiter "
|
|
265
|
+
f"als die woertliche")
|
|
266
|
+
if man.get("permission_exec_match", "literal") != "prefix":
|
|
267
|
+
return resolve_placeholders(woertlich, man)
|
|
268
|
+
if korb == "allow" and praefix != woertlich.strip():
|
|
269
|
+
raise AbbildungsFehler(
|
|
270
|
+
f"permissions.json: allow-Regel '{woertlich.strip()}' hat eine kuerzere "
|
|
271
|
+
f"prefix-Form - bei allow waere das eine Lockerung")
|
|
272
|
+
praefix = resolve_placeholders(praefix, man)
|
|
273
|
+
if praefix.startswith("<"):
|
|
274
|
+
# Offener Projektplatzhalter: Der Overlay Owner traegt den vollstaendigen Befehl
|
|
275
|
+
# ein; ein angehaengtes Praefixzeichen wuerde ihn verfaelschen.
|
|
276
|
+
return praefix
|
|
277
|
+
return praefix + man.get("permission_exec_suffix", ":*")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _regel_rendern(regel: dict, man: dict, korb: str) -> list[str]:
|
|
281
|
+
verb = regel["tool"]
|
|
282
|
+
ziele = _werkzeuge(man, verb)
|
|
283
|
+
if not ziele:
|
|
284
|
+
if korb in ("deny", "ask"):
|
|
285
|
+
raise AbbildungsFehler(
|
|
286
|
+
f"{man.get('client', '?')}: kein Werkzeug fuer '{verb}', die "
|
|
287
|
+
f"{korb}-Regel {regel} liesse sich nur durch Weglassen abbilden - das "
|
|
288
|
+
f"waere eine Lockerung")
|
|
289
|
+
return []
|
|
290
|
+
ohne_muster = set(man.get("permission_tools_bare", []))
|
|
291
|
+
if verb == "exec":
|
|
292
|
+
argument = _befehl(regel, man, korb)
|
|
293
|
+
elif verb == "skill":
|
|
294
|
+
# Ein Aufrufname, kein Pfad: weder Wurzelpraefix noch Praefixzeichen, und
|
|
295
|
+
# keine Musterausweitung. Gemessen am 2026-09-14 (D-82): Das Argument wird
|
|
296
|
+
# woertlich verglichen - Skill(fw-*) laesst den Aufruf von fw-code-explain
|
|
297
|
+
# NICHT durch. Wer hier ein Muster erzeugte, erzeugte eine Freigabe, die
|
|
298
|
+
# lautlos nichts freigibt - der Befundtyp von D-66, mit umgekehrtem
|
|
299
|
+
# Vorzeichen.
|
|
300
|
+
argument = regel["pattern"]
|
|
301
|
+
else:
|
|
302
|
+
argument = _muster(regel["pattern"], man)
|
|
303
|
+
return [name if name in ohne_muster else f"{name}({argument})" for name in ziele]
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _korb_rendern(quelle: dict, man: dict, korb: str) -> list[str]:
|
|
307
|
+
raus: list[str] = []
|
|
308
|
+
for regel in quelle.get(korb, []):
|
|
309
|
+
for gerendert in _regel_rendern(regel, man, korb):
|
|
310
|
+
if gerendert not in raus:
|
|
311
|
+
raus.append(gerendert)
|
|
312
|
+
return raus
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def basket_rules(quelle: dict, man: dict, korb: str) -> list[str]:
|
|
316
|
+
"""Die Regeln eines Korbes in der Schreibweise dieses Clients.
|
|
317
|
+
|
|
318
|
+
Dieselbe Bauart wie core_rules, nur ohne die Einschraenkung auf 'core': true. Der
|
|
319
|
+
Validator haelt die installierte Datei damit gegen die Kernquelle (Pruefung 37,
|
|
320
|
+
CR-2026-061, D-77): Was hier erzeugt wird, muss dort stehen; was dort zusaetzlich
|
|
321
|
+
steht, ist nur im deny-Korb zulaessig - dort ist es eine Verschaerfung, in ask und
|
|
322
|
+
allow eine Ausweitung.
|
|
323
|
+
|
|
324
|
+
Oeffentlich und nicht _korb_rendern, weil eine Pruefung, die auf eine private
|
|
325
|
+
Funktion greift, beim naechsten Umbau still ausfaellt.
|
|
326
|
+
"""
|
|
327
|
+
return _korb_rendern(quelle, man, korb)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def core_rules(quelle: dict, man: dict) -> list[str]:
|
|
331
|
+
"""Die Kernzusagen B1 bis B6 in der Schreibweise dieses Clients."""
|
|
332
|
+
raus: list[str] = []
|
|
333
|
+
for regel in quelle.get("deny", []):
|
|
334
|
+
if not regel.get("core"):
|
|
335
|
+
continue
|
|
336
|
+
for gerendert in _regel_rendern(regel, man, "deny"):
|
|
337
|
+
if gerendert not in raus:
|
|
338
|
+
raus.append(gerendert)
|
|
339
|
+
return raus
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def import_control(man: dict) -> tuple[str, object] | None:
|
|
343
|
+
"""Importsteuerung dieses Clients als (Schluessel, Wert) - oder None.
|
|
344
|
+
|
|
345
|
+
Ein Client ohne solchen Mechanismus traegt das Feld nicht; dann bleibt es bei der
|
|
346
|
+
Auskunft im Abschnitt "Anweisungs- und Konfigurationsquellen ausserhalb des
|
|
347
|
+
Projekts" seines Client Packs (D-37). Dasselbe Feld liest Pruefung 22.
|
|
348
|
+
"""
|
|
349
|
+
steuerung = man.get("import_control")
|
|
350
|
+
if not steuerung:
|
|
351
|
+
return None
|
|
352
|
+
schluessel = steuerung.get("key")
|
|
353
|
+
if not schluessel or "value" not in steuerung:
|
|
354
|
+
raise AbbildungsFehler(
|
|
355
|
+
f"{man.get('client', '?')}/manifest.json: import_control braucht 'key' "
|
|
356
|
+
f"und 'value'")
|
|
357
|
+
return schluessel, steuerung["value"]
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _kommentar(man: dict, mit_hooks: bool) -> str:
|
|
361
|
+
kern = core_dir_name(man)
|
|
362
|
+
pack = man.get("client", "?")
|
|
363
|
+
teile = [
|
|
364
|
+
f"Berechtigungsvorlage des Frameworks (Ebene 3), erzeugt fuer das Client "
|
|
365
|
+
f"Pack {pack}.",
|
|
366
|
+
f"Regelmenge: {kern}/framework/runtime/permissions.json. Abbildung auf die "
|
|
367
|
+
f"Werkzeuge dieses Clients: {kern}/clients/{pack}/manifest.json. Inhaltliche "
|
|
368
|
+
f"Aenderungen gehoeren dorthin und laufen als Aenderungsantrag.",
|
|
369
|
+
"Mechanismus: deny vor ask vor allow.",
|
|
370
|
+
]
|
|
371
|
+
if mit_hooks:
|
|
372
|
+
teile.append("Dieser Client kennt keine eigene Hook-Datei; die Hooks stehen "
|
|
373
|
+
"deshalb hier unter 'hooks'.")
|
|
374
|
+
teile.append(
|
|
375
|
+
"Platzhalter in spitzen Klammern traegt der Overlay Owner hier ein - das ist der "
|
|
376
|
+
"einzige Teil dieser Datei, der dem Projekt gehoert.")
|
|
377
|
+
teile.append(
|
|
378
|
+
"Die unter _core_rules_integrity aufgefuehrten Regeln duerfen vom Projekt nicht "
|
|
379
|
+
f"entfernt werden; {kern}/tests/scripts/validate-framework.py prueft sie gegen "
|
|
380
|
+
"die Kernquelle.")
|
|
381
|
+
teile.append(
|
|
382
|
+
"Derselbe Lauf haelt alle drei Koerbe gegen die Kernquelle: Was dort erzeugt "
|
|
383
|
+
"wird, muss hier stehen. Eine zusaetzliche Regel ist nur unter 'deny' zulaessig "
|
|
384
|
+
"- dort ist sie eine Verschaerfung; unter 'ask' und 'allow' waere sie eine "
|
|
385
|
+
"Ausweitung und ist ein Fehler. Ein zusaetzlicher freigegebener Befehl gehoert "
|
|
386
|
+
"deshalb nicht hierher, sondern in Abschnitt 6 des Overlays und damit in die "
|
|
387
|
+
"Regelschicht.")
|
|
388
|
+
teile.append(
|
|
389
|
+
"Ein Befehlsschlitz traegt den Befehl, den Abschnitt 5 oder 6 des Overlays fuer "
|
|
390
|
+
"seinen Platzhalter erklaert - ein anderer Befehl an dieser Stelle ist ein "
|
|
391
|
+
"Fehler, auch wenn die Zahl der Zeilen stimmt. Bis 0.43.0 stand der Satz "
|
|
392
|
+
"darueber hier ohne diese Bedingung und versprach zu viel: Ein offener Schlitz "
|
|
393
|
+
"deckte eine hinzugefuegte Zeile (CR-2026-066, D-90). Die Pfadlisten haelt "
|
|
394
|
+
"der Validator unter --strict-overlay gegen das Overlay, in einer Richtung: "
|
|
395
|
+
"Jeder ausgeschlossene Pfad braucht hier eine Lese- und eine Schreibsperre, "
|
|
396
|
+
"jeder nur lesbare eine Schreibsperre (Pruefungen 59 und 89; K-151, D-399).")
|
|
397
|
+
if import_control(man) is not None:
|
|
398
|
+
teile.append(
|
|
399
|
+
"Die Importsteuerung schaltet Regel- und Skillquellen fremder "
|
|
400
|
+
"Werkzeugformate ab (D-37). Das eigene Format der Wurzel-Anweisungsdatei "
|
|
401
|
+
"bleibt eingeschaltet. Sie ist ein Standard, keine Schranke: Die "
|
|
402
|
+
"Benutzerkonfiguration dieser Arbeitsstation hat Vorrang.")
|
|
403
|
+
hinweis = man.get("permissions_note")
|
|
404
|
+
if hinweis:
|
|
405
|
+
teile.append(hinweis)
|
|
406
|
+
teile.append("Exakte Mustersemantik: siehe Zeile B3 der Faehigkeitsmatrix "
|
|
407
|
+
"dieses Client Packs.")
|
|
408
|
+
return " ".join(teile)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def render_permissions(quelltext: str, man: dict, hooks_quelltext: str | None = None) -> str:
|
|
412
|
+
"""Berechtigungsdatei dieses Client Packs aus der neutralen Regelmenge.
|
|
413
|
+
|
|
414
|
+
hooks_quelltext wird nur uebergeben, wenn der Client keine eigene Hook-Datei kennt;
|
|
415
|
+
die Hooks werden dann in dieselbe Datei eingebettet.
|
|
416
|
+
"""
|
|
417
|
+
quelle = json.loads(quelltext)
|
|
418
|
+
rechte: dict = {}
|
|
419
|
+
rechte.update(man.get("permissions_extra", {}))
|
|
420
|
+
for korb in ("deny", "ask", "allow"):
|
|
421
|
+
rechte[korb] = _korb_rendern(quelle, man, korb)
|
|
422
|
+
|
|
423
|
+
ergebnis: dict = {"_comment": _kommentar(man, hooks_quelltext is not None),
|
|
424
|
+
"permissions": rechte}
|
|
425
|
+
# Zusatzschluessel auf der OBERSTEN Ebene der Einstellungsdatei - nicht innerhalb
|
|
426
|
+
# von permissions. Der Unterschied ist keine Kosmetik: Ein Schluessel auf der
|
|
427
|
+
# falschen Ebene wird von diesem Client stillschweigend nicht gelesen, und nichts
|
|
428
|
+
# meldet es. permissions_extra deckt die eine Ebene, settings_extra die andere
|
|
429
|
+
# (CR-2026-087, D-155). Pruefung 54 haelt beide gegen die erzeugte Datei.
|
|
430
|
+
for schluessel, wert in man.get("settings_extra", {}).items():
|
|
431
|
+
ergebnis[schluessel] = wert
|
|
432
|
+
steuerung = import_control(man)
|
|
433
|
+
if steuerung is not None:
|
|
434
|
+
ergebnis[steuerung[0]] = steuerung[1]
|
|
435
|
+
if hooks_quelltext is not None:
|
|
436
|
+
ergebnis["hooks"] = _hooks_objekt(hooks_quelltext, man)
|
|
437
|
+
ergebnis["_core_rules_integrity"] = {"deny_must_contain": core_rules(quelle, man)}
|
|
438
|
+
return json.dumps(ergebnis, indent=2, ensure_ascii=False) + "\n"
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
# ---------------------------------------------------------------------------
|
|
442
|
+
# Hooks
|
|
443
|
+
# ---------------------------------------------------------------------------
|
|
444
|
+
|
|
445
|
+
def _hook_matcher(verben: list[str], man: dict) -> str:
|
|
446
|
+
abbildung = man.get("hook_tools")
|
|
447
|
+
if abbildung is None:
|
|
448
|
+
raise AbbildungsFehler(
|
|
449
|
+
f"{man.get('client', '?')}/manifest.json: Feld hook_tools fehlt")
|
|
450
|
+
# Ein Pack darf eine Werkzeugklasse nicht dadurch aus der Durchsetzung nehmen, dass
|
|
451
|
+
# es sie leer laesst - deshalb ist eine leere Liste ein Fehler. Fuehrt der Client
|
|
452
|
+
# aber tatsaechlich kein Werkzeug dieser Art, muss es einen Weg geben, das zu sagen:
|
|
453
|
+
# hook_tools_absent erklaert die Abwesenheit ausdruecklich. Der Unterschied ist der
|
|
454
|
+
# zwischen "nicht abgebildet" und "gibt es nicht", und er muss deklariert werden,
|
|
455
|
+
# nicht aus einer leeren Liste erraten (CR-2026-047 E3, D-47). Pruefung 26 prueft,
|
|
456
|
+
# dass eine so erklaerte Abwesenheit auch in permission_tools steht.
|
|
457
|
+
erklaert_abwesend = set(man.get("hook_tools_absent") or [])
|
|
458
|
+
# SEIT 1.20.0 EIN DRITTER ZUSTAND (K-184, D-486): "unerhoben". Ein Verb, das die
|
|
459
|
+
# Kernquelle neu fuehrt, ist nicht fuer jeden Client gemessen - und ein Matcher auf
|
|
460
|
+
# eine ungemessene Werkzeugform waere eine Zusage ohne Mechanismus (D-78). Das Pack
|
|
461
|
+
# erklaert es ausdruecklich; Pruefung 26 verlangt die Begruendung und haelt die drei
|
|
462
|
+
# Zustaende auseinander.
|
|
463
|
+
erklaert_unerhoben = set(man.get("hook_tools_unerhoben") or [])
|
|
464
|
+
namen: list[str] = []
|
|
465
|
+
for verb in verben:
|
|
466
|
+
if verb in erklaert_unerhoben and not abbildung.get(verb):
|
|
467
|
+
continue
|
|
468
|
+
if verb not in abbildung:
|
|
469
|
+
raise AbbildungsFehler(
|
|
470
|
+
f"{man.get('client', '?')}/manifest.json: hook_tools kennt das "
|
|
471
|
+
f"Werkzeugverb '{verb}' nicht")
|
|
472
|
+
if not abbildung[verb]:
|
|
473
|
+
if verb in erklaert_abwesend:
|
|
474
|
+
continue
|
|
475
|
+
raise AbbildungsFehler(
|
|
476
|
+
f"{man.get('client', '?')}: kein Hook-Werkzeug fuer '{verb}' - der Hook "
|
|
477
|
+
f"wuerde fuer diese Werkzeugklasse nicht ausloesen. Fuehrt der Client "
|
|
478
|
+
f"kein solches Werkzeug, gehoert das Verb in hook_tools_absent")
|
|
479
|
+
for name in abbildung[verb]:
|
|
480
|
+
if name not in namen:
|
|
481
|
+
namen.append(name)
|
|
482
|
+
return man.get("hook_matcher_separator", "|").join(namen)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
# Kandidaten in der Reihenfolge, in der sie geprueft werden. "python3" steht vorn,
|
|
486
|
+
# weil es auf POSIX-Systemen der verlaessliche Name ist; "py" ist der Windows-Launcher.
|
|
487
|
+
INTERPRETER_KANDIDATEN = ("python3", "python", "py")
|
|
488
|
+
_SONDE = "KOOLIE-INTERPRETER-OK"
|
|
489
|
+
_interpreter_gemerkt: list[str] = []
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
def python_interpreter() -> str:
|
|
493
|
+
"""Ermittelt einen Interpreternamen, der auf dieser Maschine wirklich Python startet.
|
|
494
|
+
|
|
495
|
+
Nicht "python3" fest verdrahten: Unter Windows ist das haeufig der
|
|
496
|
+
Microsoft-Store-Alias, der nur eine Fehlermeldung ausgibt und mit Exit-Code 49
|
|
497
|
+
endet, ohne dass ein Interpreter startet. Ein Hook, der so aufgerufen wird, laeuft
|
|
498
|
+
nie - und ein Schutz-Hook, der nicht laeuft, blockiert nichts (AP2-CC-13).
|
|
499
|
+
|
|
500
|
+
Geprueft wird deshalb nicht die Anwesenheit des Namens, sondern seine Wirkung:
|
|
501
|
+
Der Kandidat muss eine Sonde ausgeben. Das ist dieselbe Unterscheidung, die D-23
|
|
502
|
+
fuer Pruefungen verlangt - Anwesenheit ist kein Nachweis.
|
|
503
|
+
"""
|
|
504
|
+
if _interpreter_gemerkt:
|
|
505
|
+
return _interpreter_gemerkt[0]
|
|
506
|
+
versucht = []
|
|
507
|
+
for kandidat in INTERPRETER_KANDIDATEN:
|
|
508
|
+
try:
|
|
509
|
+
lauf = subprocess.run(
|
|
510
|
+
[kandidat, "-c", "import sys; sys.stdout.write('%s')" % _SONDE],
|
|
511
|
+
capture_output=True, text=True, timeout=15)
|
|
512
|
+
except (OSError, subprocess.SubprocessError) as fehler:
|
|
513
|
+
versucht.append("%s (%s)" % (kandidat, type(fehler).__name__))
|
|
514
|
+
continue
|
|
515
|
+
if lauf.returncode == 0 and _SONDE in (lauf.stdout or ""):
|
|
516
|
+
_interpreter_gemerkt.append(kandidat)
|
|
517
|
+
return kandidat
|
|
518
|
+
versucht.append("%s (Exit %s)" % (kandidat, lauf.returncode))
|
|
519
|
+
raise AbbildungsFehler(
|
|
520
|
+
"Kein funktionsfaehiger Python-Interpreter fuer die Hook-Aufrufe gefunden. "
|
|
521
|
+
"Geprueft: " + ", ".join(versucht) + ". Die Hooks des Frameworks setzen die "
|
|
522
|
+
"Zusagen H2 und die Overlay-Statusmeldung durch; ohne Interpreter liefen sie "
|
|
523
|
+
"nicht, und ein Schutz-Hook, der nicht laeuft, blockiert nichts (AP2-CC-13). "
|
|
524
|
+
"Installation abgebrochen, statt eine Zusage zu erzeugen, die nicht traegt (D-26)")
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _hook_befehl(script: str, man: dict, enforcing: bool = False,
|
|
528
|
+
needs_project_paths: bool = False) -> str:
|
|
529
|
+
"""Aufrufkommando eines Hooks fuer diesen Client.
|
|
530
|
+
|
|
531
|
+
Bei einem durchsetzenden Hook (enforcing in der Kernquelle) haengt die Abbildung
|
|
532
|
+
--fail-closed an, sobald das Pack das Eingabeschema seines Clients als bestaetigt
|
|
533
|
+
fuehrt. Der Schalter steht damit im Kommando, das der Client ohnehin ausfuehrt, und
|
|
534
|
+
nicht in einer Umgebungsvariablen: Ob ein Client env an den Hook-Prozess weiterreicht,
|
|
535
|
+
ist fuer keines der Packs belegt, und eine Sperre, die auf einer unbelegten
|
|
536
|
+
Clientzusage steht, ist genau die Lage, aus der AP2-CC-13 kam (D-31).
|
|
537
|
+
|
|
538
|
+
Dieselbe Begruendung traegt needs_project_paths: Ein Hook, der Projektverzeichnis und
|
|
539
|
+
Regelablage braucht, bekommt sie als Argumente aus dieser Abbildung - nicht aus der
|
|
540
|
+
Umgebung und schon gar nicht geraten. Ein Skript des Kerns kennt die Laufzeitschicht
|
|
541
|
+
eines Packs nicht (D-30, fortgeschrieben mit CR-2026-037).
|
|
542
|
+
"""
|
|
543
|
+
variable = man.get("hook_project_dir_var")
|
|
544
|
+
ausdruck = man.get("hook_project_dir_expr")
|
|
545
|
+
if not variable and not ausdruck:
|
|
546
|
+
raise AbbildungsFehler(
|
|
547
|
+
f"{man.get('client', '?')}/manifest.json: Feld hook_project_dir_var fehlt")
|
|
548
|
+
# WARUM ES DANEBEN EINEN AUSDRUCK GIBT (CR-2026-133, D-347). Eine Variable ist
|
|
549
|
+
# nur eine der Formen, in denen ein Client dem Hook sein Projektverzeichnis sagt -
|
|
550
|
+
# und bei openai-codex ist es keine: Am 2026-09-23 ist der Hook-Prozess
|
|
551
|
+
# aufgezeichnet worden, und seine Umgebung fuehrt AUSSER CODEX_HOME nichts; sein
|
|
552
|
+
# ARBEITSVERZEICHNIS ist das Projektverzeichnis. Eine Variable, die es nicht gibt,
|
|
553
|
+
# haette das Kommando auf einen Pfad unterhalb der Wurzel zeigen lassen - der Hook
|
|
554
|
+
# waere gestartet worden und haette nichts gefunden.
|
|
555
|
+
wurzel = ausdruck if ausdruck else "$" + variable
|
|
556
|
+
befehl = (python_interpreter() + ' "' + wurzel + '/'
|
|
557
|
+
+ core_dir_name(man) + '/' + script + '"')
|
|
558
|
+
if enforcing and man.get("hook_fail_closed") is True:
|
|
559
|
+
befehl += " --fail-closed"
|
|
560
|
+
if enforcing:
|
|
561
|
+
# DIE SPERRFORM GEHOERT IN DAS KOMMANDO, UND DER GRUND IST GEMESSEN
|
|
562
|
+
# (CR-2026-133, D-347). Ein durchsetzender Hook sagt seinem Client, dass er
|
|
563
|
+
# sperrt - und die Form dieser Aussage ist clientgebunden. Am 2026-09-23 an
|
|
564
|
+
# einer realen Installation von openai-codex gemessen: Die bisherige Form
|
|
565
|
+
# ({"decision": "block"} und Exit 2) meldet der Client als FEHLGESCHLAGENEN
|
|
566
|
+
# Hook und FUEHRT DIE OPERATION AUS - im Gegenlauf kam der Koederinhalt
|
|
567
|
+
# woertlich heraus. Dieselbe Sperre in der Form, die dieser Client liest,
|
|
568
|
+
# blockiert - auch im Modus, der Rueckfragen und Sandkasten abschaltet.
|
|
569
|
+
# ➡️ Ein Hook, der laeuft und dessen Sperrform der Client nicht liest, ist
|
|
570
|
+
# eine Zusage ohne Mechanismus, und nichts meldet es.
|
|
571
|
+
# Der Standard bleibt die bisherige Form; ein Pack, das eine andere braucht,
|
|
572
|
+
# SAGT sie. Pruefung 86 verlangt von jedem Pack mit durchsetzendem Hook, dass
|
|
573
|
+
# das Skript die genannte Form kennt.
|
|
574
|
+
form = man.get("hook_block_form")
|
|
575
|
+
if form:
|
|
576
|
+
befehl += " --sperrform " + form
|
|
577
|
+
if needs_project_paths:
|
|
578
|
+
regelablage = man.get("runtime_placeholders", {}).get("<RULES_DIR>")
|
|
579
|
+
if not regelablage:
|
|
580
|
+
raise AbbildungsFehler(
|
|
581
|
+
f"{man.get('client', '?')}/manifest.json: <RULES_DIR> fehlt in "
|
|
582
|
+
f"runtime_placeholders; der meldende Hook braucht die Regelablage")
|
|
583
|
+
befehl += ' "' + wurzel + '" "' + regelablage + '"'
|
|
584
|
+
return befehl
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
def _hooks_objekt(quelltext: str, man: dict) -> dict:
|
|
588
|
+
quelle = json.loads(quelltext)
|
|
589
|
+
ergebnis: dict = {}
|
|
590
|
+
for ereignis, eintraege in quelle.items():
|
|
591
|
+
if ereignis.startswith("_"):
|
|
592
|
+
continue
|
|
593
|
+
gerendert = []
|
|
594
|
+
for eintrag in eintraege:
|
|
595
|
+
neu: dict = {}
|
|
596
|
+
if "on" in eintrag:
|
|
597
|
+
neu["matcher"] = _hook_matcher(eintrag["on"], man)
|
|
598
|
+
# Zusatzfelder, die dieser Client je Hook-Eintrag verlangt. Bei
|
|
599
|
+
# openai-codex ist das "enabled": true - gemessen am 2026-09-23: ohne
|
|
600
|
+
# dieses Feld laeuft der Eintrag nicht, und der Client meldet es nicht.
|
|
601
|
+
# Ein Pack, das kein solches Feld braucht, fuehrt das Manifestfeld nicht.
|
|
602
|
+
neu.update(man.get("hook_handler_extra") or {})
|
|
603
|
+
# "enforcing" und "needs_project_paths" sind Steuerinformation der
|
|
604
|
+
# Kernquelle und keine Felder des Clients; sie steuern nur, ob das Kommando
|
|
605
|
+
# --fail-closed traegt und ob ihm Projektverzeichnis und Regelablage folgen.
|
|
606
|
+
neu["hooks"] = [
|
|
607
|
+
{"type": h["type"],
|
|
608
|
+
"command": _hook_befehl(h["script"], man,
|
|
609
|
+
h.get("enforcing") is True,
|
|
610
|
+
h.get("needs_project_paths") is True),
|
|
611
|
+
"timeout": h["timeout"]}
|
|
612
|
+
for h in eintrag["hooks"]
|
|
613
|
+
]
|
|
614
|
+
gerendert.append(neu)
|
|
615
|
+
ergebnis[ereignis] = gerendert
|
|
616
|
+
return ergebnis
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def render_hooks(quelltext: str, man: dict) -> str:
|
|
620
|
+
"""Eigenstaendige Hook-Datei fuer Clients, die eine kennen.
|
|
621
|
+
|
|
622
|
+
Manche Clients erwarten die Ereignisse unter einem Schluessel der obersten Ebene
|
|
623
|
+
statt unmittelbar in der Datei. Welcher das ist, sagt das Pack in
|
|
624
|
+
hooks_file_wrapper - gemessen, nicht geraten: Bei openai-codex meldet der Client
|
|
625
|
+
eine Datei ohne diesen Schluessel mit *unknown field `PreToolUse`* und LAEDT SIE
|
|
626
|
+
NICHT; er startet trotzdem, und ohne die Meldung im Blick haette man einen
|
|
627
|
+
ausgelieferten Schutz-Hook gehabt, der nie laeuft (D-347).
|
|
628
|
+
|
|
629
|
+
Seit 1.13.0 gibt es eine zweite Gestalt (hooks_format "kiro-v1", D-414): eine
|
|
630
|
+
LISTE von Hooks mit Ausloeser je Eintrag statt eines Objekts je Ereignis.
|
|
631
|
+
"""
|
|
632
|
+
if man.get("hooks_format") == "kiro-v1":
|
|
633
|
+
return render_hooks_kiro(quelltext, man)
|
|
634
|
+
if man.get("hooks_format") == "cursor-v1":
|
|
635
|
+
return render_hooks_cursor(quelltext, man)
|
|
636
|
+
objekt = _hooks_objekt(quelltext, man)
|
|
637
|
+
schluessel = man.get("hooks_file_wrapper")
|
|
638
|
+
if schluessel:
|
|
639
|
+
objekt = {schluessel: objekt}
|
|
640
|
+
return json.dumps(objekt, indent=2, ensure_ascii=False) + "\n"
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
# ---------------------------------------------------------------------------
|
|
644
|
+
# Hilfsfunktionen fuer die Aufrufer
|
|
645
|
+
# ---------------------------------------------------------------------------
|
|
646
|
+
|
|
647
|
+
def hooks_in_permissions(man: dict) -> bool:
|
|
648
|
+
"""Wahr, wenn dieser Client keine eigene Hook-Datei kennt.
|
|
649
|
+
|
|
650
|
+
Erkennbar daran, dass Hook- und Berechtigungsdatei auf denselben Pfad zeigen; eine
|
|
651
|
+
zusaetzliche Angabe im Manifest waere eine zweite Wahrheit ueber dieselbe Tatsache.
|
|
652
|
+
"""
|
|
653
|
+
p = man.get("runtime_placeholders", {})
|
|
654
|
+
hooks, rechte = p.get("<HOOKS_FILE>"), p.get("<PERMISSIONS_FILE>")
|
|
655
|
+
return hooks is not None and hooks == rechte
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def load_source(core_dir: str, name: str) -> str:
|
|
659
|
+
with open(os.path.join(core_dir, "framework", "runtime", name), encoding="utf-8") as fh:
|
|
660
|
+
return fh.read()
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
# ---------------------------------------------------------------------------
|
|
664
|
+
# Zweite Ausgabeform: Rechteprofil (TOML) und Befehlsregeln (eigene Regelsprache)
|
|
665
|
+
# ---------------------------------------------------------------------------
|
|
666
|
+
#
|
|
667
|
+
# WARUM ES SIE GIBT, UND DER GRUND IST GEMESSEN (CR-2026-133, D-346). Die erste
|
|
668
|
+
# Ausgabeform rendert Koerbe aus Regeln der Gestalt Werkzeug(Muster) in EINE Datei.
|
|
669
|
+
# Der Client openai-codex kennt diese Gestalt nicht. Er bindet Pfade an eine
|
|
670
|
+
# Zugriffsart (read / write / deny) in einem Rechteprofil seiner Konfigurationsdatei,
|
|
671
|
+
# und Befehle an eigene Regeldateien in einer eigenen Sprache. Die Regelmenge des
|
|
672
|
+
# Kerns zerfaellt damit in ZWEI Erzeugnisse, und keines davon ist eine Liste von
|
|
673
|
+
# Werkzeug(Muster)-Zeilen.
|
|
674
|
+
#
|
|
675
|
+
# WAS DABEI AUSFAELLT, FAELLT NICHT LAUTLOS AUS. Gemessen am 2026-09-23
|
|
676
|
+
# (tests/protocols/2026-09-23-bau-openai-codex.md):
|
|
677
|
+
# * Ein Musterausdruck ist als Schluessel nur mit ABSOLUTEM oder ~/-Vorsatz
|
|
678
|
+
# zulaessig - und dann nur fuer die Zugriffsart deny. Ein projektrelativer
|
|
679
|
+
# Schluessel (":workspace/<pfad>") nimmt KEIN Muster. Musterform und
|
|
680
|
+
# Versionierbarkeit schliessen einander damit aus, und B3 faellt an beiden Enden.
|
|
681
|
+
# (Gemessen mit 0.156.1. Die Herstellerdokumentation nennt seit 0.157 Globs
|
|
682
|
+
# unter ":workspace_roots" als deny-Leseregeln - nicht nachgemessen, K-160.)
|
|
683
|
+
# * Seit 0.157 heisst das Sonderziel ":workspace_roots" und traegt seine
|
|
684
|
+
# Unterpfade als eigene Tabelle ("." = der Arbeitsbereich selbst). Die alte
|
|
685
|
+
# Form ":workspace/<pfad>" meldet 0.157 als unbekannt und IGNORIERT sie - mit
|
|
686
|
+
# ihr war der ganze Arbeitsbereich schreibgeschuetzt, gemessen mit
|
|
687
|
+
# `codex sandbox` am 2026-09-26 (K-157, D-412).
|
|
688
|
+
# * Ein deny-Leserecht verlangt ausserdem den erhoehten Windows-Sandkasten; auf
|
|
689
|
+
# einem unerhoehten Arbeitsplatz LAEUFT DER CLIENT DANN NICHT (fail-closed).
|
|
690
|
+
# Diese Ausgabeform rendert den Lesekorb deshalb NICHT und fuehrt seine Regeln in
|
|
691
|
+
# nicht_abgebildete_pfadregeln() auf. Pruefung 87 haelt die Zahl gegen das Pack; das
|
|
692
|
+
# Pack traegt B3 als [NICHT ABBILDBAR] mit Begruendung, Ersatz und Freigabe nach
|
|
693
|
+
# clients/README.md Abschnitt 4. Eine Regel, die den Client am Starten hindert, waere
|
|
694
|
+
# keine Schranke, sondern ein Ausfall.
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def _toml_zeichenkette(text: str) -> str:
|
|
698
|
+
return '"' + text.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _toml_wert(wert: object) -> str:
|
|
702
|
+
if wert is True:
|
|
703
|
+
return "true"
|
|
704
|
+
if wert is False:
|
|
705
|
+
return "false"
|
|
706
|
+
if isinstance(wert, int):
|
|
707
|
+
return str(wert)
|
|
708
|
+
return _toml_zeichenkette(str(wert))
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def _profil_schluessel(rohmuster: str, man: dict) -> str | None:
|
|
712
|
+
"""Projektrelativer Schluessel des Rechteprofils - oder None, wenn nicht abbildbar.
|
|
713
|
+
|
|
714
|
+
Abbildbar ist genau, was nach dem Aufloesen der Platzhalter KEIN Muster mehr
|
|
715
|
+
enthaelt: Die Schluesselsyntax dieses Clients nimmt Muster nur mit absolutem oder
|
|
716
|
+
~/-Vorsatz an, und ein absoluter Pfad in einem versionierten Traeger waere ein Wert
|
|
717
|
+
dieser Arbeitsstation. Ein offener Projektplatzhalter bleibt ebenfalls aus - er
|
|
718
|
+
traegt eine Liste, die der Overlay Owner fuellt, und die Schluesselseite einer
|
|
719
|
+
TOML-Tabelle kennt keinen Schlitz.
|
|
720
|
+
"""
|
|
721
|
+
muster = resolve_placeholders(rohmuster, man)
|
|
722
|
+
if muster.startswith("<"):
|
|
723
|
+
return None
|
|
724
|
+
# Ein Teilbaummuster ist die eine Musterform, die dieser Client projektrelativ
|
|
725
|
+
# nimmt - gemessen am 2026-09-23: ":workspace/.koolie/**" wird angenommen und
|
|
726
|
+
# steht danach als Teilbaum ":workspace/.koolie" im wirksamen Profil. Alles
|
|
727
|
+
# andere - ein Namensmuster wie **/*.lock - bleibt aus.
|
|
728
|
+
if muster.endswith("/**"):
|
|
729
|
+
muster = muster[:-3]
|
|
730
|
+
if any(z in muster for z in ("*", "?", "[")):
|
|
731
|
+
return None
|
|
732
|
+
vorsatz = man.get("permission_path_special", ":workspace_roots")
|
|
733
|
+
if muster.startswith("./"):
|
|
734
|
+
muster = muster[2:]
|
|
735
|
+
return vorsatz + "/" + muster
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def _profil_eintraege(quelle: dict, man: dict) -> tuple[dict, list[str]]:
|
|
739
|
+
"""Die Pfadregeln des Kerns als Eintraege des Rechteprofils.
|
|
740
|
+
|
|
741
|
+
Rueckgabe: (Eintraege, nicht abgebildete Rohregeln). Die zweite Haelfte ist der
|
|
742
|
+
Grund, warum dieser Ausgabeform eine Zeile im Pack gehoert.
|
|
743
|
+
"""
|
|
744
|
+
eintraege: dict[str, str] = {}
|
|
745
|
+
offen: list[str] = []
|
|
746
|
+
for schluessel, wert in (man.get("permission_path_base") or {}).items():
|
|
747
|
+
eintraege[schluessel] = wert
|
|
748
|
+
for regel in quelle.get("deny", []):
|
|
749
|
+
verb = regel.get("tool")
|
|
750
|
+
if verb == "write":
|
|
751
|
+
schluessel = _profil_schluessel(regel["pattern"], man)
|
|
752
|
+
if schluessel is None:
|
|
753
|
+
offen.append("write " + regel["pattern"])
|
|
754
|
+
continue
|
|
755
|
+
# Schreibverbot auf einem Teilbaum heisst hier: lesbar, nicht schreibbar.
|
|
756
|
+
eintraege[schluessel] = "read"
|
|
757
|
+
elif verb == "read":
|
|
758
|
+
offen.append("read " + regel["pattern"])
|
|
759
|
+
return eintraege, offen
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
def nicht_abgebildete_pfadregeln(quelltext: str, man: dict) -> list[str]:
|
|
763
|
+
"""Die Pfadregeln, die diese Ausgabeform nicht traegt - oeffentlich fuer Pruefung 87."""
|
|
764
|
+
return _profil_eintraege(json.loads(quelltext), man)[1]
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
def _toml_kommentar(man: dict) -> list[str]:
|
|
768
|
+
kern = core_dir_name(man)
|
|
769
|
+
pack = man.get("client", "?")
|
|
770
|
+
return [
|
|
771
|
+
"Berechtigungsvorlage des Frameworks (Ebene 3), erzeugt fuer das Client Pack "
|
|
772
|
+
+ pack + ".",
|
|
773
|
+
"Regelmenge: " + kern + "/framework/runtime/permissions.json. Abbildung auf die",
|
|
774
|
+
"Form dieses Clients: " + kern + "/clients/" + pack + "/manifest.json.",
|
|
775
|
+
"Inhaltliche Aenderungen gehoeren dorthin und laufen als Aenderungsantrag.",
|
|
776
|
+
"Diese Datei traegt die PFADSEITE. Die Befehlsseite steht in der Regeldatei, die",
|
|
777
|
+
"dasselbe Werkzeug erzeugt; beide zusammen sind die Berechtigungsschicht dieses",
|
|
778
|
+
"Packs.",
|
|
779
|
+
"Was diese Datei NICHT traegt, steht in Zeile B3 der Faehigkeitsmatrix des Packs:",
|
|
780
|
+
"Der Lesekorb des Kerns ist hier nicht abbildbar - ein Muster verlangt einen",
|
|
781
|
+
"absoluten Vorsatz und waere damit ein Wert dieser Arbeitsstation, und ein",
|
|
782
|
+
"deny-Leserecht verlangt den erhoehten Windows-Sandkasten.",
|
|
783
|
+
"Die projektlokale Schicht laedt NUR, wenn dieses Projekt in der Benutzer-",
|
|
784
|
+
"konfiguration des Clients als vertraut eingetragen ist. Ohne diesen Eintrag",
|
|
785
|
+
"traegt diese Datei nichts.",
|
|
786
|
+
"Diese Datei traegt ausschliesslich Schluessel, die der Client kennt: Ein",
|
|
787
|
+
"unbekannter Schluessel wird gemeldet und mit --strict-config zum Fehler. Die",
|
|
788
|
+
"Kernregeln haelt deshalb der Validator gegen die Kernquelle und nicht eine",
|
|
789
|
+
"zweite Liste in dieser Datei.",
|
|
790
|
+
]
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
def render_permissions_toml(quelltext: str, man: dict) -> str:
|
|
794
|
+
"""Rechteprofil dieses Client Packs aus der neutralen Regelmenge."""
|
|
795
|
+
quelle = json.loads(quelltext)
|
|
796
|
+
profil = man.get("permission_profile_name")
|
|
797
|
+
if not profil:
|
|
798
|
+
raise AbbildungsFehler(
|
|
799
|
+
man.get("client", "?") + "/manifest.json: Feld permission_profile_name "
|
|
800
|
+
"fehlt - ohne Profilnamen gibt es keine Tabelle, in die die Pfadregeln "
|
|
801
|
+
"gehoeren")
|
|
802
|
+
eintraege, _ = _profil_eintraege(quelle, man)
|
|
803
|
+
|
|
804
|
+
zeilen = ["# " + teil for teil in _toml_kommentar(man)]
|
|
805
|
+
zeilen.append("")
|
|
806
|
+
zeilen.append("default_permissions = " + _toml_zeichenkette(profil))
|
|
807
|
+
zeilen.append("")
|
|
808
|
+
zeilen.append("[permissions." + profil + "]")
|
|
809
|
+
beschreibung = man.get("permission_profile_description")
|
|
810
|
+
if beschreibung:
|
|
811
|
+
zeilen.append("description = " + _toml_zeichenkette(beschreibung))
|
|
812
|
+
zeilen.append("")
|
|
813
|
+
zeilen.append("[permissions." + profil + ".filesystem]")
|
|
814
|
+
# Das Sonderziel des Arbeitsbereichs traegt seine Unterpfade als eigene Tabelle:
|
|
815
|
+
# der Arbeitsbereich selbst als ".", jeder Teilbaum relativ dazu (K-157, D-412).
|
|
816
|
+
vorsatz = man.get("permission_path_special", ":workspace_roots")
|
|
817
|
+
unter: dict[str, str] = {}
|
|
818
|
+
for schluessel in sorted(eintraege):
|
|
819
|
+
if schluessel == vorsatz:
|
|
820
|
+
unter["."] = eintraege[schluessel]
|
|
821
|
+
elif schluessel.startswith(vorsatz + "/"):
|
|
822
|
+
unter[schluessel[len(vorsatz) + 1:]] = eintraege[schluessel]
|
|
823
|
+
else:
|
|
824
|
+
zeilen.append(_toml_zeichenkette(schluessel) + " = "
|
|
825
|
+
+ _toml_zeichenkette(eintraege[schluessel]))
|
|
826
|
+
if unter:
|
|
827
|
+
zeilen.append("")
|
|
828
|
+
zeilen.append("[permissions." + profil + ".filesystem."
|
|
829
|
+
+ _toml_zeichenkette(vorsatz) + "]")
|
|
830
|
+
for schluessel in sorted(unter):
|
|
831
|
+
zeilen.append(_toml_zeichenkette(schluessel) + " = "
|
|
832
|
+
+ _toml_zeichenkette(unter[schluessel]))
|
|
833
|
+
netz = man.get("permission_profile_network")
|
|
834
|
+
if netz:
|
|
835
|
+
zeilen.append("")
|
|
836
|
+
zeilen.append("[permissions." + profil + ".network]")
|
|
837
|
+
for schluessel in sorted(netz):
|
|
838
|
+
zeilen.append(schluessel + " = " + _toml_wert(netz[schluessel]))
|
|
839
|
+
for tabelle in sorted(man.get("permissions_toml_extra") or {}):
|
|
840
|
+
inhalt = man["permissions_toml_extra"][tabelle]
|
|
841
|
+
zeilen.append("")
|
|
842
|
+
zeilen.append("[" + tabelle + "]")
|
|
843
|
+
for schluessel in sorted(inhalt):
|
|
844
|
+
zeilen.append(schluessel + " = " + _toml_wert(inhalt[schluessel]))
|
|
845
|
+
return "\n".join(zeilen) + "\n"
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def _policy_zeile(regel: dict, man: dict, decision: str) -> str | None:
|
|
849
|
+
"""Eine Befehlsregel in der Regelsprache dieses Clients - oder None bei Schlitz."""
|
|
850
|
+
woertlich = regel["command"].strip()
|
|
851
|
+
praefix = regel.get("prefix", woertlich).strip()
|
|
852
|
+
if not woertlich.startswith(praefix):
|
|
853
|
+
raise AbbildungsFehler(
|
|
854
|
+
"permissions.json: prefix '" + praefix + "' ist kein Praefix von command '"
|
|
855
|
+
+ woertlich + "' - die Praefixform waere nicht nachweislich breiter als "
|
|
856
|
+
"die woertliche")
|
|
857
|
+
if decision == "allow" and praefix != woertlich:
|
|
858
|
+
raise AbbildungsFehler(
|
|
859
|
+
"permissions.json: allow-Regel '" + woertlich + "' hat eine kuerzere "
|
|
860
|
+
"prefix-Form - bei allow waere das eine Lockerung")
|
|
861
|
+
aufgeloest = resolve_placeholders(praefix, man)
|
|
862
|
+
if aufgeloest.startswith("<"):
|
|
863
|
+
# Offener Projektplatzhalter: Sein Wert gehoert dem Projekt, und diese
|
|
864
|
+
# Regelsprache kennt keinen Schlitz. Der Befehl bleibt aus - das ist bei
|
|
865
|
+
# 'prompt' eine Verschaerfung (er laeuft in die Rueckfrage) und bei 'allow'
|
|
866
|
+
# ebenfalls (er ist nicht vorab freigegeben).
|
|
867
|
+
return None
|
|
868
|
+
tokens = ", ".join(_toml_zeichenkette(t) for t in aufgeloest.split())
|
|
869
|
+
grund = regel.get("justification") or ("Framework-Regel (" + decision + ")")
|
|
870
|
+
return ("prefix_rule(pattern = [" + tokens + "], decision = "
|
|
871
|
+
+ _toml_zeichenkette(decision) + ", justification = "
|
|
872
|
+
+ _toml_zeichenkette(grund) + ")")
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
#: Korb der Kernquelle -> Entscheidung der Regelsprache dieses Clients.
|
|
876
|
+
POLICY_ENTSCHEIDUNG = {"deny": "forbidden", "ask": "prompt", "allow": "allow"}
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
def _policy_kommentar(man: dict) -> list[str]:
|
|
880
|
+
kern = core_dir_name(man)
|
|
881
|
+
pack = man.get("client", "?")
|
|
882
|
+
return [
|
|
883
|
+
"Befehlsregeln des Frameworks (Ebene 3), erzeugt fuer das Client Pack " + pack + ".",
|
|
884
|
+
"Regelmenge: " + kern + "/framework/runtime/permissions.json, Regeln mit tool=exec.",
|
|
885
|
+
"Reihenfolge der Koerbe: deny vor ask vor allow - forbidden vor prompt vor allow.",
|
|
886
|
+
"Die Praefixform ist nachweislich mindestens so breit wie die woertliche Form;",
|
|
887
|
+
"ihre benannte Grenze ist eine andere Schreibweise desselben Befehls.",
|
|
888
|
+
"Ein Befehlsschlitz des Overlays steht NICHT hier: Sein Wert gehoert dem Projekt,",
|
|
889
|
+
"und diese Regelsprache kennt keinen Platzhalter. Sein Ausbleiben ist in beiden",
|
|
890
|
+
"Koerben eine Verschaerfung.",
|
|
891
|
+
]
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def render_exec_policy(quelltext: str, man: dict) -> str:
|
|
895
|
+
"""Befehlsregeln dieses Client Packs aus der neutralen Regelmenge.
|
|
896
|
+
|
|
897
|
+
Die Regeldatei liegt projektlokal und ist damit versioniert - das ist die Haelfte
|
|
898
|
+
von B1, die dieser Client traegt. Gemessen am 2026-09-23 an einer realen
|
|
899
|
+
Installation: Ein Befehl, den diese Datei verbietet, wird abgewiesen, und der
|
|
900
|
+
Client nennt dabei die Begruendung dieser Datei woertlich.
|
|
901
|
+
"""
|
|
902
|
+
quelle = json.loads(quelltext)
|
|
903
|
+
zeilen = ["# " + teil for teil in _policy_kommentar(man)]
|
|
904
|
+
zeilen.append("")
|
|
905
|
+
for korb in ("deny", "ask", "allow"):
|
|
906
|
+
entscheidung = POLICY_ENTSCHEIDUNG[korb]
|
|
907
|
+
raus: list[str] = []
|
|
908
|
+
for regel in quelle.get(korb, []):
|
|
909
|
+
if regel.get("tool") != "exec":
|
|
910
|
+
continue
|
|
911
|
+
gerendert = _policy_zeile(regel, man, entscheidung)
|
|
912
|
+
if gerendert and gerendert not in raus:
|
|
913
|
+
raus.append(gerendert)
|
|
914
|
+
if not raus:
|
|
915
|
+
continue
|
|
916
|
+
zeilen.append("# --- " + korb + " -> " + entscheidung + " ---")
|
|
917
|
+
zeilen.extend(raus)
|
|
918
|
+
zeilen.append("")
|
|
919
|
+
return "\n".join(zeilen).rstrip("\n") + "\n"
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
def permissions_format(man: dict) -> str:
|
|
923
|
+
"""Ausgabeform der Berechtigungsdatei dieses Packs ('json', 'toml', 'kiro-agent',
|
|
924
|
+
'cursor-json').
|
|
925
|
+
|
|
926
|
+
Der Standard ist 'json': Zwei der fuenf Packs fuehren ihn, und ein fehlendes Feld
|
|
927
|
+
darf nicht die neue Form bedeuten - ein Pack soll seine Form SAGEN und sie nicht
|
|
928
|
+
durch Schweigen erben.
|
|
929
|
+
"""
|
|
930
|
+
return man.get("permissions_format", "json")
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
# ---------------------------------------------------------------------------
|
|
934
|
+
# Dritte Ausgabeform: das Agentenprofil mit Faehigkeitsregeln (kiro)
|
|
935
|
+
# ---------------------------------------------------------------------------
|
|
936
|
+
#
|
|
937
|
+
# WARUM ES SIE GIBT, UND DER GRUND IST GEMESSEN (CR-2026-150, D-414). Der Client kiro
|
|
938
|
+
# fuehrt seine Berechtigungen als Regeln der Gestalt {capability, match, effect} - und
|
|
939
|
+
# die Datei, die ein Projekt dafuer im Arbeitsbereich halten koennte, liegt NICHT im
|
|
940
|
+
# Repositorium, sondern je Arbeitsplatz unter ~/.kiro/workspace-roots/<hash>/. Der
|
|
941
|
+
# einzige versionierbare Traeger ist das Feld `permissions` eines Agentenprofils unter
|
|
942
|
+
# .kiro/agents/. Gemessen am 2026-09-26 mit kiro-cli 2.24.1, Engine V3:
|
|
943
|
+
# * Ein deny im Agentenprofil weist ab und nennt sich ("Source: agent-profile").
|
|
944
|
+
# * Das Profil wirkt nur, wenn es der AKTIVE Agent ist. Die Arbeitsbereichsdatei
|
|
945
|
+
# .kiro/settings/cli.json waehlt ihn (chat.defaultAgent) und schlaegt eine globale
|
|
946
|
+
# Einstellung; render_client_settings() erzeugt sie.
|
|
947
|
+
# * Fehlt das Profil oder ist es kein gueltiges JSON, faellt der Client STILL auf
|
|
948
|
+
# seinen eingebauten Agenten zurueck - nur eine Warnung auf stderr, und .env war
|
|
949
|
+
# lesbar. Pruefung 96 haelt deshalb Einstellung und Profil gegeneinander.
|
|
950
|
+
# * Eine Regel mit unbekannter Faehigkeit wird uebersprungen, gemeldet nur im
|
|
951
|
+
# Protokoll des Clients. Diese Abbildung erzeugt deshalb nur Faehigkeiten aus
|
|
952
|
+
# KIRO_FAEHIGKEITEN, und Pruefung 96 liest dieselbe Menge.
|
|
953
|
+
# * Befehlsmuster: ohne '*' vergleicht der Client WOERTLICH ("echo" sperrte
|
|
954
|
+
# "echo hallo" nicht). Die Praefixform braucht deshalb das Suffix '*'.
|
|
955
|
+
# * Ein Pfadmuster ohne Wildcard trifft auch Unterverzeichnisse (".env" sperrte
|
|
956
|
+
# "sub/.env") - breiter als woertlich, also keine Lockerung.
|
|
957
|
+
|
|
958
|
+
#: Die Faehigkeiten, die der Client kennt (Herstellerdokumentation, Seite
|
|
959
|
+
#: "Permissions", abgerufen 2026-09-26). Eine Regel ausserhalb dieser Menge wuerde
|
|
960
|
+
#: der Client ueberspringen - gemessen (K23 im Protokoll des Baus).
|
|
961
|
+
KIRO_FAEHIGKEITEN = ("fs_read", "fs_write", "shell", "web_fetch", "web_search", "mcp",
|
|
962
|
+
"subagent", "skill", "power", "context", "diagnostics",
|
|
963
|
+
"sandbox_network", "all", "builtin", "filesystem")
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def _kiro_argument(regel: dict, man: dict, korb: str) -> str:
|
|
967
|
+
verb = regel["tool"]
|
|
968
|
+
if verb == "exec":
|
|
969
|
+
return _befehl(regel, man, korb)
|
|
970
|
+
if verb == "skill":
|
|
971
|
+
# Ein Aufrufname, kein Pfad, und woertlich: gemessen am 2026-09-26 - die
|
|
972
|
+
# Freigabe auf einen Namen laesst genau diesen Skill laden, einen anderen nicht.
|
|
973
|
+
return regel["pattern"]
|
|
974
|
+
return _muster(regel["pattern"], man)
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
def kiro_regeln(quelle: dict, man: dict) -> list[dict]:
|
|
978
|
+
"""Die Regelmenge des Kerns als Faehigkeitsregeln dieses Clients.
|
|
979
|
+
|
|
980
|
+
Je Korb und Faehigkeit EINE Regel mit der Liste ihrer Muster, in der Reihenfolge
|
|
981
|
+
der Quelle. Dieselben drei Zusicherungen wie bei den anderen Ausgabeformen: deny
|
|
982
|
+
und ask ohne Zielfaehigkeit brechen ab, die Praefixform ist nachweislich breiter,
|
|
983
|
+
allow wird nie verbreitert (_befehl).
|
|
984
|
+
"""
|
|
985
|
+
# AUSNAHMEN INNERHALB EINER REGEL (D-415). Die Kernregel "write <RUNTIME_DIR>/**"
|
|
986
|
+
# sperrt bei diesem Client auch .kiro/specs/ - und dort legt der Client sein
|
|
987
|
+
# Planartefakt ab, das nach D-415 der Traeger des Plans ist. Kein Framework-Artefakt,
|
|
988
|
+
# aber im Schutzbereich einer Kernregel. Ein allow koennte es nicht freigeben
|
|
989
|
+
# (deny gewinnt); das Feld exclude der Regel nimmt den Teilbaum aus, und nur ihn.
|
|
990
|
+
# Das Manifest nennt die Ausnahme je Quellregel ("verb muster"), und nur fuer deny.
|
|
991
|
+
ausnahmen = man.get("permission_rule_exclude") or {}
|
|
992
|
+
regeln: list[dict] = []
|
|
993
|
+
for korb in ("deny", "ask", "allow"):
|
|
994
|
+
gruppen: dict[str, list[str]] = {}
|
|
995
|
+
einzeln: list[dict] = []
|
|
996
|
+
for regel in quelle.get(korb, []):
|
|
997
|
+
schluessel = f"{regel['tool']} {regel.get('pattern', regel.get('command', ''))}"
|
|
998
|
+
if schluessel in ausnahmen and korb != "deny":
|
|
999
|
+
raise AbbildungsFehler(
|
|
1000
|
+
f"{man.get('client', '?')}/manifest.json: permission_rule_exclude nennt "
|
|
1001
|
+
f"die {korb}-Regel '{schluessel}' - eine Ausnahme ist nur an einem "
|
|
1002
|
+
f"Verbot zulaessig; an ask oder allow veraenderte sie die Breite einer "
|
|
1003
|
+
f"Freigabe")
|
|
1004
|
+
ziele = _werkzeuge(man, regel["tool"])
|
|
1005
|
+
if not ziele:
|
|
1006
|
+
if korb in ("deny", "ask"):
|
|
1007
|
+
raise AbbildungsFehler(
|
|
1008
|
+
f"{man.get('client', '?')}: keine Faehigkeit fuer "
|
|
1009
|
+
f"'{regel['tool']}', die {korb}-Regel {regel} liesse sich nur "
|
|
1010
|
+
f"durch Weglassen abbilden - das waere eine Lockerung")
|
|
1011
|
+
continue
|
|
1012
|
+
argument = _kiro_argument(regel, man, korb)
|
|
1013
|
+
for faehigkeit in ziele:
|
|
1014
|
+
if faehigkeit not in KIRO_FAEHIGKEITEN:
|
|
1015
|
+
raise AbbildungsFehler(
|
|
1016
|
+
f"{man.get('client', '?')}/manifest.json: permission_tools nennt "
|
|
1017
|
+
f"die Faehigkeit '{faehigkeit}', die der Client nicht kennt - er "
|
|
1018
|
+
f"uebersprange die Regel und meldete es nur im Protokoll (D-414)")
|
|
1019
|
+
if schluessel in ausnahmen:
|
|
1020
|
+
einzeln.append({"capability": faehigkeit, "match": [argument],
|
|
1021
|
+
"exclude": list(ausnahmen[schluessel]),
|
|
1022
|
+
"effect": korb})
|
|
1023
|
+
continue
|
|
1024
|
+
liste = gruppen.setdefault(faehigkeit, [])
|
|
1025
|
+
if argument not in liste:
|
|
1026
|
+
liste.append(argument)
|
|
1027
|
+
for faehigkeit, muster in gruppen.items():
|
|
1028
|
+
regeln.append({"capability": faehigkeit, "match": muster, "effect": korb})
|
|
1029
|
+
regeln.extend(einzeln)
|
|
1030
|
+
return regeln
|
|
1031
|
+
|
|
1032
|
+
|
|
1033
|
+
def kiro_kernregeln(quelle: dict, man: dict) -> list[str]:
|
|
1034
|
+
"""Die Kernzusagen als 'faehigkeit muster' - fuer _core_rules_integrity."""
|
|
1035
|
+
raus: list[str] = []
|
|
1036
|
+
for regel in quelle.get("deny", []):
|
|
1037
|
+
if not regel.get("core"):
|
|
1038
|
+
continue
|
|
1039
|
+
argument = _kiro_argument(regel, man, "deny")
|
|
1040
|
+
for faehigkeit in _werkzeuge(man, regel["tool"]):
|
|
1041
|
+
eintrag = faehigkeit + " " + argument
|
|
1042
|
+
if eintrag not in raus:
|
|
1043
|
+
raus.append(eintrag)
|
|
1044
|
+
return raus
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
def render_permissions_kiro(quelltext: str, man: dict) -> str:
|
|
1048
|
+
"""Agentenprofil dieses Client Packs aus der neutralen Regelmenge.
|
|
1049
|
+
|
|
1050
|
+
Unbekannte Schluessel (_comment, _core_rules_integrity) nimmt der Client an -
|
|
1051
|
+
gemessen am 2026-09-26: Mit beiden im Profil blieb es der aktive Agent, und sein
|
|
1052
|
+
deny wies ab (K21 im Protokoll des Baus).
|
|
1053
|
+
"""
|
|
1054
|
+
quelle = json.loads(quelltext)
|
|
1055
|
+
name = man.get("permission_profile_name")
|
|
1056
|
+
if not name:
|
|
1057
|
+
raise AbbildungsFehler(
|
|
1058
|
+
man.get("client", "?") + "/manifest.json: Feld permission_profile_name "
|
|
1059
|
+
"fehlt - ohne Namen gibt es kein Agentenprofil, das die Einstellungsdatei "
|
|
1060
|
+
"waehlen koennte")
|
|
1061
|
+
kern = core_dir_name(man)
|
|
1062
|
+
pack = man.get("client", "?")
|
|
1063
|
+
kommentar = " ".join([
|
|
1064
|
+
f"Agentenprofil des Frameworks (Ebene 3), erzeugt fuer das Client Pack {pack}.",
|
|
1065
|
+
f"Regelmenge: {kern}/framework/runtime/permissions.json. Abbildung: "
|
|
1066
|
+
f"{kern}/clients/{pack}/manifest.json. Inhaltliche Aenderungen gehoeren dorthin "
|
|
1067
|
+
f"und laufen als Aenderungsantrag.",
|
|
1068
|
+
"Mechanismus: deny vor ask vor allow, ueber alle Ebenen des Clients - ein deny "
|
|
1069
|
+
"an irgendeiner Stelle gewinnt.",
|
|
1070
|
+
"Dieses Profil wirkt NUR als aktiver Agent. Die Datei "
|
|
1071
|
+
+ (man.get("client_settings_file") or "?") + " waehlt es als Standard; wer mit "
|
|
1072
|
+
"--agent einen anderen Agenten waehlt, arbeitet ohne diese Regeln. Fehlt diese "
|
|
1073
|
+
"Datei oder ist sie kein gueltiges JSON, faellt der Client still auf seinen "
|
|
1074
|
+
"eingebauten Agenten zurueck (Pruefung 96).",
|
|
1075
|
+
"Platzhalter in spitzen Klammern traegt der Overlay Owner in die match-Listen "
|
|
1076
|
+
"ein. Zusaetzliche Regeln sind nur als deny zulaessig.",
|
|
1077
|
+
"Exakte Mustersemantik: Zeilen B3 und B6 der Faehigkeitsmatrix dieses Client "
|
|
1078
|
+
"Packs.",
|
|
1079
|
+
])
|
|
1080
|
+
profil: dict = {
|
|
1081
|
+
"_comment": kommentar,
|
|
1082
|
+
"name": name,
|
|
1083
|
+
"description": man.get("permission_profile_description", ""),
|
|
1084
|
+
"tools": list(man.get("permission_profile_tools") or ["*"]),
|
|
1085
|
+
"permissions": {"rules": kiro_regeln(quelle, man)},
|
|
1086
|
+
"_core_rules_integrity": {"deny_must_contain": kiro_kernregeln(quelle, man)},
|
|
1087
|
+
}
|
|
1088
|
+
return json.dumps(profil, indent=2, ensure_ascii=False) + "\n"
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
def render_client_settings(man: dict) -> str:
|
|
1092
|
+
"""Die Einstellungsdatei des Arbeitsbereichs, die das Agentenprofil waehlt.
|
|
1093
|
+
|
|
1094
|
+
Ohne Kommentarschluessel: Die Datei ist die Einstellungsablage des Clients und
|
|
1095
|
+
wird von seinem Werkzeug `settings --workspace` geschrieben; ein fremder
|
|
1096
|
+
Schluessel darin ist nicht gemessen. Die Erklaerung steht in der Kernquelle
|
|
1097
|
+
framework/runtime/client-settings.json und in der README der Laufzeitschicht.
|
|
1098
|
+
"""
|
|
1099
|
+
werte = man.get("client_settings")
|
|
1100
|
+
if not werte or not isinstance(werte, dict):
|
|
1101
|
+
raise AbbildungsFehler(
|
|
1102
|
+
man.get("client", "?") + "/manifest.json: client_settings fehlt oder ist "
|
|
1103
|
+
"kein Objekt")
|
|
1104
|
+
return json.dumps(werte, indent=2, ensure_ascii=False) + "\n"
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
def render_hooks_kiro(quelltext: str, man: dict) -> str:
|
|
1108
|
+
"""Hook-Datei in der Gestalt {version: v1, hooks: [...]}.
|
|
1109
|
+
|
|
1110
|
+
Gemessen am 2026-09-26 in einer interaktiven Sitzung (die Hooks laufen NICHT im
|
|
1111
|
+
Betrieb ohne Rueckfragen - der Client aktiviert sie dort gar nicht):
|
|
1112
|
+
* Der Ausloeser steht je Eintrag; die Namen der Kernquelle (PreToolUse,
|
|
1113
|
+
SessionStart) sind gueltig. "PromptSubmit" aus der Dokumentation ist es nicht.
|
|
1114
|
+
* Der Matcher ist ein REGULAERER AUSDRUCK ueber den Werkzeugnamen - "*" allein
|
|
1115
|
+
kompiliert nicht, und der Eintrag laedt dann ohne Filter nicht.
|
|
1116
|
+
* Das Zeitlimit steht in SEKUNDEN (Schema des Clients).
|
|
1117
|
+
* Exit 2 sperrt die Werkzeugausfuehrung; der Folgeaufruf lief.
|
|
1118
|
+
Kein Kommentarschluessel auf oberster Ebene: Die Erklaerung steht in
|
|
1119
|
+
`description` jedes Eintrags, einem Feld, das das Schema fuehrt.
|
|
1120
|
+
"""
|
|
1121
|
+
quelle = json.loads(quelltext)
|
|
1122
|
+
eintraege: list[dict] = []
|
|
1123
|
+
for ereignis, gruppen in quelle.items():
|
|
1124
|
+
if ereignis.startswith("_"):
|
|
1125
|
+
continue
|
|
1126
|
+
for gi, gruppe in enumerate(gruppen):
|
|
1127
|
+
matcher = _hook_matcher(gruppe["on"], man) if "on" in gruppe else None
|
|
1128
|
+
for hi, h in enumerate(gruppe["hooks"]):
|
|
1129
|
+
skript = os.path.basename(h["script"]).rsplit(".", 1)[0]
|
|
1130
|
+
eintrag: dict = {
|
|
1131
|
+
"name": f"koolie-{skript}",
|
|
1132
|
+
"description": (f"Hook des Frameworks ({ereignis}), erzeugt aus "
|
|
1133
|
+
f"{core_dir_name(man)}/framework/runtime/hooks.json "
|
|
1134
|
+
f"fuer das Client Pack {man.get('client', '?')}."),
|
|
1135
|
+
"trigger": ereignis,
|
|
1136
|
+
}
|
|
1137
|
+
if matcher:
|
|
1138
|
+
eintrag["matcher"] = matcher
|
|
1139
|
+
eintrag["action"] = {
|
|
1140
|
+
"type": h["type"],
|
|
1141
|
+
"command": _hook_befehl(h["script"], man,
|
|
1142
|
+
h.get("enforcing") is True,
|
|
1143
|
+
h.get("needs_project_paths") is True),
|
|
1144
|
+
}
|
|
1145
|
+
eintrag["timeout"] = h["timeout"]
|
|
1146
|
+
eintraege.append(eintrag)
|
|
1147
|
+
return json.dumps({"version": "v1", "hooks": eintraege},
|
|
1148
|
+
indent=2, ensure_ascii=False) + "\n"
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def exec_policy_file(man: dict) -> str | None:
|
|
1152
|
+
"""Zielpfad der Befehlsregeldatei - oder None, wenn das Pack keine fuehrt."""
|
|
1153
|
+
return man.get("exec_policy_file")
|
|
1154
|
+
|
|
1155
|
+
|
|
1156
|
+
# ---------------------------------------------------------------------------
|
|
1157
|
+
# Die Endung der Regeldateien je Pack (seit 1.16.0, CR-2026-155)
|
|
1158
|
+
# ---------------------------------------------------------------------------
|
|
1159
|
+
#
|
|
1160
|
+
# Der Client cursor laedt aus .cursor/rules/ NUR Dateien mit der Endung .mdc - eine
|
|
1161
|
+
# .md-Datei dort ignoriert er, gemessen am 2026-09-26 (Kennwort in r2.md stand in
|
|
1162
|
+
# keiner Sitzung im Kontext; Herstellerdokumentation QU-1 sagt dasselbe). Die Kernquellen
|
|
1163
|
+
# heissen weiter *.md; das Pack nennt die Endung, und jede Stelle, die eine Regeldatei
|
|
1164
|
+
# der Ablage beim Namen nennt, fragt hier.
|
|
1165
|
+
|
|
1166
|
+
def regel_endung(man: dict) -> str:
|
|
1167
|
+
return man.get("rule_file_ext", ".md")
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
def regeldatei(man: dict, name: str) -> str:
|
|
1171
|
+
"""Der Dateiname einer Regel in der Ablage dieses Packs ('20-project-overlay.md'
|
|
1172
|
+
wird bei cursor zu '20-project-overlay.mdc')."""
|
|
1173
|
+
endung = regel_endung(man)
|
|
1174
|
+
if endung != ".md" and name.endswith(".md"):
|
|
1175
|
+
return name[:-3] + endung
|
|
1176
|
+
return name
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
def ist_regeldatei(man: dict, dateiname: str) -> bool:
|
|
1180
|
+
return dateiname.endswith(regel_endung(man))
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
# ---------------------------------------------------------------------------
|
|
1184
|
+
# Vierte Ausgabeform: die Berechtigungsdatei des Projekts bei cursor (cursor-json)
|
|
1185
|
+
# ---------------------------------------------------------------------------
|
|
1186
|
+
#
|
|
1187
|
+
# WARUM ES SIE GIBT, UND DER GRUND IST GEMESSEN (CR-2026-155, D-440). Der Client fuehrt
|
|
1188
|
+
# Regeln der Gestalt Werkzeug(Muster) wie die erste Ausgabeform - aber drei Dinge
|
|
1189
|
+
# schliessen die erste Form aus, alle am 2026-09-26 an cursor-agent 2026.09.26 gemessen:
|
|
1190
|
+
# * Die Projektdatei .cursor/cli.json darf NUR den Schluessel permissions tragen. Mit
|
|
1191
|
+
# _comment und _core_rules_integrity brach der Client mit Exit 1 ab ("unrecognized
|
|
1192
|
+
# keys") - fail-closed, aber ein Pack, das den Client nicht starten laesst, ist ein
|
|
1193
|
+
# Ausfall. Die Kernregeln haelt deshalb Pruefung 97 gegen die Kernquelle.
|
|
1194
|
+
# * Es gibt nur allow und deny, KEINEN Rueckfragekorb. Was nicht erlaubt ist, faellt
|
|
1195
|
+
# bei Befehlen und externen Werkzeugen in die Rueckfrage des Clients - bei Dateien
|
|
1196
|
+
# IM Arbeitsbereich nicht: Geschrieben wird dort ohne Rueckfrage (gemessen, auch ohne
|
|
1197
|
+
# --force). Die Rueckfrageregeln der Kernquelle werden deshalb nicht gerendert; jedes
|
|
1198
|
+
# ihrer Verben muss das Manifest unter permission_ask_ohne_korb erklaeren.
|
|
1199
|
+
# * Ein Pfadmuster wird VERANKERT gegen den ABSOLUTEN Pfad verglichen, und '*' steht
|
|
1200
|
+
# fuer eine beliebige Zeichenfolge ueber Trenner hinweg. Unter Windows traf deshalb
|
|
1201
|
+
# weder 'Read(.env)' noch 'Read(**/.env)' - beide Koeder kamen heraus -, wohl aber
|
|
1202
|
+
# 'Read(*\.env)'. Jedes Pfadmuster wird darum in ZWEI Schreibweisen erzeugt, mit
|
|
1203
|
+
# '/' und mit '\', beide mit fuehrendem '*'. Das ist breiter als das Kernmuster
|
|
1204
|
+
# (ein 'AGENTS.md' in einem Unterordner faellt mit darunter) - bei deny eine
|
|
1205
|
+
# Verschaerfung, und allow fuehrt nur '**', das ohnehin alles trifft.
|
|
1206
|
+
|
|
1207
|
+
def cursor_pfadmuster(rohmuster: str, man: dict) -> list[str]:
|
|
1208
|
+
"""Die Schreibweisen eines Pfadmusters, die der Client gegen den absoluten Pfad trifft."""
|
|
1209
|
+
muster = resolve_placeholders(rohmuster, man)
|
|
1210
|
+
if muster.startswith("<") or muster in ("*", "**"):
|
|
1211
|
+
return [muster]
|
|
1212
|
+
rest = muster[2:] if muster.startswith("./") else muster
|
|
1213
|
+
while rest.startswith("**/"):
|
|
1214
|
+
rest = rest[3:]
|
|
1215
|
+
return ["*/" + rest, "*\\" + rest.replace("/", "\\")]
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
def _cursor_argumente(regel: dict, man: dict, korb: str) -> list[str]:
|
|
1219
|
+
verb = regel["tool"]
|
|
1220
|
+
if verb == "exec":
|
|
1221
|
+
return [_befehl(regel, man, korb)]
|
|
1222
|
+
if verb in ("read", "write"):
|
|
1223
|
+
return cursor_pfadmuster(regel["pattern"], man)
|
|
1224
|
+
# fetch nennt eine Domain, kein Pfad; skill und mcp einen Namen.
|
|
1225
|
+
return [resolve_placeholders(regel["pattern"], man)]
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
def cursor_koerbe(quelle: dict, man: dict) -> dict:
|
|
1229
|
+
"""allow und deny in der Schreibweise dieses Clients - ask wird erklaert, nicht erzeugt."""
|
|
1230
|
+
ohne_korb = man.get("permission_ask_ohne_korb") or {}
|
|
1231
|
+
for regel in quelle.get("ask", []):
|
|
1232
|
+
if regel["tool"] not in ohne_korb:
|
|
1233
|
+
raise AbbildungsFehler(
|
|
1234
|
+
f"{man.get('client', '?')}/manifest.json: permission_ask_ohne_korb erklaert "
|
|
1235
|
+
f"das Verb '{regel['tool']}' nicht. Dieser Client kennt keinen Rueckfragekorb; "
|
|
1236
|
+
f"eine Rueckfrageregel, deren Verbleib nicht erklaert ist, waere still "
|
|
1237
|
+
f"entfallen (D-440)")
|
|
1238
|
+
koerbe: dict = {"allow": [], "deny": []}
|
|
1239
|
+
for korb in ("allow", "deny"):
|
|
1240
|
+
for regel in quelle.get(korb, []):
|
|
1241
|
+
ziele = _werkzeuge(man, regel["tool"])
|
|
1242
|
+
if not ziele:
|
|
1243
|
+
if korb == "deny":
|
|
1244
|
+
raise AbbildungsFehler(
|
|
1245
|
+
f"{man.get('client', '?')}: kein Werkzeug fuer '{regel['tool']}', die "
|
|
1246
|
+
f"deny-Regel {regel} liesse sich nur durch Weglassen abbilden - das "
|
|
1247
|
+
f"waere eine Lockerung")
|
|
1248
|
+
continue
|
|
1249
|
+
for argument in _cursor_argumente(regel, man, korb):
|
|
1250
|
+
for typ in ziele:
|
|
1251
|
+
eintrag = f"{typ}({argument})"
|
|
1252
|
+
if eintrag not in koerbe[korb]:
|
|
1253
|
+
koerbe[korb].append(eintrag)
|
|
1254
|
+
return koerbe
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
def cursor_kernregeln(quelle: dict, man: dict) -> list[str]:
|
|
1258
|
+
"""Die Kernzusagen in der Schreibweise dieses Clients - fuer Pruefung 97."""
|
|
1259
|
+
raus: list[str] = []
|
|
1260
|
+
for regel in quelle.get("deny", []):
|
|
1261
|
+
if not regel.get("core"):
|
|
1262
|
+
continue
|
|
1263
|
+
for argument in _cursor_argumente(regel, man, "deny"):
|
|
1264
|
+
for typ in _werkzeuge(man, regel["tool"]):
|
|
1265
|
+
eintrag = f"{typ}({argument})"
|
|
1266
|
+
if eintrag not in raus:
|
|
1267
|
+
raus.append(eintrag)
|
|
1268
|
+
return raus
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
def render_permissions_cursor(quelltext: str, man: dict) -> str:
|
|
1272
|
+
"""Die Berechtigungsdatei des Projekts: {"permissions": {"allow": [...], "deny": [...]}}.
|
|
1273
|
+
|
|
1274
|
+
Kein weiterer Schluessel - der Client verweigert sonst den Start (D-440). Die
|
|
1275
|
+
Erklaerung, die bei den uebrigen Formen in _comment steht, steht in der README der
|
|
1276
|
+
Laufzeitschicht und im Pack.
|
|
1277
|
+
"""
|
|
1278
|
+
koerbe = cursor_koerbe(json.loads(quelltext), man)
|
|
1279
|
+
return json.dumps({"permissions": koerbe}, indent=2, ensure_ascii=False) + "\n"
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
def cursor_ignore_muster(quelle: dict, man: dict) -> list[str]:
|
|
1283
|
+
"""Die Leseverbote der Kernquelle als Zeilen der Ausschlussdatei (ohne Projektschlitze)."""
|
|
1284
|
+
raus: list[str] = []
|
|
1285
|
+
for regel in quelle.get("deny", []):
|
|
1286
|
+
if regel.get("tool") != "read":
|
|
1287
|
+
continue
|
|
1288
|
+
muster = resolve_placeholders(regel["pattern"], man)
|
|
1289
|
+
if muster.startswith("<") or muster in raus:
|
|
1290
|
+
continue
|
|
1291
|
+
raus.append(muster)
|
|
1292
|
+
return raus
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
def render_ignore_cursor(quelltext: str, man: dict) -> str:
|
|
1296
|
+
"""Die Ausschlussdatei des Clients (.cursorignore) aus den Leseverboten der Kernquelle.
|
|
1297
|
+
|
|
1298
|
+
WARUM ES SIE NEBEN DER BERECHTIGUNGSDATEI GIBT (CR-2026-155, D-443). Gemessen am
|
|
1299
|
+
2026-09-26: Das Suchwerkzeug des Clients wertet ein Read-Verbot der
|
|
1300
|
+
Berechtigungsdatei NICHT aus - der Koeder aus secrets/ kam heraus. Mit derselben
|
|
1301
|
+
Angabe in .cursorignore wies der Client das Lesen von .env und sub/.env ab, meldete
|
|
1302
|
+
die Suche im Ordner als ausgefiltert und fand bei der Suche ueber das ganze Projekt
|
|
1303
|
+
nichts. Die Datei spricht die Syntax von .gitignore - relativ zum Projekt und ohne
|
|
1304
|
+
die Trennerfrage der Berechtigungsdatei. Die Shell erreicht sie nicht ('cat .env'
|
|
1305
|
+
lief); das haelt der Schutz-Hook.
|
|
1306
|
+
"""
|
|
1307
|
+
kern = core_dir_name(man)
|
|
1308
|
+
kopf = [
|
|
1309
|
+
"# Ausschlussdatei des Frameworks, erzeugt fuer das Client Pack "
|
|
1310
|
+
+ man.get("client", "?") + ".",
|
|
1311
|
+
"# Quelle: die Leseverbote in " + kern + "/framework/runtime/permissions.json.",
|
|
1312
|
+
"# Der Client verweigert dem Agenten Lesen und Suchen in diesen Pfaden.",
|
|
1313
|
+
"# Die Datei gehoert dem Projekt: <EXCLUDED_PATHS> hier zusaetzlich eintragen.",
|
|
1314
|
+
"# Eine Zeile des Kerns zu entfernen ist eine Lockerung (Pruefung 97).",
|
|
1315
|
+
"",
|
|
1316
|
+
]
|
|
1317
|
+
return "\n".join(kopf + cursor_ignore_muster(json.loads(quelltext), man)) + "\n"
|
|
1318
|
+
|
|
1319
|
+
|
|
1320
|
+
def render_hooks_cursor(quelltext: str, man: dict) -> str:
|
|
1321
|
+
"""Hook-Datei in der Gestalt {version: 1, hooks: {ereignis: [...]}}.
|
|
1322
|
+
|
|
1323
|
+
Gemessen am 2026-09-26 (CR-2026-155, D-441):
|
|
1324
|
+
* Die Ereignisse heissen klein (preToolUse, sessionStart); hook_event_names bildet
|
|
1325
|
+
die Namen der Kernquelle ab.
|
|
1326
|
+
* Der Matcher ist ein regulaerer Ausdruck ueber den Werkzeugnamen.
|
|
1327
|
+
* Ein Hook, der scheitert, laesst die Operation standardmaessig DURCH (fail-open).
|
|
1328
|
+
Ein durchsetzender Hook traegt deshalb failClosed - und dann wertet der Client
|
|
1329
|
+
auch einen Hook OHNE AUSGABE als gescheitert; die Sperrform permission-json
|
|
1330
|
+
antwortet beim Durchlass mit '{}'.
|
|
1331
|
+
* Der Hook-Prozess steht in der Projektwurzel (hook_project_dir_expr ".").
|
|
1332
|
+
"""
|
|
1333
|
+
quelle = json.loads(quelltext)
|
|
1334
|
+
namen = man.get("hook_event_names") or {}
|
|
1335
|
+
ergebnis: dict = {}
|
|
1336
|
+
for ereignis, gruppen in quelle.items():
|
|
1337
|
+
if ereignis.startswith("_"):
|
|
1338
|
+
continue
|
|
1339
|
+
if ereignis not in namen:
|
|
1340
|
+
raise AbbildungsFehler(
|
|
1341
|
+
f"{man.get('client', '?')}/manifest.json: hook_event_names kennt das "
|
|
1342
|
+
f"Ereignis '{ereignis}' nicht - der Hook liefe unter keinem Namen, den der "
|
|
1343
|
+
f"Client ausloest")
|
|
1344
|
+
eintraege: list[dict] = []
|
|
1345
|
+
for gruppe in gruppen:
|
|
1346
|
+
matcher = _hook_matcher(gruppe["on"], man) if "on" in gruppe else None
|
|
1347
|
+
for h in gruppe["hooks"]:
|
|
1348
|
+
durchsetzend = h.get("enforcing") is True
|
|
1349
|
+
eintrag: dict = {"command": _hook_befehl(h["script"], man, durchsetzend,
|
|
1350
|
+
h.get("needs_project_paths") is True),
|
|
1351
|
+
"timeout": h["timeout"]}
|
|
1352
|
+
if matcher:
|
|
1353
|
+
eintrag["matcher"] = matcher
|
|
1354
|
+
if durchsetzend and man.get("hook_fail_closed") is True:
|
|
1355
|
+
eintrag["failClosed"] = True
|
|
1356
|
+
eintraege.append(eintrag)
|
|
1357
|
+
ergebnis[namen[ereignis]] = eintraege
|
|
1358
|
+
return json.dumps({"version": 1, "hooks": ergebnis}, indent=2, ensure_ascii=False) + "\n"
|