# Schema der Registry-Dateien

Zwei Dateien steuern den Compiler. `sources.json` sagt, woher Regeln
kommen. `targets.json` sagt, in welche Formate sie gehen. Beide sind
reine Daten; der Compiler kennt keinen Assistenten beim Namen.

## sources.json

```json
{
  "version": 1,
  "trigger_keys": ["any-code", "docs", "tests"],
  "router_slug": "skill-router",
  "depth_slug": "depth-projection",
  "depth_vocabulary": ["A1", "A2", "A3", "depth level"],
  "envelope_slots": ["gate-preamble", "artifact-summary", "decision-frame", "narration"],
  "sources": [
    {"name": "custom", "kind": "custom", "path": "sources/custom", "enabled": true},
    {"name": "tekom", "kind": "architectural", "path": "sources/vendored/tekom", "enabled": true}
  ]
}
```

| Feld | Bedeutung |
|---|---|
| `trigger_keys` | Geschlossene Liste der Momente, die eine Regel im Router beanspruchen darf. Zwei Regeln auf einem Key sind ein Fehler. |
| `router_slug` | Slug der Regel, die als Router gilt. Jeder Trigger-Key braucht eine Router-Zeile; jede Router-Zeile braucht eine Regel. |
| `depth_slug`, `depth_vocabulary`, `envelope_slots` | Nur diese eine Regel darf über Tiefe sprechen und die Slots nennen. Andere Regeln, die ein Vokabel als ganzes Wort tragen, brechen den Build. |
| `sources[]` | `kind` bestimmt die Priorität bei Dubletten: `custom` schlägt alles, dann `spec-kit`, `architectural`, `community`. `path` ist ein Ordner mit Markdown-Dateien. Optional `url` für einen Remote-Fetch mit Fallback auf den Ordner. |

### Eine Regel-Datei

```markdown
---
scope: scoped
globs: ["**/*.md", "docs/**"]
description: ASD-STE100 rules for clear documentation. Use when writing or editing documentation.
trigger: documentation-style
supersedes: old-doc-rule
---
# STE Documentation Style

- One instruction per sentence.
```

| Frontmatter | Bedeutung |
|---|---|
| `scope` | `global` (immer geladen, im Wortbudget) oder `scoped` (eigene Datei je Format, braucht `globs`). |
| `globs` | Dateimuster. `["**/*"]` ist der Catch-all; Adapter mit `skip_globs` lassen ihn aus. |
| `description` | Höchstens 25 Wörter. Sagt, wann die Regel gilt; die Assistenten lesen nur das, bis die Regel greift. |
| `trigger` | Ein Key aus `trigger_keys`. |
| `supersedes` | Slug einer Regel, die diese ersetzt; die alte wird mit Warnung fallen gelassen. |

Jede `#`-Überschrift ist eine Regel. Ein `##` öffnet eine neue Regel. Der
Slug entsteht aus der Überschrift; reservierte Windows-Namen (`con`,
`nul`, `com1`) sind verboten.

## targets.json

```json
{
  "version": 1,
  "roster_budget": {"owned_words": 1000, "vendor_reserve_words": 1600,
                    "roster_glob": ".claude/skills/*/SKILL.md", "dead_marker": "DEPRECATED"},
  "publish_rewrites": [[".github", "github-dir"]],
  "plugin": {"name": "bestaiconfig", "description": "...", "owner": {"name": "..."},
             "skills_from": "skills", "hook_seed": ".claude/settings.json",
             "hook_path_rewrites": [["\"$CLAUDE_PROJECT_DIR/.claude/skills/", "\"${CLAUDE_PLUGIN_ROOT}/skills/"]],
             "mcp_seed": ".mcp.json",
             "layout": {"native_manifest": ".claude-plugin/plugin.json", "portable_manifest": "plugin.json",
                        "skills_dir": "skills", "hooks_file": "hooks/hooks.json",
                        "mcp_files": [".mcp.json", "mcp.json"],
                        "portable_schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"}},
  "targets": [],
  "tools": []
}
```

### Ein Adapter (`targets[]`)

| Feld | Bedeutung |
|---|---|
| `id` | Komponentenname im Installer. |
| `output_dir` | Ordner im Zielprojekt; leer für Root-Dateien. Wird beim Install komplett ersetzt (`preserve` schützt Muster). |
| `extension` | Dateiendung mit Punkt, auch mehrteilig (`.instructions.md`). |
| `global_placement` | `single_file` (eine Always-on-Datei, genau ein Adapter), `pinned_module` (Always-on-Modul plus Scoped-Dateien), `none` (nur Scoped-Dateien). |
| `pinned_global_name` | Dateiname der Always-on-Datei ohne Endung. |
| `frontmatter` | Vorlage mit Slots: `{alwaysApply}`, `{globs}` (Flow-Liste), `{globs_yaml}` (Block-Liste), `{globs_csv}` (Komma-String), `{description}`, `{slug}`. Erste und letzte Zeile `---`. |
| `budget_profile` | `global` (100 Zeilen, unter 200 Wörter) oder `scoped` (80 Zeilen). |
| `scoped_layout` | `flat` (eine Datei je Regel), `per_rule_dir` (Ordner je Regel mit `per_rule_filename`), `none`. |
| `skip_globs` | Regeln, deren Globs alle hier stehen, werden für diesen Adapter nicht geschrieben. |
| `install.adapter_symlinks` | `[{"link": "CLAUDE.md", "target": "AGENTS.md"}]`. Ziel ist die Always-on-Datei oder ein registry-eigener Ordner. |
| `install.legacy_cleanup` | Dateien, die der Install entfernt (alte Formate). |
| `install.scaffold_dirs` | Ordner, die der Install anlegt, falls sie fehlen. |
| `install.preserve` | Dateinamen-Muster im `output_dir`, die ein Reinstall zurückkopiert. |
| `install.seeds` | `{"source_dir", "install_dir", "files", "known_hashes"}`. Nur geschrieben, wenn die Datei fehlt; `known_hashes` erlaubt das Auffrischen unveränderter alter Stände. |
| `install.owned_assets` | `{"source", "install", "kind": "text" \| "program"}`. Registry-eigene Dateien außerhalb der Regel-Emission, bei jedem Install ersetzt. |
| `install.required` | `true`: die Komponente ist keine Wahl. |

### Werkzeuge (`tools[]`)

`{"name", "purpose", "install": ["brew install x"], "project_path", "payload", "payload_keep"}`.
Der Installer führt `install` nie aus. Fehlt das Werkzeug, nennt er den
ersten Befehl als Hinweis. Mit `project_path` prüft er diesen Pfad statt
`command -v name`. Mit `payload` (Liste von Projektordnern) führt der
Image-Bau des Webdienstes den ersten Befehl einmal aus und packt diese
Ordner ins Paket; der Installer kopiert sie. Ordner in `payload_keep`
liegen in einem `payload`-Ordner und gehören dem Nutzer, sobald es sie
gibt: der Installer schreibt sie nur, wenn sie fehlen.

`bundle_url` (Registry-weit): die https-Adresse des Pakets. Landet im
Manifest als `install.bundle`.

### Registry-weite Felder

| Feld | Bedeutung |
|---|---|
| `roster_budget` | Wortbudget aller Skill-Descriptions: eigener Anteil bricht den Build, Vendor-Anteil warnt beim Install. |
| `publish_rewrites` | Segment-Umbenennung nur im ausgelieferten Baum. Das Manifest behält Installationspfade; der Installer dreht die Umbenennung je Download zurück. |
| `plugin` | Erzeugt `plugin.zip` und `marketplace.json`. `skills_from` nennt den Adapter, dessen Ausgabe zum Skill-Ordner wird. |

## manifest.json (Ausgabe)

`{"version": 1, "artifacts": [...], "install": {...}, "registry_version": "1.6.1"}`.
`install` trägt je Komponente `owned_dirs`, `owned_files`, `symlinks`,
`seeds`, `preserve`, `remove`, `scaffold_dirs`, `required` sowie flache
Listen: `artifact_hashes` (`pfad :: sha256`), `seed_hashes`,
`seed_refresh_hashes`, `tools` (`name :: befehl`), `tool_paths`,
`roster_budget`, `publish_rewrites`. Der Installer liest das Manifest
zeilenweise; das kanonische Layout ist ein Vertrag, kein Zufall.
