JDL — Plugin-System¶
Status: Normativer Entwurf, konsolidiert
Version: 0.3.7
Datum: 2026-09-16
Geltung: Was ein Plugin ist, was sein Artefakt tragen muss, was es beitragen darf, wie es geladen, materialisiert, aktiviert, deaktiviert und für späteres Hot-Reload vorbereitet wird.Nicht hier: Constitution-Regelsprache (
spec-constitution-system), Namensraumregeln (spec-namespaces-und-module), Meta-Innenmechanik (jdl-meta-phase), CompilerDB-Innenmechanik (jade-compiler-db-spec), Context-Innenmechanik (spec-context-model), Bridge-Innenmechanik (spec-context-bridge), Resource-/Autoritätssemantik (spec-resource-model), Scheduler-Innenmechanik (jdl-vm-scheduler-spec), Trap-/Fault-Innenmechanik (jade-fehlermodell), JVB-/FFI-Innenmechanik.
Kennzeichnung
| Marke | Bedeutung |
|---|---|
[N] |
normativ, gilt |
[D] |
Entscheidung dieser Fassung |
[O] |
offen |
Syntaxbeispiele sind spekulativ, solange die betreffenden Grammatiken nicht normiert sind.
0. Auf einen Blick¶
0.1 Ein Plugin ist kein Sonderfall [D]¶
Ein Plugin führt keine eigene Semantikschicht neben Jade ein. Es bündelt und transportiert Dinge, die Jade ohnehin kennt:
| Bestandteil | Rolle | Spezifiziert in |
|---|---|---|
| Root-Namensraum | wo seine Symbole leben | spec-namespaces-und-module |
| Constitution | was herausdarf und was erlaubt ist | spec-constitution-system |
PluginArtifact |
was zwischen Ersteller und Verwender transportiert wird | dieses Dokument |
| CompilerDB-Beiträge | was zur Compile-Zeit registriert oder materialisiert wird | jade-compiler-db-spec |
| Context-/Runtime-Beiträge | worin Laufzeitinstanzen leben und kommunizieren | spec-context-model |
| Fault-/Lifetime-Regeln | wie Ausführung endet und Ressourcen freigegeben werden | jade-fehlermodell |
Das PluginArtifact ist Verpackung und Materialisierungsgrenze, kein zweites Typsystem, kein zweiter Compiler und keine alternative Runtime.
Das ist die Leitlinie für den ganzen Entwurf: Wo das Plugin-System einen eigenen Mechanismus erfinden müsste, obwohl Jade denselben Sachverhalt bereits über Descriptoren, CompilerDB, Contexts, Scheduler, JME oder FaultDomains ausdrücken kann, ist der Plugin-Entwurf vermutlich falsch.
0.2 Was ein Plugin beitragen darf¶
Compiler-Seite Extended-Ops · Passes · QueryProvider · Meta-/Engine-Code
Runtime-Seite Typen · Protokolle · Services · Implementierungen
Artefakt-Seite fertige Module · deferred Generics/Meta · native Payloads
CLI-Seite Commands
Alles ist optional. „Compiler-Plugin“, „Runtime-Plugin“ oder „Format-Plugin“ sind Rollen, die sich aus den Beiträgen ergeben, keine Plugin-Klassen.
0.3 Die harte Linie [N] 12 §4.2¶
Plugins dürfen Extended-Instruktionen ergänzen. Core-Instruktionen dürfen sie weder verändern noch deren Semantik überschreiben.
Geprüft wird das Ergebnis, nicht die Absicht: Nach dem Lowering steht Core da, und das durchläuft den Verifier wie alles andere.
0.4 Die Lifecycle-Idee [D]¶
Aktivierung und Deaktivierung lösen dasselbe Problem aus entgegengesetzten Richtungen:
Aktivierung
vorbereiten → prüfen → atomar sichtbar machen
Deaktivierung
neue Erreichbarkeit schließen → laufende Kopplung abbauen
→ Registrierungen lösen → Ressourcen freigeben
Aktivierung benutzt dafür die StagingTransaction.
Deaktivierung erfindet keine spiegelbildliche „Unstaging“-Transaktion. Nach ACTIVE existieren reale Tasks, Contexts, Bridges, Handles, native Aufrufe und Compiler-Abhängigkeiten. Sie müssen über ihre jeweiligen Eigentümer und Lebenszeitregeln verschwinden.
Darum lautet der Abbau:
Hold ist dabei kein Plugin-Status. Im Jade-Fehlermodell ist ein Hold ein Anspruch auf eine konkrete Ressource, der nur deren Freigabe verhindert. Offene Holds sind der Grund, warum ein Plugin nach dem Detach noch in RECLAIMING verbleiben kann.
Teil I — Was ein Plugin ist¶
1. Artefakt und Manifest¶
1.1 PluginArtifact [N]¶
PluginArtifact ist die einzige Plugin-Distributionseinheit. Ein übersetztes Plugin muss dabei nicht vollständig materialisiert sein: Es schließt alles ab, was beim Ersteller bereits feststeht, und trägt den Rest in einer kontrollierten, später materialisierbaren Form weiter.
// spekulativ — Kategorien normativ, konkretes Binärschema nicht festgelegt
type PluginArtifact : struct {
manifest: PluginManifest
rootModule: ModulePath
modules: [VmModuleArtifact]
deferred: [DeferredCompilerArtifact]
native: [NativeContribution]
}
Ein VmModuleArtifact ist fertige ausführbare Ausgabe für eine bereits vollständig bestimmte Moduleinheit.
DeferredCompilerArtifact ist keine zweite Quellform und kein serialisiertes internes Compilerobjekt, sondern die kanonische, versionierte Artefaktdarstellung für Compile-Zeit-Arbeit, die erst beim Verwender abgeschlossen werden kann.
P-4
[D]EinPluginArtifactist ein teilweise abgeschlossenes Compilerprodukt: fertige und absichtlich deferred Bestandteile dürfen nebeneinander existieren.
1.1.1 Zwei Formen und ein Grund [D]¶
Deferred Inhalt ist nicht eine Sorte Ding. Zwei Fragen lohnt es sich auseinanderzuhalten:
Formen gibt es zwei:
GenericDefinition parametrische Definition samt Materialisierungs-Closure (§1.3)
MetaStructuralForm Compile-Zeit-Code, dessen Auswertung offen ist (§1.4)
Gründe gibt es mehrere, und sie werden deklariert statt erraten:
Consumer-Typen fehlen
lokale Bindings fehlen
Target-/Build-Fakten fehlen
lokale Meta-Eingaben fehlen
P-28
[D]EinDeferredCompilerArtifactträgt genau eine der beiden Formen und einen deklarierten Aufschubgrund. Target-Abhängigkeit ist ein Grund, keine dritte Form.
Das ist nicht Ordnungsliebe. Eine dritte Form wäre ein drittes stabiles Artefaktformat, und für target-abhängigen Maschinencode wäre das genau die vorgelagerte Backend-Repräsentation, die P-7 als Plugin-ABI ausschließt. Der Trennstrich verläuft zwischen „diese Definition ist noch nicht spezialisiert“ und „diese Definition ist fertig, nur noch nicht für dieses Zielsystem übersetzt“. Nur das Erste gehört in ein deferred Artefakt.
Der Aufschubgrund macht außerdem die Ableitung aus §15.4 erst möglich: Ob ein Artefakt beim Consumer eine Compilerinstanz braucht, liest man an den Gründen ab und nicht an einem frei behaupteten Manifest-Flag.
Für v0.1 folgt daraus eine Grenze. Eine vollständig bestimmte, nicht-generische Definition, die nur wegen des Zielsystems noch nicht übersetzt ist, wird nicht deferred transportiert. Sie liegt pro unterstütztem Target als fertiges VmModuleArtifact im Artefakt — oder das Target wird nicht unterstützt. Das ist eine Packaging-Entscheidung und kostet kein neues Format.
1.2 Abgeschlossen oder deferred [D]¶
Die Grenze ist nicht „Source gegen Binary“, sondern welche Fakten bereits feststehen.
nur Plugin-eigene + fest deklarierte Build-Fakten nötig
→ beim Ersteller abschließbar
Consumer-Typen, lokale Bindings, Target-/Build-Fakten
oder lokale Meta-Eingaben nötig
→ deferred
Typische abgeschlossene Bestandteile:
konkrete Runtime-Funktionen
vollständig bestimmte Typen und Provides
bereits materialisierte Descriptor-Ergebnisse
vorgebaute Spezialisierungen
vollständig abgeschlossene Compilerergebnisse, deren Semantik beim Consumer nicht mehr neu ausgewertet wird
Typische deferred Bestandteile:
GenericDefinition öffentliche parametrische Definitionen
MetaStructuralForm Meta-Auswertungen mit lokalen Compile-Time-Eingaben
verbraucherabhängige Spezialisierungen, Constraints und Provides
→ Teil der Materialisierungs-Closure ihrer GenericDefinition, kein eigener Eintrag
Target-Abhängigkeit steht hier bewusst nicht als eigene Sorte. Sie ist ein Aufschubgrund nach §1.1.1, keine Form.
Raw Source kann für Debugging, Rebuilds oder Distribution zusätzlich vorhanden sein, ist aber keine semantische Voraussetzung zum Laden eines Plugins.
1.3 Generische Definitionen und Materialisierungs-Closure [N]¶
Öffentliche parametrische Konstrukte können nicht vollständig beim Ersteller spezialisiert werden, wenn ihre konkreten Argumente erst beim Verwender existieren.
P-5
[N]Exportiert ein Plugin eine beim Verwender spezialisierbare Definition, muss das Artefakt genügend Compilerinformation enthalten, um jede zulässige Spezialisierung ohne Zugriff auf den ursprünglichen Quellbaum zu materialisieren.
Dazu gehört die Materialisierungs-Closure: alle privaten oder öffentlichen Definitionen, Constraints, Provides, Meta-Funktionen und Symbolbeziehungen, die transitiv für die Spezialisierung benötigt werden.
public foo[T]
├── private Helper[T]
├── constraint C[T]
├── provide P for Helper[T]
└── Meta-Funktion M[T]
→ alle vier gehören zur Materialisierungs-Closure von foo[T]
P-29
[N]Die distributierte Darstellung einer solchen Definition heißtGenericDefinition. Sie ist kanonisch, versioniert und unveränderlich, enthält keine lauf-lokalen Identitäten und ist kein internes Compiler-IR.
Normativ muss nur feststehen, dass sie für jede zulässige Spezialisierung ausreicht. Wie der Consumer intern daraus arbeitet — Templates, spezialisiertes IR, direkte Monomorphisierung — ist Implementierungsdetail und kein Vertrag.
Ein Punkt braucht dabei Sorgfalt. GenericDefinition und MetaStructuralForm sind Geschwister im Artefakt, aber die Closure einer generischen Definition enthält Meta-Funktionen. Sie werden deshalb referenziert, nicht eingebettet:
GenericDefinition foo[T]
├── eigener Rumpf
└── kanonische Referenzen auf
├── weitere GenericDefinition-Einträge
└── MetaStructuralForm-Einträge
Sonst läge derselbe Meta-Rumpf zweimal im Artefakt, mit zwei Gelegenheiten, verschieden zu sein — und die Proof-Prüfung aus §13.4.1 hätte zwei Kandidaten, ohne dass jemand sagt, welcher gilt.
Vorgebaute Spezialisierungen dürfen zusätzlich im Artefakt liegen. Sie ersetzen die Closure nicht, solange weitere zulässige Argumente beim Consumer möglich sind.
1.4 Deferred Meta und MetaStructuralForm [D]¶
Deferred Meta ist Compile-Zeit-Code, dessen Auswertung erst beim Consumer stattfinden kann, weil dafür Consumer-Typen, lokale Build-Fakten, Bindings oder andere lokale Compile-Time-Eingaben fehlen.
Dafür braucht das Artefakt eine Ebene, auf der die Totalitätsbedingungen noch direkt prüfbar sind. Ein bereits gelowerter Kontrollflussgraph genügt nicht: Dort ist ein fold nur noch ein Graph mit Kanten, und strukturelle Rekursion sieht von außen wie allgemeine Rekursion aus. Aus dem CFG nachträglich wieder zu beweisen, dass eine Rückwärtskante über eine endliche Menge läuft, wäre eine Analyseaufgabe, die Jade an dieser Stelle gar nicht erst erzeugen muss.
Darum gilt:
P-6
[N]Deferred Meta darf nur ausgeführt werden, wenn für den auszuführenden Meta-Code ein lokal akzeptierter Terminierungs-/Totalitätsnachweis vorliegt. Ein vom Plugin mitgelieferter Proof darf wiederverwendet werden, ersetzt aber nicht die Validierung durch den Consumer.P-7
[D]Die normative distributierte Darstellung von deferred Meta heißtMetaStructuralForm. Sie ist kanonisch, versioniert, unveränderlich und liegt vor dem Lowering in einen allgemeinen CFG oder JBC.
MetaStructuralForm ist keine zweite Quellsprache und kein serialisierter interner AST. Sie ist eine öffentliche Artefaktform mit genau der Struktur, die der Consumer für die Meta-Regeln braucht.
Sie erhält insbesondere sichtbar:
unveränderliche Bindungen
if und match
map / filter / fold über endliche Eingaben
strukturelle Rekursion
direkte Aufrufe anderer Compile-Zeit-Funktionen
`db.*`-Fassadenoperationen, deren Expansion auf reguläre Queries zurückführbar bleibt
Literale und kanonische Symbol-/Typparameter-Referenzen
Sie enthält ausdrücklich nicht:
beliebige Basic-Block-Graphen
freie Sprünge
ungekennzeichnete Rückwärtskanten
lauf-lokale TypeIds oder Arena-Indizes als persistente Identität
Pointer auf Compilerstrukturen
mutable interne Compilerobjekte
Referenzen, die erst beim Consumer auflösbar sind, bleiben als kanonische symbolische Referenzen erhalten. Stage löst sie gegen die lokale CompilerDB auf, bevor der Meta-Rumpf validiert oder ausgeführt wird.
Nach erfolgreicher lokaler Validierung darf der Consumer MetaStructuralForm in seine eigene interne Ausführungsrepräsentation lowern — etwa lokales IR oder JBC für die Compile-Zeit-VM. Diese gelowerte Form ist kein Plugin-ABI-Vertrag und darf sich zwischen Compilerimplementierungen ändern.
Damit bleibt die Artefaktgrenze klein:
PluginArtifact
└── MetaStructuralForm stabile distributierte Compile-Zeit-Form
Consumer intern
└── lokales IR / JBC frei austauschbare Ausführungsform
Die konkrete Proof-Erzeugung und die vollständige Regelmenge gehören jdl-meta-phase. Diese Spec legt nur die notwendige Repräsentation fest, auf der der Consumer sie noch strukturell prüfen kann.
1.4.1 Spurlosigkeit und Artefaktgrenze [D]¶
Die bisherige Formulierung von MP-5 — Compile-Zeit-Code existiere „weder im Bytecode noch in einer VM-Struktur“ — ist ohne Geltungsbereich zu stark. Meta-Code wird zur Compile-Zeit auf einer VM ausgeführt; deferred Meta muss außerdem zwischen Plugin-Ersteller und Consumer transportiert werden.
Für das Plugin-System gilt deshalb die präzisierte Lesart:
Compile-Zeit-Code existiert nicht im ausgelieferten Laufzeitartefakt des Consumer-Programms und nicht als Teil seiner Runtime-Semantik.
Ein PluginArtifact ist an dieser Stelle Compiler-/Distributionsartefakt, nicht das fertige Laufzeitartefakt eines Programms. MetaStructuralForm darin verletzt die Spurlosigkeit daher nicht.
Beim Consumer gilt:
PluginArtifact mit deferred Meta
↓ Stage: validieren
MetaStructuralForm
↓ lokal lowern und ausführen
temporäre Compile-Zeit-Ausführungsform
↓ Materialisierung / Link
Laufzeitartefakt
└── kein deferred Meta-Code
Der Strip-Punkt ist damit die Materialisierung beziehungsweise das Linken des Laufzeitartefakts, nicht das Bauen des Plugins.
Beim Laden zur Laufzeit nach P-10 gibt es kein Linken eines Programmartefakts. Die Grenze verschiebt sich dann auf die Domänengrenze: MetaStructuralForm und ihre lokale Ausführungsform bleiben vollständig auf der Compilerseite, und was die Runtime übernimmt, sind ausschließlich materialisierte Ergebnisse. Meta-Code überquert diese Grenze in keinem der beiden Fälle.
jdl-meta-phase muss MP-5 entsprechend auf das ausgelieferte Laufzeitartefakt eingrenzen. Bis diese Folgeänderung dort steht, ist die ältere wortwörtliche Fassung von MP-5 mit deferred Plugins nicht vereinbar.
1.5 Artefakt-, Compiler- und Cache-Kompatibilität [D]¶
Drei Identitäten dürfen nicht vermischt werden:
interfaceFingerprint
→ beobachtbare Plugin-Oberfläche und Kompatibilität für Nutzer
ArtifactFormatVersion + CompilerContract
→ kann dieser Consumer die distributierte Artefaktdarstellung verstehen?
CompilerDB-Cache-Key
→ darf ein lokal berechnetes Ergebnis wiederverwendet werden?
Der Compilervertrag bindet nicht an eine konkrete Compiler-Buildnummer. Zwei Compilerimplementierungen dürfen dasselbe Artefakt konsumieren, wenn sie denselben versionierten Artefakt-/Semantikvertrag erfüllen.
Die persistente Cache-Identität bleibt Sache der CompilerDB. Dafür wird hier kein zweiter Cache-Begriff eingeführt.
1.6 Native Bestandteile [D]¶
Ein Plugin darf native Bestandteile benötigen. Für Jade auf Linux gibt es zwei zulässige Formen:
bundled
→ das PluginArtifact trägt die passende native Payload, typischerweise eine .so
external
→ das Artifact deklariert eine vom Host bereitzustellende native Abhängigkeit
Beide Formen müssen loader-seitig prüfbar sein. Mindestens relevant sind Target/ABI, Identität beziehungsweise Hash, benötigte Symbole und die JVB-/FFI-Bindungsinformation.
Native Nutzung setzt eine dafür provisionierte FFI-/NativeHost-Resource beziehungsweise einen autorisierten Host-Provider im ausführenden Context voraus. Die konkrete Resource-Form gehört in die JVB-/FFI-Spec; das Plugin-System erfindet dafür keine eigene Capability-Achse.
P-8
[N]Ein Plugin darf keine undeklarierte native Bibliothek zur Laufzeit über zufällige Suchpfade als Teil seiner Semantik entdecken. Native Abhängigkeiten gehören zur Artefakt- oder Dependency-Beschreibung.
1.7 PluginManifest [D]¶
Das Manifest enthält ausschließlich loader-relevante Metadaten. Keine vollständigen Typsignaturen.
// spekulativ
type PluginManifest : struct {
id: PluginId
version: SemVer
artifactVersion: ArtifactFormatVersion
rootNamespace: NamespacePath
modules: [ModulePath]
requires: PluginRequires
provides: PluginProvides
commands: [CommandDecl]
initEntry: Option[ModulePath]
interfaceFingerprint: Fingerprint
}
exports bleibt ausgeschlossen. Die Freigabemenge steht in der Constitution.
Der interfaceFingerprint ist abgeleitet, keine vom Autor gepflegte Behauptung. Er entsteht aus der beobachtbaren Oberfläche und gilt pro Build-Konfiguration.
1.8 Anforderungen und Beiträge¶
// spekulativ
type PluginRequires : struct {
vmVersion: SemVerRange
compilerContract: Option[CompilerContractRange]
intrinsics: [IntrinsicRef]
plugins: [PluginDependency]
}
type PluginProvides : struct {
compilerExtensions: Option[CompilerContributions]
runtimeProvides: Option[RuntimeContributions]
}
compilerContract ist nur relevant, wenn das Artefakt deferred/compilerseitige Inhalte trägt. Es bezeichnet den semantischen Artefaktvertrag, nicht eine konkrete Compiler-Buildnummer.
Das Manifest enthält in v0.1 keine allgemeine Liste von Hostrechten oder Capability-/Resource-Grants. Konkrete privilegierte Voraussetzungen werden dort geprüft, wo sie aus dem Artefakt oder aus materialisierter Semantik tatsächlich feststehen: native Host-Anforderungen aus NativeContribution/JVB-Metadaten, eine benötigte CompilerInstance aus deferred Bestandteilen, weitere Resource-Bindungen in Stage beziehungsweise bei Context-Aktivierung.
Damit wird keine zweite, frei behauptete Autoritätsbeschreibung neben ResourceBindingSpec und den realen Runtime-Resources gepflegt. Sollte der Loader später für bestimmte Resource-Arten eine vorgezogene, abgeleitete Admission-Zusammenfassung benötigen, kann das Packaging-Format sie ergänzen, ohne daraus Runtime-Autorität zu machen.
1.9 Domänenspezifische Aktivierung [D]¶
Compiler- und Runtime-Beiträge verwenden dasselbe Artefakt und denselben Preflight, werden aber in verschiedenen Domänen aktiviert:
- Compiler-Seite: QueryProvider, Passes, Extended-Ops, compilernahe Engines und deferred Materialisierung.
- Runtime-Seite: Plugin-Broker-Eintrag, Context-/Service-/Bridge-Bindungen, materialisierte Runtime-Descriptoren, Protos und Runtime-Registrierungen.
P-10
[D]Die Type Engine ist keine Runtime-Komponente eines aktiven Plugins. Muss ein zur Laufzeit angefordertes Plugin noch deferred Compilerarbeit abschließen, geschieht diese in einer Compilerinstanz vor beziehungsweise neben der Runtime-Aktivierung. Die Runtime konsumiert danach materialisierte Ergebnisse; sie leitet keine Typsemantik neu ab.
Ein Plugin mit nur einer Domäne aktiviert nur diese. Bei beiden bleiben Registrierungen und Lebenszeiten getrennt, auch wenn ein gemeinsamer Aktivierungs-Commit ihre Sichtbarkeit atomar zusammenfassen kann.
2. Die Constitution eines Plugins [D]¶
Ein Plugin bringt seine eigene Constitution mit. Sie legt fest, was aus seinem Namensraum herausdarf und welche Regeln darin gelten.
Das ist die Antwort auf die Frage, die andere Plugin-Systeme über Konvention regeln: Was ein Plugin nach außen gibt, ist geprüft und nicht nur dokumentiert.
Offen (P-O1): Gegen welche Autorität eine Plugin-Constitution validiert wird — Host, Ladepunkt oder Package-Elternteil. Gegen den Host hieße, dass dasselbe Plugin in zwei Projekten verschieden viel darf; gegen eine feste Ebene wäre vorhersehbarer, erschwert aber hostseitige Verschärfung.
Teil II — Klassen und Bootstrap¶
3. Der Seed ist kein Plugin [D]¶
Die alte Fassung beschrieb einen „Bootstrap-Kern“ als eingebettetes Plugin. Das ist verworfen.
3.1 Warum nicht¶
Meta-Werte sind JME-Werte, und Meta-Code ist gewöhnlicher JDL-Code. Beim Übersetzen läuft deshalb bereits eine VM.
Der Seed ist der Punkt davor. Ein in JDL geschriebenes Seed-Plugin bräuchte einen Compiler, um zu entstehen, und der Compiler bräuchte wiederum genau diesen Seed. Ein Kreis wird nicht dadurch zum Bootstrap, dass man ihn enthusiastisch benennt.
3.2 Was stattdessen gilt [D]¶
Der Seed entsteht in Zig über die CompilerDB, ohne JDL-Quelle. Er wird als Input gesetzt; Provider materialisieren daraus die vorgesehenen Descriptoren.
Er trägt mindestens:
Builtins bool, i8..i64, u8..u64, isize, usize, f32, f64, str, Unit, Never
jade::core Option, Result, TypeId, SymbolId, HandleId, MetaRecord, Diagnose-Typen
Seed-Descriptoren Meta-Context nach X-7 sowie weitere Bootstrap-Descriptoren
Intrinsic-Tabelle feste Indizes, Vertrag mit der VM
Option, Result und die vom Compiler selbst benannten Meta-/Identitätstypen stammen aus dem Seed, nicht aus der Stdlib.
3.3 Aufnahmekriterium [N]¶
Ein Typ gehört in den Seed, wenn der Compiler ihn von sich aus benennt, ohne dass der Nutzer den Namen schreibt.
Das gilt für Option, Result und für weitere tatsächlich axiomatisch notwendige Typen. Es gilt nicht für Collections, Operator-Protokolle oder Komforttypen, nur weil sie „grundlegend“ wirken.
3.4 Der Vollständigkeitsprüfer [D]¶
Die CompilerDB kann prüfen, ob ein vorhandener Seed-Eintrag in sich konsistent ist. Sie erkennt nicht automatisch, dass ein notwendiger, noch nirgends referenzierter Eintrag fehlt.
Darum braucht der Seed eine kleine normative Vollständigkeitsliste: Diese Einträge müssen nach dem Seeding existieren, mit dieser Gestalt.
4. Die Stdlib ist ein privilegiertes Plugin [N]¶
Die Stdlib ist das erste Plugin nach dem Seeding und der Wurzelknoten des regulären Plugin-Dependency-Graphen.
Privilegien:
- nicht austauschbar;
- versionierbar;
- darf die reservierten Namensräume
jade::undjdl::befüllen; - ihre Commands sind unqualifiziert (§11).
Grenze: Sie darf jade::core nicht befüllen. Dieser Namespace gehört dem Seed.
Für v0.1 bleibt die Stdlib monolithisch. Eine spätere Aufteilung ist ein Packaging-Refactoring, keine Änderung des Plugin-Modells.
Die Stdlib benutzt denselben Artefaktpfad wie andere Plugins: abgeschlossene und deferred Bestandteile dürfen nebeneinander vorkommen.
5. Externe Plugins [N]¶
Alle anderen Plugins werden über das Projektmanifest oder die CLI referenziert und durchlaufen den vollständigen regulären Lifecycle.
Der Unterschied zu eingebetteten Artefakten betrifft Discovery, Trust und Austauschbarkeit, nicht das Format und nicht die semantischen Prüfungen.
Teil III — Namensraum, Freigabe, Kohärenz¶
6. Root-Namensraum [N]¶
Jedes Plugin deklariert genau einen kanonischen Root-Namensraum. Alle seine Module liegen darunter.
- Ein externes Plugin darf keinen privilegierten fremden Root beanspruchen.
- Seed und Stdlib sind die vorgesehenen Ausnahmen für
jade::core,jade::undjdl::. - Im Normalbetrieb gibt es nie zwei aktive Generationen mit demselben Root-Namensraum.
Der Root begrenzt die Kollisionsfläche strukturell. Ein Plugin erweitert nicht beliebig den globalen Namensraum und hofft anschließend auf gutes Benehmen.
6.1 Generationen [D]¶
Für normales Laden reicht die Identität PluginId. Hot-Reload braucht zusätzlich eine Laufzeitidentität für die konkrete aktivierte Fassung.
P-11
[D]PluginIdbezeichnet das logische Plugin. Eine PluginGeneration bezeichnet eine konkrete Artefaktgeneration dieses Plugins als Lifecycle-Einheit. Sie entsteht mitSTAGEDals Kandidat und wird mit dem Commit zur aktiven Generation.
Die Identität muss vor ACTIVE existieren, nicht erst danach. Init darf nach P-25 Besitz erzeugen, und dieser Besitz braucht einen Eigentümer, der einen Fehlschlag überlebt. Eine Generation, die erst beim Commit entstünde, könnte einen gescheiterten Init nicht aufräumen — es gäbe niemanden, dem die Reste gehören.
Im Normalfall existiert genau eine relevante Generation. Während eines späteren Hot-Replacements dürfen kurzzeitig mehrere Generationen desselben PluginId existieren:
Es bleibt trotzdem genau eine Generation der reguläre Eintrittspunkt für neue Arbeit.
Diese Trennung ist notwendig, weil die alte Generation während Drain und Reclaim noch reale Ressourcen besitzen kann, nachdem die neue bereits aktiv ist.
6.1.1 Wem die Generation gehört [D]¶
Ein reines Compiler-Plugin durchläuft denselben Lifecycle wie jedes andere — STAGED → INIT → ACTIVE und den ganzen Abbau — hat aber keine Runtime-Seite. Ein Eigentümer, der nur Runtime-Generationen führt, könnte diesen Lifecycle nicht halten. Ohne ausdrückliche Zuordnung besäße ihn also niemand.
P-30
[D]DiePluginGenerationgehört dem Loader beziehungsweise der Plugin-Aktivierungsinstanz. Plugin Broker und Compiler-Registries halten jeweils nur eine Projektion dieser Generation, keine eigene Lifecycle-Identität.
PluginGeneration
Loader / Aktivierung
│
┌───────────┴───────────┐
▼ ▼
Compiler-Projektion Runtime-Projektion
CompilerDB / Registries Plugin Broker
Ein compiler-only Plugin hat nur die linke Projektion, ein runtime-only Plugin nur die rechte, ein gemischtes beide. Der Lifecycle bleibt in allen drei Fällen genau einer, und der gemeinsame Commit aus §13.6 koordiniert die Projektionen statt zwei getrennte Lebenszyklen zu synchronisieren.
Das passt zur bereits getroffenen Aufteilung in §1.9: Verschieden sind die Domänen der Beiträge, nicht die Identität des Plugins.
7. Freigabe statt Exportliste [D]¶
Was ein Plugin nach außen gibt, steht in seiner Constitution, nicht im Manifest.
Die Freigabe darf namentlich oder über Muster beschrieben werden; der materialisierte ConstitutionDescriptor hält die aufgelöste Menge. Der Fingerabdruck entsteht daraus.
8. Coherence über Plugin-Grenzen¶
8.1 Package-Begriff [N]¶
Für Plugin-Coherence gilt: gleiches Package = gleiche Plugin-ID. Nicht Namensraumpräfix, Verzeichnisname oder Konvention.
Seed, jade:: und jdl:: bilden für Coherence ein gemeinsames Core-Package.
8.2 Die Regel [D]¶
Ein provide P for T ist zulässig, wenn P oder T im selben Package definiert ist.
Das erlaubt sowohl „mein Typ für dein Protokoll“ als auch „mein Protokoll für deinen Typ“, ohne die globale Kohärenz aufzugeben.
8.3 ForeignExtensible [D]¶
Gehören weder P noch T dem Plugin, ist eine Implementierung nur zulässig, wenn das Protokoll die Fremderweiterung ausdrücklich erlaubt.
8.4 Geltungsbereich der Eindeutigkeit [D]¶
Die Regel „höchstens ein provide P for T“ braucht in einem laufenden Plugin-System einen Geltungsbereich, der sich aus tatsächlicher Auflösungserreichbarkeit ergibt. Ein rein buildzeitlicher Verbundbegriff reicht dafür nicht.
P-21
[D]Die(P, T)-Eindeutigkeit gilt über alle Provide-Beiträge, die in derselben CompilerDB-Instanz für Auflösung gleichzeitig erreichbar sind.
Für eine gewöhnliche Stage umfasst dieser Geltungsbereich:
Seed / Core-Package
Stdlib
regulär ACTIVE PluginGenerations dieser Compilerinstanz
eigene StagingTransaction
Namensauflösung bleibt trotzdem an deklarierte Dependencies und Namespace-Regeln gebunden. Die Kohärenzprüfung ist weiter gefasst, weil zwei gleichzeitig registrierte Implementierungen nicht erst dann mehrdeutig werden, wenn beide zufällig in derselben Quelldatei importiert wurden.
Eine Generation, deren Compiler-Beiträge beim Detach für neue Auflösungen nicht mehr auswählbar sind, verlässt damit den Eindeutigkeitsbereich. Ein vorher verbotenes Duplikat kann danach zulässig werden, ohne dass eine Sonderregel nötig ist.
Für Replacement gilt die Maskierungsregel aus §13.4: Die zu ersetzende Generation nimmt nicht an der Namens- und Provide-Auflösung des Kandidaten teil, bleibt aber für den Kompatibilitätsvergleich lesbar.
Zwei im so definierten Geltungsbereich gleichzeitig gültige Implementierungen für dieselbe (P, T)-Kombination sind ein harter Fehler.
8.5 Newtype-Ausweichpfad [N]¶
Wer fremdes Verhalten für einen fremden Typ braucht und kein Opt-in besitzt, nimmt einen Newtype.
Teil IV — Beiträge¶
9. Compiler-Beiträge [D]¶
Das Plugin-System führt keine zweite Compilerpipeline ein.
CompilerContributions
├── Extended-Ops + Lowering Adapter
├── Passes
└── QueryProvider
└── EngineProvider
9.1 Extended-Ops¶
Eine Extended-Instruktion braucht einen Lowering-Pfad zu Core. Der Core bleibt unverändert.
Nummern oder persistente Identitäten werden nicht aus Registrierungsreihenfolge abgeleitet. Was in Artefakten referenziert wird, braucht eine stabile, versionierte Identität.
9.2 Passes¶
Plugin-Passes benutzen die bestehende Pass-/Verifier-Pipeline.
Die Plugin-Spec besitzt nicht das Vokabular der Pass-Bedingungen. Ein Pass deklariert Preconditions, Postconditions und Invalidations in der Form, die IR-/Pipeline-Spec festlegt.
Es gibt keinen Plugin-eigenen PipelinePlan und keine zweite Pass-DAG.
9.3 QueryProvider und EngineProvider¶
Ein Plugin kann Provider für bestehende Derived Queries registrieren.
EngineProvider sind eine spezialisierte Provider-Rolle, kein zweites Registrierungsmodell.
Provider-Auswahl muss eindeutig sein. Änderungen an der aktiven Providermenge sind CompilerDB-relevante Inputs und müssen sich in Dependency-Tracking und Invalidierung niederschlagen. Ein Plugin darf nicht durch eine Registry-Hintertür das Query-System deterministisch aussehen lassen, während sich die Provider hinter dessen Rücken ändern.
QueryProvider erweitern das Query-Substrat, nicht die bevorzugte Benutzerschnittstelle. Meta-Code, Engines und Werkzeuge konsumieren im Regelfall die kuratierte CompilerDB-Fassade (db.*). Fassadenoperationen dürfen mehrere primitive/derived Queries komponieren, müssen deren Query-Identität, Dependency-Kanten, Caching und Provenance aber sichtbar erhalten. Eine Fassadenoperation darf deshalb nicht durch privaten Direktzugriff auf Compilerzustand zu einer undurchsichtigen Superquery werden.
Für Plugins folgt daraus: Ein Provider registriert eine klar benannte Query-Semantik; höhere db.*-Operationen können sie mit anderen Queries komponieren. Plugin-Code muss die rohe Registry- oder Endpoint-Struktur der CompilerDB nicht kennen.
Semantic Views und Proofs erzeugen keine neue Beitragsklasse. Eine Semantic View ist, sofern der zugehörige Query-Vertrag existiert und im Geltungsbereich zulässig ist, eine gewöhnliche Derived Query mit gewöhnlichem QueryProvider. Ein Proof-Provider ist ebenfalls normale Compilersemantik über Queries. Das Plugin-System führt dafür weder ViewProviderRegistry noch ProofRegistry ein.
Diese Einordnung gibt einem Plugin nicht automatisch das Recht, Core-Sicherheitsurteile zu ersetzen. Für sealed/core Proof-Verträge muss jade-compiler-db-spec beziehungsweise die zuständige Fachspec festlegen, welche Provider autorisiert sind. Die Eindeutigkeitsregel bleibt unberührt: Für denselben Derived-Query-Vertrag gibt es pro Stage-Profil genau einen aktiven Provider. Mehrere logische Proof-Regeln müssen innerhalb dieses Providers oder über getrennte Query-Verträge komponiert werden. Ebenso registriert ein Plugin-Konstrukt nicht eigenmächtig einen zweiten Core-View-Provider; sofern der Core-View-Vertrag erweiterbar ist, beteiligt es sich über die dafür vorgesehene JDL-seitige provide-/Dispatch-Schnittstelle.
Ob externe Plugins neue Query-Verträge selbst definieren dürfen, ist keine Plugin-Sonderregel und wird hier nicht neu entschieden. Falls jade-compiler-db-spec dies für einen Geltungsbereich erlaubt, gelten dieselben Schema-, Versions-, Namespace-, Determinismus- und Providerregeln wie für andere Queries.
Das Plugin-System normiert außerdem keine universelle Form eines Proof-Ergebnisses. Label-, Totalitäts- oder andere Proof-Queries behalten ihre fachlich definierte Judgement-Struktur.
9.4 Frontend-Parser gegen Datenparser [D]¶
„Parser“ bezeichnet zwei unterschiedliche Rollen:
JDL-Frontend-Provider
→ nimmt an der Übersetzung von JDL teil
→ kann Syntaxoberfläche beeinflussen
→ unterliegt §10 und der Constitution
Daten-/Formatparser
→ liest Markdown, TOML, YAML, JSON, ...
→ erzeugt Daten oder Syntaxbäume
→ erhält dadurch keinerlei JDL-Frontend-Autorität
Frontend-Autorität entsteht aus dem registrierten Query-/Frontend-Vertrag, nicht aus einem Namen wie ParserEngine.
10. Syntaxerweiterung ist Sugar [D]¶
Ein Plugin darf Syntaxoberfläche ergänzen.
Regel P-1. Eine Erweiterung darf die Oberfläche verändern, nicht die Bedeutung. Was sie erzeugt, muss ausdrückbares JDL sein, das alle gewöhnlichen Gates unverändert durchläuft.
Der Prüfstein ist einfach: Könnte man die erzeugte JDL-Form von Hand hinschreiben und bekäme dieselben Prüfungen und dieselbe Semantik? Dann ist es Sugar. Wenn nicht, wurde eine zweite Sprache durch die Hintertür eingeführt.
10.1 Grammatik als Blueprint [D]¶
Für v0.1 gilt:
Bei einem Datenformat ist der Output eine Datenstruktur. Bei einer JDL-Syntaxerweiterung muss ausdrückbares JDL herausfallen.
Ob ein Descriptor später optional zu spezialisiertem Parser-Code kompiliert werden darf, ist eine Optimierungsfrage und offen.
10.2 Was die Constitution nicht kann¶
Die Constitution kann prüfen, was der Compiler weiß. Sie kann nicht beweisen, dass eine Syntaxerweiterung lesbar, geschmackvoll oder für Kollegen zumutbar ist.
Das ist kein Verifierproblem. Menschliche Kreativität bleibt eine ausreichend leistungsfähige Quelle für Dialekte.
10.3 Herkunft durchreichen [O]¶
Generierte beziehungsweise umgeschriebene JDL-Formen müssen ihre Source-Provenance bis zu Diagnostics sinnvoll weiterreichen. Die konkrete Form ist offen.
11. Commands [D]¶
Ein Plugin darf Commands für das jade-Binary beisteuern.
Regel P-2. Plugin-Commands sind über den Plugin-Namen qualifiziert:
jade myplugin mycommand. Commands der Stdlib sind unqualifiziert:jade build.
Damit kann ein externes Plugin einen Stdlib-Command nicht überschreiben.
Command-Namen und Optionen stehen im Manifest, damit Discovery und --help ohne Ausführung des Plugins möglich sind.
Ein Plugin, dessen Name mit einem reservierten unqualifizierten Stdlib-Command kollidiert, wird abgelehnt.
Offen (P-O2): Welcher Resource-/Context-Vertrag für bewusst vom Nutzer gestartete Tool-Commands gilt.
12. Plattform-Plugins [D]¶
Ein Plattform-Plugin ist kein gewöhnliches additives Plugin, sondern eine Implementierung eines darüberliegenden gemeinsamen Vertrags.
Regel P-3. Der Plattformvertrag steht in der Stdlib, nicht im Plattform-Plugin.
Die Stdlib deklariert Protokolle, Typen und Signaturen. Das Plattform-Plugin liefert die Implementierung.
Es darf pro Vertrag nur eine aktive Auswahl geben. Plattformspezifische Zusatzfähigkeiten stehen separat daneben; sie dürfen den gemeinsamen Vertrag nicht still erweitern.
Teil V — Lifecycle¶
13. Statusmodell [D]¶
Der Lifecycle trennt die Vorbereitung eines Plugins von seiner späteren Ausführung und den Abbau wiederum von der Freigabe seiner Ressourcen.
Aktivierung
DISCOVERED → LOADED → VERIFIED → STAGED → INIT → ACTIVE
Abbau einer aktiven Generation
ACTIVE → DRAINING → DETACHING → RECLAIMING → UNLOADED
Fehlschlag vor ACTIVE
LOADED / VERIFIED → FAILED
STAGED → FAILED
INIT → DETACHING → RECLAIMING → FAILED
Die drei Fehlerformen unterscheiden sich darin, wie viel bereits existiert:
LOADED / VERIFIED
→ FAILED
keine Plugin-Ausführung wurde begonnen
STAGED
→ FAILED
StagingTransaction verwerfen;
kurzlebige Compiler-/Ausführungsressourcen gehören ihren normalen Ownern
INIT
→ DETACHING → RECLAIMING → FAILED
weil Init bereits Contexts, Tasks, native Ressourcen oder Holds erzeugt haben kann
vor ACTIVE
→ BLOCKED
notwendige Dependency ist FAILED/BLOCKED
STAGED bezeichnet den Zustand einer StagingTransaction. Sie ist keine Jade-Ausführungsdomäne und damit kein Context.
DRAINING ist der Abbauzustand einer zuvor ACTIVE Generation. DETACHING und RECLAIMING werden zusätzlich für einen Kandidaten benutzt, dessen INIT scheitert, obwohl er ACTIVE nie erreicht hat.
Ein solcher Kandidat durchläuft ausdrücklich kein DRAINING. DRAINING existiert, um fremden Eintritt zu schließen — und ein Kandidat war nie regulär erreichbar: Niemand außerhalb konnte ihn auflösen, binden oder aufrufen. Was er an eigener Arbeit gestartet hat, wird innerhalb von DETACHING beendet, in derselben Reihenfolge nach F-25: erst unzugänglich machen, dann das Ende der tatsächlichen Kopplung abwarten, dann die Kante entfernen.
Das Fehlerergebnis einer Aktivierungsoperation darf dem Aufrufer bereits feststehen, während der Kandidat intern noch in DETACHING oder RECLAIMING auf sicheren Rückbau wartet. Ein offener Hold wird nicht dadurch ungefährlich, dass die Aktivierung ohnehin schon gescheitert ist.
P-12
[D]Holdist kein Plugin-Lifecycle-Zustand. Ein Hold liegt nachjade-fehlermodellauf einer konkreten Ressource. Er darf den Reclaim dieser Ressource verzögern, nicht die Bedeutung eines ganzen Plugins ersetzen.
13.1 Discovery¶
Quellen in v0.1:
Remote-Registry bleibt Phase 2.
Das Projektmanifest ist die Quelle der Wahrheit für Plugin-Abhängigkeiten. Die CLI ist eine Bedienoberfläche dafür, keine parallele Dependency-Datenbank.
13.2 Load¶
Load validiert das äußere Artefaktformat und liest Manifest sowie loader-relevante Inhaltsverzeichnisse.
Noch läuft kein Plugin-Code und noch entsteht keine globale semantische Sichtbarkeit.
13.3 Verify — Preflight ohne Type Engine¶
Verify arbeitet auf Manifest- und Artefaktmetadaten:
- Integrität, in v0.1 mindestens Hash; Signaturen später.
ArtifactFormatVersionund gegebenenfallsCompilerContract.- VM-Kompatibilität gegen
requires.vmVersion. - Target-/ABI-Kompatibilität nativer Payloads und deklarierter nativer Dependencies.
- Intrinsics-Verfügbarkeit.
- Dependency-DAG: ladbar, keine Versionskonflikte, keine Zyklen.
- Root-Namensraum-Konflikte.
- Command-Namenskonflikte.
- strukturelle Vollständigkeit der deferred Materialisierungs-Closure.
- Schemaversion aller enthaltenen
DeferredCompilerArtifact-Formen:GenericDefinitionundMetaStructuralFormmüssen von diesem Consumer verstanden werden. - Nur bei Runtime-Load: Verfügbarkeit beziehungsweise zulässige Provisionierung einer
CompilerInstance-Resource nach P-26, sofern deferred Bestandteile consumer-seitig materialisiert werden müssen.
Punkt 10 prüft Form, Version und Wohlgeformtheit — nicht, was in der Form steht. Die semantische Arbeit braucht aufgelöste Referenzen und bleibt in Stage: die Struktur- und Totalitätsprüfung deferred Metas nach §13.4.1, die Materialisierung generischer Definitionen nach §13.4.
Beide Formen sind nach P-28 und P-29 versioniert. Ohne diesen Prüfpunkt gäbe es eine normativ versionierte Artefaktform, deren Version im Lifecycle nirgends abgelehnt werden kann — und der Consumer merkte erst mitten in einer offenen Transaktion, dass er das Artefakt gar nicht lesen kann.
Punkt 11 gilt ausdrücklich nur beim Laden zur Laufzeit. Bei einem gewöhnlichen Build läuft bereits eine Compilerinstanz im Build-System; es gibt keine neu zu provisionierende Runtime-Resource, deren Erreichbarkeit geprüft werden müsste. P-26 ist eine Admission-Regel für Runtime-Load und keine allgemeine Artefaktregel.
Wo die Regel greift, verhindert sie den unangenehmsten Fall: ein Plugin, das erst nach dem Öffnen einer Transaktion daran scheitert, dass in diesem Zielzustand gar nicht übersetzt werden darf.
Verify macht keine semantische Type-Engine-Arbeit. Dafür fehlen bewusst Consumer-spezifische Fakten; genau deshalb gibt es Stage.
Bei Fehler: FAILED. Kein Plugin-Code läuft, kein globaler Zustand ändert sich.
Eine spätere Replacement-Operation darf beim Namespace-Check die zu ersetzende Generation desselben logischen Plugins als kontrollierten Sonderfall benennen. Das ist keine allgemeine Lockerung der Root-Invariante.
13.4 Stage — transaktional¶
Vorbedingung: Alle notwendigen Dependencies sind ACTIVE.
Stage eröffnet eine StagingTransaction.
Für die verschiedenen Prüfungen sind drei Sichten zu unterscheiden. Sie sind absichtlich verschieden weit, weil sie verschiedene Fragen beantworten.
Resolution View — woran Namen und Provides des Kandidaten gebunden werden
eigene staged Beiträge
Seed / Core und Stdlib
deklarierte ACTIVE Dependencies
nicht: Beiträge anderer gleichzeitig gestagter Plugins
nicht: Beiträge nicht deklarierter ACTIVE Plugins
Coherence View — worüber die (P, T)-Eindeutigkeit nach §8.4 geprüft wird
Resolution View
+ alle weiteren gleichzeitig resolver-erreichbaren ACTIVE Provides
derselben CompilerDB-Instanz
Die Coherence View ist weiter als die Resolution View, und das ist kein Versehen. Ein Kandidat darf ein fremdes provide nicht benutzen, das er nicht deklariert hat — aber er darf es auch nicht doppeln. Benutzung ist eine Frage der Dependencies, Eindeutigkeit eine Frage der Erreichbarkeit.
Replacement Compare View
Nur bei einem Replacement darf zusätzlich die zu ersetzende Generation gelesen werden — aber ausschließlich als Vergleichsgegenstand:
P-22
[D]Die zu ersetzende Generation ist für Namensauflösung, Provide-Auswahl und Meta-Auflösung des Kandidaten maskiert. Ihre Vergleichsmetadaten bleiben lesbar.
Damit kann N+1 gegen N auf Kompatibilität geprüft werden, ohne dass N versehentlich zur semantischen Dependency von N+1 wird oder die eigene alte Provide-Menge jeden Replacement-Versuch als Duplikat blockiert.
Innerhalb der Transaktion werden, soweit nötig:
fertige Descriptor-/Artefaktergebnisse validiert und eingebunden
deferred Definitionen für spätere Materialisierung registriert
MetaStructuralForm referenziell aufgelöst und lokal validiert
deferred Meta gegen lokale Compile-Time-Fakten ausgewertet
Compiler-Beiträge auf Eindeutigkeit und Verträglichkeit geprüft
Coherence und Constitution geprüft
Context-/Binding-Beschreibungen geprüft
Patches und Registry-Änderungen gesammelt
Öffentliche Generics werden nicht vorsorglich vollständig spezialisiert. Stage registriert ihre materialisierbare Definition.
Alles, was während Stage entsteht, gehört bis zum Commit derselben Transaktion. Es gibt keinen Nebenpfad, auf dem ein erzeugter Descriptor oder eine Registry-Änderung vorher global sichtbar wird.
Bei semantischem Fehler wird die gesamte Transaktion verworfen. Falls bereits Init begonnen hatte, gilt stattdessen §13.5: Runtime-Besitz wird nicht durch das Verwerfen von Compiler-Patches beseitigt.
13.4.1 Meta-Validierung und Ausführung [D]¶
Die lokale Proof-Prüfung geschieht zu Beginn von Stage, bevor irgendein deferred Meta-Rumpf ausgeführt wird.
Die Reihenfolge ist:
1. MetaStructuralForm auf kanonische Kodierung und Wohlgeformtheit prüfen
(die Schemaversion wurde bereits in Verify geprüft)
2. symbolische Call-/Typreferenzen gegen die lokale CompilerDB auflösen
3. erlaubte strukturelle Konstrukte und Totalitätsbedingungen prüfen
4. mitgelieferten Proof validieren oder lokalen Proof erzeugen
5. erst danach in eine lokale Ausführungsform lowern
6. Meta-Code ausführen
Damit liegt die Prüfung spät genug, um Consumer-spezifische Aufrufziele zu kennen, aber früh genug, dass kein fremder Meta-Code vor erfolgreicher Validierung läuft.
P-23
[D]Ein ungültiger oder lokal nicht akzeptierter Terminierungs-/Totalitätsnachweis beendet Stage vor der ersten Meta-Ausführung.
Meta konstruiert ausschließlich über die vorgesehene CompilerDB-Fassade. Schreibende Fassadenoperationen lowern auf die normalen Patch-/Materializer-Wege; einen privaten Direktpfad in globalen Compilerzustand gibt es nicht.
13.4.1.1 Was der Nachweis abdeckt — und was nicht [D]¶
Der strukturelle Nachweis deckt den lokalen Kontrollfluss eines Meta-Rumpfes ab: zulässige Schleifenformen, strukturelle Rekursion mit nachweisbarer Abnahme, Iteration über endliche Eingaben — und transitiv die direkt aufgerufenen Compile-Zeit-Funktionen.
Er deckt nicht ab, was über db.* passiert. Eine db.*-Operation kann einen anderen Provider auslösen, und dieser kann wiederum Meta-Code sein. Ob das terminiert, hängt dort nicht am Rumpf, sondern daran, ob der Query-Graph einen Zyklus bildet.
Dafür existiert bereits ein Mechanismus, und er sitzt an der richtigen Stelle: Die CompilerDB behandelt Query-Zyklen als Fehler und liefert ein strukturiertes Diagnostic mit Query-Trace (jade-compiler-db-spec §7.3). Ein Zyklus wird erst zur Auswertungszeit sichtbar; ihn statisch beweisen zu wollen wäre derselbe Fehler wie die CFG-Rekonstruktion aus §1.4.
P-27
[D]Der Totalitätsnachweis gilt für den lokalen Kontrollfluss des Meta-Codes. Terminierung überdb.*-Grenzen hinweg wird nicht statisch bewiesen, sondern von der Zyklusbehandlung der CompilerDB getragen. Erst beide zusammen ergeben die Zusage, dass Meta nicht hängenbleibt; keiner von beiden allein tut das.
Praktisch heißt das: Ein kaputtes oder bösartiges Plugin bekommt eine Diagnose — entweder über unzulässigen Kontrollfluss oder über einen Query-Zyklus — statt die Übersetzung stillstehen zu lassen.
Total heißt allerdings nicht schnell. Eine terminierende, aber sehr teure Meta-Auswertung ist ein Betriebsproblem, kein Semantikproblem, und fällt unter den operativen Abbruch aus §13.4.2.
13.4.2 Ausführung, Fault-Isolation und Provider-Faults [D]¶
StagingTransaction und Ausführungs-Context sind getrennt.
Wenn Stage oder Init JDL-Code ausführen muss, läuft dieser Code in einer materialisierten Ausführungsdomäne.
Ob diese Domäne gegenüber Compiler oder Host fault-isoliert ist, ergibt sich aus dem normalen Fault-Modell:
separater Context
+ ausschließlich fault-safe Beziehungen
→ eigene FaultDomain möglich
fault-coupling Beziehung
→ gemeinsame FaultDomain
→ keine lokale Isolationszusage
Das Plugin-System darf eine lokale Trap-Grenze nicht behaupten, wenn die materialisierten Beziehungen sie nicht tragen.
Ein zweiter Ausführungs-Context ist keine zweite CompilerDB. Compilerwissen fließt ausschließlich über die vorgesehenen CompilerDB-/Meta-Zugänge.
Wenn ein fault-isolierter Provider- oder Meta-Eintritt faultet, wird der Trap selbst nicht zu einem JDL-Wert gemacht. Die überlebende Compilerseite beobachtet vielmehr, dass der fremde Eintritt keine gültige Antwort geliefert hat, und muss entscheiden, ob daraus Compilerwissen entstehen darf.
Darum werden zwei Fälle getrennt:
deterministischer Provider-Fault
der Fault folgt reproduzierbar aus Provider-Version + Query-Eingaben
→ äußerer Compiler erzeugt ein Diagnostic über den Provider-Fault
→ dieses Diagnostic darf als Query-Ergebnis cachebar sein
operativer Abbruch
CompilerDB-Cancellation / Supersede
externer Shutdown
transiente Ressourcen-/Admission-Störung
→ kein QueryResult publizieren
→ keinen neuen Cacheeintrag erzeugen
→ StagingTransaction abbrechen
(hatte Init bereits begonnen, gilt zusätzlich §13.5)
Fatal Faults folgen ausschließlich dem allgemeinen Fault-Modell und werden hier nicht in Diagnostics umgedeutet.
P-24
[D]Cachebar ist die deterministische Compilerdiagnose über einen Provider-Fault, nicht der Trap als Sprachwert. Operative Abbrüche beantworten die Query nicht und dürfen deshalb keinen QueryResult publizieren.
Die konkrete Klassifikation und Publikationsmechanik gehört jade-compiler-db-spec; diese Spec legt nur fest, welche der beiden semantischen Folgen der Plugin-Lifecycle erwartet.
13.5 Init¶
Init läuft vor Commit unter derselben StagingTransaction.
Ein Plugin ohne initEntry hat nichts auszuführen. INIT wird dann durchlaufen, ohne dass Plugin-Code läuft; es entsteht kein Besitz, und der Rückbaupfad weiter unten bleibt leer.
Er darf internen Zustand vorbereiten, aber noch keine globale Sichtbarkeit herstellen.
In v0.1 darf Init keine irreversiblen externen Seiteneffekte auslösen. Sonst wäre „Aktivierung ist fehlgeschlagen“ nur eine lokale Behauptung über eine Außenwelt, die längst verändert wurde.
Laufzeitinstanzen, die Init vorbereitet, leben in den dafür vorgesehenen Contexts. Die StagingTransaction ist kein Lebenszeitcontainer.
P-25
[D]Alles, was Init mit eigener Lebenszeit erzeugt — Contexts, Tasks, EngineInstances, native Ressourcen, Handles oder Holds — muss bereits beim Erzeugen einer konkreten Kandidaten-PluginGenerationbeziehungsweise einem normalen Runtime-Owner zugeordnet sein.
Damit kann ein Init-Fehler denselben Besitzpfad benutzen wie ein späterer Unload.
Scheitert Init:
1. Commit wird dauerhaft ausgeschlossen.
2. Compiler-Patches und noch nicht veröffentlichte Registry-Änderungen
der StagingTransaction werden verworfen.
3. init-eigene Eintrittsmöglichkeiten werden geschlossen.
4. der Kandidat geht nach DETACHING.
5. seine laufende init-eigene Arbeit wird regulär beendet oder gecancelt.
6. Beziehungen und Registrierungen werden gelöst.
7. der Kandidat geht nach RECLAIMING.
8. Ressourcen werden freigegeben, sobald ihre Holds enden.
9. terminaler Lifecycle-Zustand: FAILED.
Eine gescheiterte Aktivierung kann deshalb in RECLAIMING verbleiben, obwohl ihr Fehler bereits bekannt ist. Das ist kein unvollständiger Rollback, sondern die ehrliche Trennung zwischen „wird nicht aktiv“ und „alles ist bereits freigegeben“.
Für Hot-Reload folgt daraus: Scheitert Init von N+1, bleibt N vollständig ACTIVE; nur der Kandidat N+1 durchläuft seinen eigenen Rückbau.
13.6 Commit¶
Commit macht die vorbereiteten Beiträge semantisch gemeinsam sichtbar:
Descriptor-/CompilerDB-Patches
Provider-/Pass-/Extended-Op-Registrierungen
Runtime-/Service-/Bridge-Registrierungen
Lifecycle der PluginGeneration und ihrer Domänenprojektionen
Die Implementierung darf mehrere interne Stores koordinieren. Semantisch gibt es aber keinen erfolgreich aktivierten Zustand, in dem nur die Hälfte des Plugins sichtbar ist.
Status danach: ACTIVE.
13.7 Aktivierungsreihenfolge [N]¶
Aktivierung erfolgt in v0.1 strikt sequenziell in topologischer Dependency-Reihenfolge.
Dependencies werden vor Dependents aktiviert.
Ein Fehler blockiert nur noch nicht aktive Dependents. Bereits aktive Plugins werden nicht rückwirkend in einen erfundenen BLOCKED-Zustand versetzt; Laufzeitfehler aktiver Contexts unterliegen dem normalen Context-/Fault-Modell.
14. Deaktivierung und Unload [D, Implementierung Phase 2]¶
Die architektonische Semantik des Unloads wird hier festgelegt. Die vollständige Implementierung bleibt Phase 2.
Das ist absichtlich getrennt: Man muss heute wissen, welche Zustände und Invarianten die Architektur tragen soll, ohne deshalb morgen schon dlclose() auf alles zu werfen, das nicht schnell genug wegläuft.
14.1 Unload ist kein rückwärts abgespielter Commit¶
Vor ACTIVE besitzt das Plugin nur vorbereiteten, noch nicht global sichtbaren Zustand. Darum kann eine fehlgeschlagene Stage verworfen werden.
Nach ACTIVE können existieren:
laufende Tasks
Context-Instanzen
Service-Bindungen
Bridge-Beziehungen
EngineInstances
JME-Ressourcen und Handles
native Aufrufe und Callbacks
CompilerDB-Auswertungen
Provider-/Pass-/Extended-Op-Registrierungen
Consumer-Artefakte, die aus Plugin-Compilerarbeit entstanden sind
Diese Dinge können nicht atomar „ungeschrieben“ werden.
P-13
[D]Aktivierung verhindert halbfertige Sichtbarkeit durch Staging. Deaktivierung verhindert halbfertiges Verschwinden durch schrittweisen Entzug von Erreichbarkeit, Kopplung und Besitz.
14.2 Dependency-Closure und Reihenfolge [D]¶
Ein normales Unload darf keine aktive Dependency unter einem aktiven Dependent wegziehen.
Darum gilt:
Ein Unload-Vorgang darf entweder:
- abgelehnt werden, solange aktive Dependents existieren, oder
- eine explizit geplante Deaktivierungs-Closure umfassen.
Innerhalb dieser Closure wird in umgekehrter topologischer Reihenfolge abgebaut.
P-14
[N]Außerhalb einer atomaren Replacement-Operation darf keineACTIVEGeneration von einer Generation abhängen, dieACTIVEbereits verlassen hat.
14.3 DRAINING — neue Erreichbarkeit schließen, bestehende Arbeit beenden [D]¶
Der Übergang ACTIVE → DRAINING schließt zuerst den Eintritt für neue Arbeit.
Das bedeutet für die betroffenen Beiträge sinngemäß:
keine neue Service-Auflösung auf diese Generation
keine neuen Bridge-Eintritte zu dieser Generation
keine neuen plugin-eigenen Top-Level-Tasks
keine neue Provider-Auswahl für zu entziehende Compiler-Beiträge
keine neue Bindung an zu entfernende Runtime-Registrierungen
Bestehende legitime Arbeit darf zunächst weiterlaufen.
Damit wird die Menge der noch laufenden Kopplungen endlich: Es kann nichts Neues mehr nachkommen, während man versucht, das Alte abzubauen.
Diese Reihenfolge ist keine Plugin-Sonderregel. Sie ist die Anwendung von jade-fehlermodell F-25: Beim Abbau wird eine Beziehung zuerst unzugänglich gemacht, dann das Ende der tatsächlichen Kopplung abgewartet und erst danach die Kante entfernt.
14.4 Drain, Root-Nursery, Cancellation, Throw und Trap [D]¶
Wie lange bestehende Tasks auslaufen dürfen, ist Context-/Scheduler-Policy.
Ein Plugin besitzt dafür keine zweite Taskverwaltung. Laufende JDL-Arbeit hängt über die normale Ownership-Kette an den Root-Nurseries ihrer ausführenden Contexts. Drain und Cancellation arbeiten deshalb über dieselbe strukturierte Task-Ownership, die auch ohne Plugins gilt; der Broker durchsucht nicht den Prozess nach „Plugin-Threads“.
Eine PluginGeneration ist dabei weder ein Context noch automatisch eine FaultDomain. Sie kann mehrere Contexts umfassen, und deren Fault-Kopplung ergibt sich ausschließlich aus den materialisierten Beziehungen. Der Lifecycle gruppiert Besitz und Registrierungen; er erfindet keine neue Trap-Grenze.
Konzeptionell kann eine Shutdown-Policy vollständig auslaufen lassen, eine Deadline setzen oder verbleibende Arbeit früh zur Cancellation freigeben. Die konkrete Syntax gehört nicht in diese Spec.
Vier Dinge bleiben getrennt:
Throw[E]
→ fachlicher, deklarierter Fehler des Plugin-Codes
Cancelled
→ Scheduler-/Lifecycle-Ergebnis einer beendeten Task
Trap
→ Runtime-/Policy-/Invariantenverletzung, nicht über JDL catchbar
Fatal
→ Verlust der Prozessintegrität nach dem Fault-Modell
P-15
[D]Ein geplanter Plugin-Unload wird nicht alsThrow[PluginUnloaded]und nicht als absichtlich erzeugter Trap modelliert.
Neue Aufrufe, die nach Schließen des Eintritts auf eine dynamisch nicht mehr verfügbare Oberfläche treffen, folgen dem Vertrag dieser Oberfläche beziehungsweise des Context-Gates. Ein alter, nach Detach ungültiger Handle ist etwas anderes: Seine Verwendung verletzt die Runtime-Gültigkeit und kann regulär trappen.
Ein Trap während Drain wird nach jade-fehlermodell behandelt. Das Plugin-System fängt ihn nicht als besondere „Unload-Ausnahme“ ab. Ebenso macht ein Trap in einem einzelnen Plugin-Context nicht automatisch die ganze PluginGeneration zu einer FaultDomain: Welche Contexts gemeinsam faulten, entscheidet der Coupling-Graph.
14.5 DETACHING — Beziehungen und Registrierungen lösen [D]¶
DETACHING beginnt erst, wenn die legitime laufende Kopplung, die eine konkrete Beziehung benötigt, beendet ist oder kontrolliert beendet wurde.
Jetzt werden die Beziehungen und Registrierungen entfernt, die die Generation aktiv erreichbar gemacht haben:
Runtime
Service-/Bridge-Registrierungen lösen
EngineInstances beenden
Context-Beziehungen schließen
Runtime-Registry-Einträge entfernen
Compiler
Provider für neue Auswertungen nicht mehr auswählbar machen
Pass-/Extended-Op-Registrierungen lösen
betroffene CompilerDB-Abhängigkeiten invalidierbar machen
Fault/Coupling
Beziehung erst unzugänglich
tatsächliche Kopplung beenden
anschließend Coupling-Graph-Kante entfernen
Der mittlere Schritt ist wichtig. Eine Registry aus einer Tabelle zu löschen bedeutet noch nicht, dass kein laufender Code sie mehr benutzt.
P-16
[N]Die Deaktivierung darf eine Relationship-/Coupling-Kante nicht entfernen, solange die zugrunde liegende tatsächliche Kopplung noch besteht.
14.6 Compiler-Beiträge verschwinden nicht durch Descriptor-Löschung [D]¶
Der DescriptorStore ist kanonisch und append-only innerhalb seiner CompilerDB-Instanz. Plugin-Unload ändert daran nichts.
Darum bedeutet „Compiler-Plugin entladen“ nicht:
Es bedeutet:
Provider/Pass/Extended-Op für neue Arbeit nicht mehr auswählen
laufende Query-Auswertungen regulär abschließen oder canceln
Registry-Änderung als CompilerDB-relevanten Input sichtbar machen
davon abhängige Query-Ergebnisse invalidieren/revalidieren
alte Descriptoren als inerte, nicht mehr erreichbare kanonische Einträge belassen
P-17
[D]Änderungen an der aktiven Compiler-Beitragsmenge müssen im Dependency-Modell der CompilerDB sichtbar sein. Eine Registry darf nicht außerhalb des Query-/Input-Modells wechseln und danach alte Cacheantworten als weiterhin gültig behandeln.
Die genaue Repräsentation dieses Registry-Inputs gehört jade-compiler-db-spec.
Bereits erzeugte Runtime-Artefakte werden dadurch nicht magisch rückwirkend umgeschrieben. Ob ein Consumer für weitere Übersetzung Re-Verify oder Rebuild braucht, folgt aus seinen Abhängigkeiten und dem Interface-/Compilervertrag.
14.7 RECLAIMING — Ausführung ist vorbei, Freigabe noch nicht zwingend [D]¶
Nach DETACHING ist die Generation nicht mehr regulär erreichbar und führt keine neue Plugin-Arbeit aus.
Damit ist aber noch nicht garantiert, dass alle ihre Ressourcen sofort freigegeben werden dürfen.
Hier greift Hold in seiner normativen Bedeutung:
Ein Hold ist ein Anspruch auf eine konkrete Ressource, der deren Freigabe verhindert.
Beispiele:
gepinntes JME-Gebiet
laufender fremder/native Aufruf mit Zugriff auf Plugin-Speicher
Lease auf einer Plugin-Ressource
Callback-/Funktionsreferenz, deren Codeabbildung noch erreichbar sein muss
Ressourcen ohne Hold dürfen sofort reclaimed werden. Nur die tatsächlich gehaltene Ressource bleibt bestehen.
P-18
[N]RECLAIMINGendet erst, wenn alle für die Generation verbleibenden Ressourcen entweder freigegeben oder in eine ausdrücklich andere Ownership-Domäne transferiert wurden und keine ihre Freigabe blockierenden Holds mehr offen sind.
Das ist der Punkt, an dem das Wort Hold tatsächlich hingehört. Ein Plugin-Level-Hold würde eine einzige festgehaltene Ressource unnötig zum Lebenszeittoken für die ganze Domäne machen und dem Fault-Modell widersprechen.
14.8 Native Code-Abbildungen [D]¶
Native Payloads verschärfen den letzten Schritt.
Eine .so darf nicht entladen werden, solange:
ein nativer Aufruf darin noch läuft
ein Callback wieder hineinrufen kann
ein fremder Funktionszeiger auf Code darin gültig bleiben muss
plugin-eigener Speicher von fremdem Code gehalten wird
Die konkrete JVB-/FFI-Repräsentation muss dafür Holds oder einen äquivalenten, vom Fehlermodell gedeckten Ressourcenbesitz materialisieren.
Ein hängender fremder Aufruf kann deshalb RECLAIMING auf unbestimmte Zeit verlängern. Das ist kein Grund, den Speicher trotzdem freizugeben; es ist genau die Grenze, die jade-fehlermodell für same-process native isolation beschreibt.
Wer hart garantierten Unload gegen beliebigen nativen Code braucht, benötigt eine härtere Isolationsebene, typischerweise einen Prozess.
14.9 UNLOADED [D]¶
Eine Generation ist UNLOADED, wenn:
sie kein regulärer Eintrittspunkt mehr ist
keine aktive Relationship/Registry auf sie zeigt
keine plugin-eigene Ausführung mehr läuft
keine von ihr zu reclaimende Ressource mehr durch einen Hold festgehalten wird
ihre ausführbaren/nativen Codeabbildungen freigebbar sind
UNLOADED sagt nichts darüber, ob das PluginArtifact noch im ContentStore, Dateisystem oder Download-Cache liegt.
Es ist ein Zustand der aktiven Umgebung, nicht eine Aussage über Bytes auf Platte.
Eine erneute Aktivierung derselben Artefaktgeneration durchläuft mindestens Verify erneut, weil Hostpolicy, Dependencies, Target oder aktive Provider sich inzwischen geändert haben können.
14.10 Seed und Stdlib [D]¶
Der Seed kann nicht „entladen“ werden; er ist kein Plugin.
Die privilegierte Stdlib ist in v0.1 an die Lebensdauer der Jade-Instanz gebunden und kein reguläres Unload-Ziel.
Ein späteres Stdlib-Live-Replacement wäre eine eigene Kompatibilitätsstufe, weil praktisch jeder reguläre Plugin- und Compile-Zeit-Vertrag daran hängt. Es wird nicht durch die bloße Existenz des generischen Plugin-Unloads impliziert.
15. Resources und Runtime-Autorität [D]¶
Das Plugin-System besitzt kein eigenes Capability-Subsystem. Konkrete Runtime-Autorität wird über Resources und deren legitime Bindungs-, Ableitungs- und Transferpfade getragen (spec-resource-model). Der RuntimeBus bleibt Command-Substrat und ist weder Policy-Datenbank noch Quelle dieser Autorität.
15.1 Drei Ebenen¶
Artefakt / Loader-Metadaten
→ können konkrete Voraussetzungen ableitbar machen
→ z.B. native Host-Integration oder deferred Compilerarbeit
→ erteilen keine Runtime-Autorität
ContextDescriptor.resourceBindings
→ beschreibt Provisionierung und Erreichbarkeit
→ enthält keine lebenden Handles
ContextInstance / Resource-Handles
→ konkrete laufzeitgebundene Resources
→ tragen gegebenenfalls tatsächliche Autorität
Eine Funktion führt Services in ihrer Effect Row. Resources stehen nicht parallel in der Row, nur weil eine konkrete Service-Implementierung intern auf ihnen arbeitet.
Beispiel:
->{FileSystem}
↓ Binding
LinuxFileSystem
↓ arbeitet über eine gebundene/provisionierte
DirectoryResource("/srv/plugin", read-only)
↓ Handle
konkrete Runtime-Autorität
Der Funktionscode verlangt FileSystem, nicht FsReadCapability. Ebenso wenig erhält ein Provider durch seine bloße Registrierung Hostautorität. Privilegierte Resources müssen über die normalen Context-/Resource-Regeln provisioniert oder abgeleitet werden.
15.2 Admission gegen konkrete Resource-Autorität [D]¶
Verify darf konkrete privilegierte Voraussetzungen ableiten, wenn sie bereits aus dem Artefakt feststehen. Beispiele sind eine benötigte CompilerInstance bei deferred Runtime-Materialisierung oder loader-relevante native Host-Voraussetzungen. Daraus folgt keine allgemeine Manifest-Rechteliste.
Die tatsächliche Umgebung entsteht erst aus ResourceBindingSpecs und den daraus materialisierten Resources. Eine Plugin-Domäne darf privilegierte Autorität nicht aus einem Manifest-Feld, einem Service-Provider oder einer frei konstruierbaren Resource minten.
P-19
[D]Das Plugin-Manifest erteilt keine Runtime-Autorität. Konkrete Autorität entsteht ausschließlich über legitim provisionierte, abgeleitete oder transferierte Resources und ihre Handles; Loader-Preflight darf dafür nur aus realen Artefaktanforderungen ableiten.
Für Child-/Plugin-Domänen gilt die Resource-Monotonie aus spec-resource-model: Neue Resource-Objekte dürfen entstehen, ihre Autorität muss aber auf eine legitime Quelle zurückführbar sein und darf bei Ableitung oder Transfer nicht verstärkt werden.
Host / Parent Resource
↓ provision / derive / attenuate
Plugin Resource
↓ Handle
konkrete nutzbare Autorität
Das Plugin-System braucht dafür in v0.1 weder ein globales CapabilitySet noch eine eigene CapabilityEngine.
15.3 Runtime-Gate und Ausführung [N]¶
Die ContextApi ist das interne Gate für materialisierte Service-/Resource-Bindungen. Sie ist kein ambienter Resource-Service-Locator für gewöhnlichen JDL-Code.
Danach führen Intrinsics und RuntimeBus die mechanischen Operationen aus und prüfen ihre jeweiligen Gültigkeitsinvarianten.
Context-/Resource-Bindung ist diese Resource hier legitim erreichbar?
Resource-Handle welche konkrete Entität/Autorität besitze ich?
RuntimeBus führe den daraus resultierenden Command geordnet aus
Intrinsic / Ring 0 ist die Maschinenoperation konkret gültig?
Ein alter oder context-fremder Handle kann daher trappen, obwohl seine Resource-Art prinzipiell zulässig wäre. Umgekehrt kann ein Plugin eine Hostoperation nicht dadurch erreichen, dass es lediglich den Namen eines Services oder einer Resource kennt.
15.4 CompilerInstance bei Runtime-Load [D]¶
Deferred Compilerarbeit benötigt bei einem Runtime-Load eine laufende Compilerinstanz. Im aktuellen Context-/Resource-Modell ist diese keine Boolean-Capability, sondern eine privilegierte Resource.
P-26
[N]Ein zur Laufzeit angefordertes Plugin darf deferred Compilerarbeit nur dann materialisieren, wenn für diesen Ladevorgang eine legitimeCompilerInstance-Resource erreichbar beziehungsweise provisionierbar ist.
Fehlt sie, ist Runtime-Load nicht grundsätzlich verboten. Es ist nur auf Artefakte beschränkt, für die im Zielzustand keine Consumer-seitige Compilerarbeit mehr nötig ist:
CompilerInstance erreichbar
→ fertige oder deferred Plugins zulässig
keine CompilerInstance erreichbar
→ nur ausreichend vormaterialisierte Plugins zulässig
Die Resource darf einer Plugin-Domäne nicht automatisch deshalb zugänglich werden, weil der Loader selbst einen Compiler besitzt. Soll eine CompilerInstance über eine Context-Grenze geteilt, geleast oder übertragen werden, gelten die allgemeinen Resource-/Bridge-Regeln.
Ob ein Artefakt bei Load eine Compilerinstanz benötigt, ist aus seinen deferred Bestandteilen ableitbar und muss nicht als frei behauptetes Manifest-Flag gepflegt werden.
15.5 Constitution gegen Resource-Autorität [D]¶
Die beiden Systeme beantworten verschiedene Fragen:
Constitution
→ darf dieses Programm / dieser Namensraum so beschrieben und materialisiert werden?
Resource-Autorität
→ welche konkrete laufzeitgebundene Entität ist in dieser Domäne erreichbar,
und welche Autorität trägt ihr Handle?
Eine Constitution kann beispielsweise verbieten, dass ein Projekt überhaupt Plugin-Domänen mit bestimmten privilegierten Host-Resources beschreibt. Die konkrete Runtime-Autorität entsteht dadurch trotzdem nicht in der Constitution, sondern durch die materialisierten Resource-Bindings.
Damit gibt es keine parallele allgemeine Capability-Achse im Plugin-System. Feingranulare Sandboxing-/Attenuationsregeln bleiben eine Resource-/Context-Frage und können später aus realem Bedarf erweitert werden.
Teil VI — Determinismus, Versionierung und Replacement¶
16. Determinismus und Versionierung¶
16.1 Fingerabdrücke, nicht TypeIds [N]¶
Eine TypeId ist lauf-lokale Internierungsidentität und kein persistenter Plugin-Schlüssel.
Der interfaceFingerprint basiert auf der beobachtbaren Oberfläche, insbesondere auf:
- Signaturen freigegebener Symbole;
- Protokolloberflächen;
- öffentlich beobachtbaren Requires- und Resource-/Autoritätsverträgen;
- Constitution;
- Init-/Lifecycle-Vertrag;
- soweit öffentlich relevant: Bridge-/Service-Verträge.
16.2 Drei verschiedene Verträge [N]¶
interfaceFingerprint
→ hat sich die beobachtbare Plugin-Oberfläche geändert?
artifactVersion / compilerContract
→ kann Loader/Compiler die distributierte Repräsentation lesen und materialisieren?
CompilerDB cache identity
→ darf ein konkretes lokales Query-/Descriptor-Ergebnis wiederverwendet werden?
Keiner ersetzt einen anderen.
16.3 Kompatibilitätsklassen [D]¶
| Klasse | Fingerabdruck | Auswirkung |
|---|---|---|
| interne Änderung | identisch | Kandidat für transparentes Replacement; State-/Native-Verträge können trotzdem blockieren |
| additive Erweiterung | verändert | Re-Verify; Consumer ohne Nutzung der Neuerung brauchen nicht zwingend Rebuild |
| strukturell inkompatibel | verändert | abhängige Consumer brauchen Re-Init, Re-Materialisierung oder Rebuild |
Ein identischer Interface-Fingerprint ist notwendig, aber nicht hinreichend für verlustfreies Live-Replacement. Lebender Zustand, native Ressourcen und externe Protokolle haben eigene Verträge.
16.4 Die Constitution gehört zur Oberfläche [D]¶
Eine verschärfte Plugin-Constitution kann Nutzercode brechen, ohne dass sich eine Signatur ändert.
Darum gehört die materialisierte Regeloberfläche in die Kompatibilitätsbetrachtung.
16.5 Hot-Reload — architektonische Form [D, Implementierung Phase 2]¶
Hot-Reload ist kein dritter Lifecycle. Es ist die koordinierte Überlappung zweier PluginGenerationen.
Die sichere Grundform lautet:
Generation N bleibt ACTIVE
Generation N+1:
load
→ verify
→ stage
→ init
alles erfolgreich?
nein → N bleibt unangetastet
N+1 baut eigenen Besitz nach §13.5 ab
ja → atomarer Cutover
Cutover:
neue Eintritte → N+1
N+1 → ACTIVE
N → DRAINING
danach:
N → DETACHING → RECLAIMING → UNLOADED
Der Vorteil ist wesentlich: Neue Arbeit benutzt sofort die neue Generation, während bereits laufende Arbeit in der alten Generation regulär auslaufen kann.
Die Root-Namensraum-Invariante bleibt erhalten, weil nie zwei Generationen zugleich der reguläre aktive Eintrittspunkt desselben Roots sind.
16.6 Dependency-Swap [D]¶
Bei einem Replacement darf ein aktiver Dependent nicht für einen Moment auf „keine Dependency“ zeigen.
Der Cutover muss deshalb die aufgelöste Dependency-Bindung atomar von Generation N auf N+1 umstellen, wenn deren Vertrag kompatibel ist.
Ist das nicht beweisbar, ist kein nahtloses Hot-Reload zulässig. Dann muss der Dependent selbst Teil der Replacement-/Rebuild-Closure werden.
16.7 Zustandsmigration [D]¶
Lebender Zustand überlebt eine Generation nicht automatisch.
Ein alter Handle ist kein Migrationsformat.
Zulässige Wege sind grundsätzlich:
Zustand liegt ohnehin außerhalb der PluginGeneration
→ z.B. expliziter externer Service / persistenter Store
oder
Zustand wird über einen ausdrücklich versionierten Migrationsvertrag exportiert
und von der neuen Generation importiert
Cross-Context-Transfer bleibt dabei an die allgemeine Bridge-/Transfersemantik gebunden. Hot-Reload erhält keine Sondergenehmigung, Handles an Ownership- und Context-Grenzen vorbeizuschieben.
Die konkrete Form eines State-Migration-Descriptors bleibt offen.
16.8 Rollback [D]¶
Vor Beginn von Init ist Rollback billig: Die neue StagingTransaction wird verworfen, Generation N bleibt ACTIVE.
Sobald Init echte Runtime-Ressourcen erzeugt hat, ist „verwerfen“ allein nicht mehr ausreichend. Ein Fehler von N+1 vor dem Cutover lässt N zwar vollständig unangetastet, der Kandidat N+1 muss aber nach §13.5 über DETACHING → RECLAIMING → FAILED zurückgebaut werden.
Der sichere Vor-Cutover-Vertrag lautet daher:
N bleibt ACTIVE
N+1 scheitert vor Cutover
→ keine Änderung an N
→ keine Sichtbarkeit von N+1
→ aber vollständiger Eigentümer-basierter Teardown von N+1
Nach dem atomaren Cutover ist allgemeiner Rollback nicht garantiert. Sobald alte Tasks gecancelt, Zustände migriert oder externe irreversible Wirkungen ausgeführt wurden, kann „zurück“ eine eigene Migration sein.
Die Architektur verspricht daher keinen magischen Reverse-Commit.
17. Testing [D]¶
Plugin-Testing bleibt deterministisch.
Zulässig:
- isolierte Test-Harness;
- Seed und Stdlib als feste Basis;
- Fake-/Mock-Abhängigkeiten über deklarierte Test-Build-Auswahl;
- deterministischer Scheduler;
- gezieltes Auslösen von Lifecycle-Transitions über die reguläre Lifecycle-API;
- Testen von Drain, Cancellation, Detach und Hold-Freigabe über kontrollierte Testressourcen.
Ausdrücklich verboten:
- Monkey-Patching;
- ad-hoc Umschreiben aktiver Registrierungen außerhalb des Plugin-Lifecycle;
- Laufzeit-Mock-Injection, die die normalen Context-/Binding-/Compile-Time-Regeln umgeht.
Der Unterschied ist wichtig: DETACHING ist eine autorisierte Lifecycle-Operation. Es ist kein Freibrief, beliebige Registries zur Laufzeit zu editieren.
18. Plugin Broker [D]¶
Der Plugin Broker hält die Runtime-Projektion einer PluginGeneration. Die Generation selbst gehört nach P-30 dem Loader. Der Broker ist damit weder Eigentümer des Lifecycles noch ein Container für beliebigen Plugin-Anwendungszustand.
PluginArtifact
→ Definitionen und distributierte Inhalte
PluginGeneration (Loader)
→ Lifecycle-Identität und -Zustand, für beide Domänen gemeinsam
Plugin Broker
→ Runtime-Projektion: aktive Runtime-Registrierungen und ihre Generationenzuordnung
Context
→ konkrete Service-Instanzen, Tasks, Ressourcen und veränderlicher Runtime-Zustand
CompilerDB / Compiler-Registries
→ Compiler-Beiträge und deren Dependency-/Invalidierungszustand
P-9
[N]Es gibt keinen implizitenPluginGlobalState. Gemeinsamer Plugin-Zustand ist ein gewöhnlicher, explizit gebundener Service in einem Context und unterliegt denselben Ownership-, Resource-/Autoritäts-, Lifetime-, Bridge- und Fault-Regeln wie anderer Zustand.
18.1 Broker-Invarianten¶
- Jede Generation mit Runtime-Beiträgen besitzt eine eindeutige Runtime-Projektion. Der Lifecycle-Eintrag selbst entsteht beim Loader ab
STAGED, Kandidaten eingeschlossen, und existiert auch für Plugins ganz ohne Runtime-Seite. - Pro
PluginIdgibt es höchstens eine Generation, die regulärerACTIVE-Eintrittspunkt ist. - Kein Plugin wird
STAGED, bevor seine DependenciesACTIVEsind. - Außerhalb eines atomaren Replacement-Cutovers hängt kein
ACTIVEPlugin von einer nicht-ACTIVEDependency ab. - Keine zwei regulär
ACTIVEGenerationen besitzen denselben Root-Namensraum. - Keine zwei im Coherence-Geltungsbereich nach §8.4 gleichzeitig erreichbaren
provide P for Texistieren für dieselbe Kombination. DRAININGnimmt keine neue reguläre Arbeit an.DETACHINGentfernt keine Coupling-Beziehung vor Ende der tatsächlichen Kopplung.UNLOADEDwird erst erreicht, wenn Reclaim nicht mehr durch offene Holds der Generation blockiert ist.
18.2 Compiler-Seite¶
Compiler-Aktivierung und -Deaktivierung erfolgen über CompilerDB-/Build-seitige Registries, nicht über den Runtime-Broker. Auch das ist eine Projektion: Die Registries führen Compiler-Beiträge, nicht den Lifecycle.
Der gemeinsame Plugin-Lifecycle koordiniert beide Domänen nur dort, wo ihr Commit beziehungsweise ihr vollständiger Unload semantisch gemeinsam sein muss.
Teil VII — Ausgelöste Änderungen in anderen Specs¶
19. Folgearbeiten [D]¶
Dieses Dokument besitzt das Plugin-Modell. Innenmechanik anderer Subsysteme wird dort ergänzt, nicht hier dupliziert.
jdl-meta-phase¶
- MP-5 muss auf das ausgelieferte Laufzeitartefakt eingegrenzt werden. Compile-Zeit-Code darf während Übersetzung und Plugin-Distribution als
MetaStructuralFormbeziehungsweise temporäre Compile-Zeit-Ausführungsform existieren. MetaStructuralFormbraucht dort den vollständigen Totalitäts-/Terminierungsvertrag: welche strukturellen Formen zulässig sind und wie strukturelle Rekursion nachgewiesen wird.- Eigentümer und Form des Terminierungs-/Totalitätsnachweises müssen benannt sein.
- Der Proof für deferred Meta fremder Artefakte muss lokal validierbar sein.
- Die Reichweite des Nachweises nach P-27 muss dort mitgeführt werden: lokaler Kontrollfluss statisch, Terminierung über
db.*durch die Zyklusbehandlung der CompilerDB. - Meta-Allokation bleibt eine offene Fremdspec-Frage und darf durch diese Plugin-Spec nicht erfunden werden.
- Der Totalitäts-/Proof-Vertrag soll Semantic Views als gewöhnliche CompilerDB-Queries konsumieren können; dafür entsteht keine zweite Analyse-Registry.
- Ein lokaler endlicher Worklist-/Fixpunktalgorithmus innerhalb eines totalen Meta-Providers ist von einem CompilerDB-Query-Zyklus zu unterscheiden; P-27 bleibt unverändert.
spec-context-model¶
- Plugin-Lifecycle braucht eine definierte Möglichkeit, neue Service-/Bridge-Eintritte einer Generation zu schließen, ohne bereits laufende legitime Kopplungen vorzeitig zu zerstören.
- Shutdown-/Drain-Policy gehört an Context/Scheduler-Governance, nicht an den Plugin Broker.
- Cross-Context-Transfer bei State-Migration bleibt den Resource-/Bridge-Regeln (
spec-resource-model,spec-context-bridge) unterworfen. CompilerInstanceist nach dem Resource-Modell die relevante privilegierte Resource für Runtime-Load mit deferred Compilerarbeit.
jade-fehlermodell¶
Keine neue Plugin-Fehlerkategorie nötig.
Unload konsumiert:
- F-25 für konservativen Beziehungsabbau;
- F-26 für den gemeinsamen Übergang zum Freigeben;
- F-28/F-29 für ressourcenbezogene Holds;
- F-30/F-31 für die Trennung von Ausführungsende, Reclaim und nativer Isolation;
- F-32/F-33 für Eintritte in fault-isolierbare Domänen.
jdl-vm-scheduler-spec¶
- konkrete Drain-/Deadline-/Cancellation-Policy für Context-Shutdown;
- Verhalten bei Tasks, die auf externe Ereignisse warten;
- deterministische Teststeuerung.
jade-compiler-db-spec¶
- aktive Provider-/Pass-/Extended-Op-Registry muss als query-relevanter Zustand modelliert sein;
- Provider-Entzug muss laufende Auswertungen über die normale Cancellation-/Revision-Semantik behandeln;
- abhängige Cacheantworten müssen invalidierbar sein;
DeterministicProviderFaultgegen operativen Abbruch muss als Publikationsentscheidung konkretisiert werden: Diagnostic/QueryResult gegen kein publiziertes Ergebnis;- DescriptorStore-Einträge werden beim Plugin-Unload nicht durch den Plugin-Lifecycle gelöscht;
- für langlebige Compilerinstanzen mit wiederholtem Hot-Reload braucht es eine Speicher-/Compaction-Entscheidung für nicht mehr erreichbare, aber kanonische Descriptoren;
- Semantic Views als Konvention gewöhnlicher Derived Queries einordnen, ohne
ViewId, zweite Registry oder neue Result-Kategorie; - Autorität für sealed/core Proof-Queries festlegen, ohne die Regel „ein aktiver Provider pro Derived Query und Stage-Profil“ aufzuweichen.
Runtime-Layout / JME¶
P-10 setzt voraus, dass Runtime-Ausführung nicht an einer lebenden Type Engine oder CompilerDB hängt. Die Runtime muss alle benötigten Layout-/Policy-Informationen aus dem Laufzeitartefakt beziehungsweise aus daraus materialisierten Runtime-Strukturen beziehen.
Die genaue Eigentums- und Lebenszeitform dieser Runtime-Layoutdaten muss dort festgelegt werden, wo JME-/Loader-Materialisierung normiert wird. Sie darf nicht implizit durch einen ewig lebenden Compiler-DescriptorStore ersetzt werden.
JVB-/FFI-Spec¶
- native Codeabbildungen, laufende FFI-Aufrufe und Callbacks brauchen einen expliziten Ressourcen-/Hold-Vertrag;
dlcloseoder äquivalente Freigabe darf erst nach Ende dieser Holds erfolgen;- starke Unload-Garantien gegen beliebigen nativen Code brauchen gegebenenfalls Prozessisolation.
Artifact-/Loader-Spec¶
PluginGenerationbeziehungsweise äquivalente Aktivierungsidentität konkretisieren;- Replacement-Kandidat desselben Root-Namensraums muss als kontrollierter Fall vom gewöhnlichen Namespace-Konflikt unterschieden werden;
MetaStructuralFormundGenericDefinitionbrauchen je Schema-Version, kanonische Serialisierung und Loader-Validierung;- der Loader ist nach P-30 Eigentümer der
PluginGeneration; die konkrete Form dieses Lifecycle-Eintrags und der beiden Projektionen gehört dorthin; - Verify prüft Formatintegrität; die semantische Proof-Prüfung bleibt Stage.
Anhang A — Normative Invarianten¶
Dieser Anhang ist ein abgeleiteter Invariantenindex. Er führt keine eigenständige Semantik ein; jede Zeile hat ihre Definition im Haupttext. Eine Abweichung zwischen Anhang und Haupttext ist deshalb kein Auslegungsfall, sondern ein Spezifikationsfehler und wird als solcher behoben.
PluginArtifactist die einzige Plugin-Distributionseinheit.- Ein
PluginArtifactdarf fertige und absichtlich deferred Bestandteile nebeneinander tragen. - Source ist keine notwendige Ladeform.
- Ein
DeferredCompilerArtifactträgt genau eine von zwei Formen —GenericDefinitionoderMetaStructuralForm— plus einen deklarierten Aufschubgrund. Target-Abhängigkeit ist ein Grund, keine Form. GenericDefinitionist kanonisch, versioniert, unveränderlich, frei von lauf-lokalen Identitäten und kein internes Compiler-IR; sie referenziert Meta-Rümpfe, statt sie einzubetten.- Deferred Meta wird distributiv als kanonische, versionierte
MetaStructuralFormoberhalb eines allgemeinen CFG/JBC transportiert. MetaStructuralFormenthält keine lauf-lokalen TypeIds, Arena-Indizes, Pointer oder mutable Compilerobjekte als persistente Identität.- Öffentliche generische Definitionen werden mit ihrer vollständigen Materialisierungs-Closure ausgeliefert.
- Deferred Meta läuft nur nach lokal akzeptiertem Terminierungs-/Totalitätsnachweis.
- Proof-Prüfung erfolgt zu Beginn von Stage nach ausreichender Referenzauflösung und vor der ersten Meta-Ausführung.
- Der Nachweis deckt lokalen Kontrollfluss ab; Terminierung über
db.*trägt die Zyklusbehandlung der CompilerDB. - Compile-Zeit-Code wird beim Erzeugen des Laufzeitartefakts entfernt; ein
PluginArtifactist kein Laufzeitartefakt. interfaceFingerprint, Artefakt-/Compilervertrag und CompilerDB-Cache-Identität sind verschiedene Verträge.- Interne mutable Compilerrepräsentationen und lokal gelowertes JBC/IR sind keine öffentliche Plugin-ABI.
- Native Payloads und externe native Dependencies sind explizit deklariert; ihre Nutzung setzt die dafür provisionierten FFI-/NativeHost-Resources beziehungsweise Host-Provider voraus.
- Compiler- und Runtime-Beiträge sind optional und werden in ihren jeweiligen Domänen aktiviert.
- Die Type Engine ist keine Runtime-Komponente eines aktiven Plugins.
- Der Seed ist kein Plugin.
- Die Stdlib ist ein privilegiertes Plugin, darf aber
jade::corenicht befüllen. - Jedes Plugin hat genau einen kanonischen Root-Namensraum.
- Freigaben stehen in der Constitution, nicht im Manifest.
PluginIdbezeichnet das logische Plugin; eine PluginGeneration bezeichnet eine konkrete Artefaktgeneration als Lifecycle-Einheit und entsteht als Kandidat mitSTAGED.- Die
PluginGenerationgehört dem Loader; Broker und Compiler-Registries halten nur Projektionen. Ein Plugin ohne Runtime-Seite hat trotzdem genau einen Lifecycle. - Es gibt höchstens eine regulär
ACTIVEGeneration pro Plugin-/Root-Eintritt. provide P for Tist zulässig, wenn P oder T im selben Package liegt; Seed,jade::undjdl::bilden ein Coherence-Package.ForeignExtensiblegilt, wenn weder P noch T dem Plugin gehören.(P, T)muss innerhalb des Coherence-Geltungsbereichs nach §8.4 eindeutig sein; maßgeblich ist gleichzeitige Auflösungserreichbarkeit.- Plugins dürfen Extended ergänzen, nicht Core verändern.
- Passes benutzen die bestehende Pass-/Verifier-Pipeline.
- QueryProvider unterliegen der CompilerDB-Eindeutigkeit; EngineProvider sind eine Provider-Art. Konsumenten verwenden bevorzugt die kuratierte
db.*-Fassade, deren Komposition Query-Identität und Dependency-Tracking erhalten muss. - Ein Datenparser erhält durch seine Existenz keine JDL-Frontend-Autorität.
- Eine Syntaxerweiterung erzeugt ausdrückbares JDL und durchläuft alle gewöhnlichen Gates.
- Für v0.1 gilt Grammatik → Descriptor → Parser; Parser-Codegenerierung ist keine Voraussetzung.
- Plugin-Commands sind qualifiziert; Stdlib-Commands unqualifiziert.
- Der Vertrag von Plattform-Plugins steht in der Stdlib.
- Aktivierung: discover → load → verify → stage → init → commit → active.
- Verify prüft die unterstützte Schemaversion aller deferred Artefaktformen. Bei Runtime-Load prüft Verify zusätzlich die Verfügbarkeit beziehungsweise Provisionierbarkeit einer
CompilerInstance-Resource nach P-26, sofern consumer-seitige Materialisierung erforderlich ist. Semantische Prüfung bleibt Stage. - Stage besitzt eine
StagingTransaction; sie ist kein Ausführungs-Context. - Resolution View, Coherence View und Replacement Compare View sind verschieden weit und werden nicht vermischt.
- Die Replacement-Generation ist aus der Resolution View des Kandidaten maskiert und nur über eine getrennte Compare View lesbar.
- Semantische Prüfungen erfolgen gegen staged, noch nicht global sichtbaren Zustand.
- Ausführbarer Plugin-Code in Stage/Init unterliegt dem normalen Context-/Fault-Modell.
- Deterministische Compilerdiagnosen über Provider-Faults dürfen cachebar sein; operative Abbrüche publizieren kein QueryResult.
- Init erzeugt Lebenszeitbesitz nur unter explizitem Runtime-/Generation-Owner.
- Ein Init-Fehler führt über
DETACHING → RECLAIMING → FAILED; das Verwerfen der StagingTransaction allein ist kein Runtime-Rollback. - Ein gescheiterter Kandidat durchläuft kein
DRAINING; er war nie regulär erreichbar. - Erst erfolgreicher Init erlaubt Commit; der Commit macht die staged Beiträge semantisch gemeinsam sichtbar.
- Kein STAGED bevor alle Dependencies ACTIVE sind.
- Aktivierung ist in v0.1 strikt sequenziell in topologischer Reihenfolge.
- Deaktivierung normaler Dependency-Closures erfolgt Dependents-vor-Dependencies.
- Deaktivierung: active → draining → detaching → reclaiming → unloaded.
DRAININGschließt neue Erreichbarkeit, bevor bestehende Kopplungen beendet werden.Throw, Cancellation, Trap und Fatal bleiben beim Plugin-Unload getrennte Begriffe.DETACHINGentfernt Beziehungen erst nach Ende der tatsächlichen Kopplung.- Compiler-Unload löscht keine DescriptorStore-Einträge durch Plugin-Lifecycle-Aktion; er entzieht Provider-/Registry-Erreichbarkeit und invalidiert abhängige Query-Arbeit.
Holdist ein Ressourcenanspruch und kein Plugin-Lifecycle-Zustand.RECLAIMINGdarf nur Ressourcen freigeben, auf denen kein Hold mehr liegt.UNLOADEDwird erst erreicht, wenn die Generation keine aktive Ausführung, Registrierungen oder unreclaimbaren gehaltenen Ressourcen mehr besitzt.- Runtime-Anwendungszustand lebt in Contexts; der Broker hält die Runtime-Projektion der Generation.
- Das Manifest enthält keine allgemeine Autoritätsliste und erteilt keine Runtime-Autorität; Loader-Preflight darf konkrete Resource-Voraussetzungen nur aus realen Artefaktanforderungen ableiten.
- Konkrete Runtime-Autorität wird über Resources und ihre Handles getragen; Services bleiben die semantischen Elemente der Effect Row.
- Runtime-Load mit deferred Compilerarbeit benötigt eine legitime
CompilerInstance-Resource; beim gewöhnlichen Build gilt diese Runtime-Admission-Regel nicht. - Ohne erreichbare/provisionierbare
CompilerInstancekönnen zur Laufzeit nur ausreichend vormaterialisierte Plugins aktiviert werden. - Versionierung richtet sich nach der beobachtbaren Oberfläche; Constitution und Lifecycle-Verträge gehören dazu.
- Persistente Kompatibilität basiert auf Fingerprints/Verträgen, nicht auf TypeIds.
- Hot-Reload materialisiert zuerst eine neue Generation und schaltet den Eintritt erst nach erfolgreicher Vorbereitung atomar um.
- Scheitert der Kandidat vor Cutover, bleibt die alte Generation unangetastet; der Kandidat kann trotzdem eigenen Teardown benötigen.
- Lebender Zustand migriert nur über ausdrückliche Ownership-/Migrationsverträge, niemals durch stilles Weiterreichen alter Handles.
- Testing bleibt deterministisch; Lifecycle-Transitions sind erlaubt, Monkey-Patching und ad-hoc Registry-Mutation nicht.
Anhang B — Entscheidungsregister¶
| Nr. | Entscheidung | Ersetzt / präzisiert |
|---|---|---|
| P-A | Der Seed entsteht in Zig über die CompilerDB | Bootstrap als Plugin |
| P-B | exports entfällt; Freigabe steht in der Constitution |
Manifest-Exportliste |
| P-C | interfaceFingerprint ist abgeleitet und build-konfigurationsgebunden |
manuell gepflegter Fingerprint |
| P-D | provide P for T bei P oder T im Package |
nur T-Ownership |
| P-E | ForeignExtensible nur wenn weder P noch T gehören |
allgemeines Fremdtyp-Opt-in |
| P-F | Seed, jade:: und jdl:: sind ein Coherence-Package |
ungeregelt |
| P-G | Compiler-Erweiterung über Extended-Ops, Passes und QueryProvider; EngineProvider sind Provider-Art | separates Handler-/Engine-Registermodell |
| P-H | Commands im Manifest, Plugin-Commands qualifiziert | fehlte |
| P-1 | Syntaxerweiterung ist Sugar; Prüfstein ist ausdrückbares JDL | fehlte |
| P-2 | Stdlib-Commands unqualifiziert, Plugin-Commands zweiteilig | fehlte |
| P-3 | Plattformvertrag steht über den Plattform-Plugins | fehlte |
| P-I | v0.1: Grammatik als Blueprint, Descriptor zur Meta-Zeit, Parser konsumiert Descriptor | fehlte |
| P-J | Plugin bringt eigene Constitution mit | fehlte |
| P-4 | PluginArtifact trägt abgeschlossene und deferred Bestandteile |
Source/Binary-Dichotomie |
| P-5 | Deferred Generics tragen vollständige Materialisierungs-Closure | fehlte |
| P-6 | Deferred Meta braucht lokal akzeptierten Terminierungs-/Totalitätsproof | unbelegte Meta-Totalität |
| P-7 | Deferred Meta wird als kanonische MetaStructuralForm oberhalb des CFG transportiert; internes IR/JBC bleibt Consumer-intern |
unbenannte deferred Artefaktebene |
| P-8 | Native Payloads/Dependencies sind explizit | implizite Laufzeitsuche |
| P-9 | Plugin-Anwendungszustand lebt in Contexts; Broker hält nur die Runtime-Projektion | impliziter Globalstate |
| P-K | StagingTransaction ist kein Context |
„Staging-Kontext“ |
| P-L | Fault-Isolation von Stage/Init folgt Context/FaultDomain | Loader-Isolationsbehauptung |
| P-10 | Type Engine bleibt Compilerseite; Runtime konsumiert materialisierte Ergebnisse | Type Engine als Runtime-Aktivierungsbestandteil |
| P-11 | Logische PluginId wird von konkreter PluginGeneration getrennt | ein Lifecycle-Eintrag konnte Hot-Reload-Überlappung nicht ausdrücken |
| P-12 | Hold ist kein Pluginstatus, sondern ressourcenbezogener Freigabeschutz |
ACTIVE → HOLD als Domain-Lifecycle |
| P-13 | Unload ist schrittweiser Entzug von Erreichbarkeit, Kopplung und Besitz | gedachte Reverse-Commit-Symmetrie |
| P-14 | Normales Unload arbeitet Dependents-vor-Dependencies | Aktivierungsreihenfolge einfach rückwärts unbegründet |
| P-15 | Cancellation ist Lifecycle, Throw fachlich, Trap Runtimefehler | Plugin-spezifischer Unload-Fehler |
| P-16 | Coupling-Kante erst nach Ende realer Kopplung entfernen | Registry-Löschung als ausreichender Detach |
| P-17 | Compilerbeitragsänderung muss CompilerDB-invalidation sichtbar sein; Descriptoren bleiben | „Typ-Deregistrierung“ |
| P-18 | Holds blockieren nur konkrete Ressourcen bis Reclaim | Domain-weites Freigabetoken |
| P-19 | Manifest erteilt keine Autorität; Loader-Preflight leitet nur konkrete Resource-Voraussetzungen ab, Runtime-Autorität hängt an Resources/Handles | RuntimeBus bzw. CapabilitySet als Autoritätseigentümer |
| P-20 | Hot-Reload ist Generation-Cutover mit anschließendem Drain der alten Generation | Unload + Load mit unnötigem Sichtbarkeitsloch |
| P-21 | (P,T)-Eindeutigkeit gilt über gleichzeitig resolver-erreichbare Beiträge einer CompilerDB-Instanz |
rein buildzeitlicher Kohärenz-Geltungsbereich |
| P-22 | Replacement maskiert die alte Generation aus der Resolution View und hält sie nur in einer Compare View lesbar | unklare Stage-Sicht |
| P-23 | Meta-Proof wird am Anfang von Stage vor der ersten Meta-Ausführung geprüft | impliziter Prüfzeitpunkt |
| P-24 | Deterministische Provider-Fault-Diagnostics können cachebar sein; operative Abbrüche publizieren kein Ergebnis | ungeklärte Fault-/Cache-Grenze |
| P-25 | Init-Besitz überlebt einen Fehlschlag bis zum normalen Teardown; INIT → DETACHING → RECLAIMING → FAILED |
billiger Init-Rollback |
| P-26 | Runtime-Load mit deferred Anteilen setzt eine legitime CompilerInstance-Resource voraus |
Capability-Flag bzw. implizites Startrecht für Compilerinstanzen |
| P-27 | Totalitätsnachweis gilt lokal; Terminierung über db.* trägt die CompilerDB-Zyklusbehandlung |
Proof als vermeintlich vollständige Zusage |
| P-28 | DeferredCompilerArtifact = zwei Formen plus deklarierter Aufschubgrund; target-deferred ist ein Grund |
unklassifizierter deferred Inhalt |
| P-29 | GenericDefinition ist die kanonische, versionierte Form deferred Generics |
nur Closure gefordert, keine Form |
| P-30 | Die PluginGeneration gehört dem Loader; Broker und Registries halten Projektionen |
Broker als impliziter Lifecycle-Eigentümer |
Anhang C — Offene Punkte und Abhängigkeiten¶
P-O1 [O] Vererbungswurzel für Plugin-Constitutions. Gegen welche Autorität wird eine Plugin-Constitution validiert: Host, Ladepunkt oder Package-Elternteil?
P-O2 [O] Resource-/Context-Vertrag für Tool-Commands. Ein bewusst gestarteter CLI-Command ist nicht automatisch dieselbe Ausführungsdomäne wie ein Runtime-Service.
P-O3 [O] Source-Provenance durch Syntax-Umschreibungen. Wie generierte JDL-Formen bis zu Diagnostics auf die ursprüngliche Quelle zurückverweisen.
P-O5 [O] Interaktion zweier Syntaxerweiterungen. Getrennte Root-Namensräume garantieren noch keine konfliktfreie gemeinsame Oberflächensyntax.
P-O6 [O] Parser-Spezialisierung. Ob Grammar-/Parser-Descriptoren dauerhaft interpretiert oder semantikgleich zu spezialisiertem Parser-Code kompiliert werden dürfen.
P-O7 [O, Abhängigkeit] Fault-sichere Aktivierungsbeziehung. Lokale Trap-Containment-Zusagen hängen an spec-context-model und jade-fehlermodell.
P-O8 [O, Fremdspec] Meta-Allokation. Der Eigentümer des Totalitätsnachweises ist in jdl-meta-phase v0.2.0 geklärt: Stage 1 trägt einen Zig-BootstrapProvider, Stage 2+ denselben Query-Vertrag durch JDL-Semantik; der Stage-2-Provider darf seine eigene Zulässigkeit nicht selbst beweisen. Offen bleibt hier nur die weiterhin ungeklärte Meta-Allokation. Die Artefaktform ist mit MetaStructuralForm entschieden, die Reichweite des Nachweises mit P-27.
P-O9 [O, Implementierung] Drain-/Deadline-Policy. Die Lifecycle-Semantik ist festgelegt; die konkrete Scheduler-/Context-Konfiguration für Graceful Drain, Deadline und Cancellation fehlt.
P-O10 [O, Fremdspec] Native Holds. JVB/FFI muss festlegen, wie laufende native Aufrufe, Callback-Ziele und Codeabbildungen ihre Holds ausdrücken und freigeben.
P-O11 [O, Fremdspec] Compiler-Beitragsentzug. CompilerDB muss die aktive Providermenge, Invalidation und laufende Query-Cancellation konkret materialisieren.
P-O12 [O] State-Migration-Vertrag. Form, Versionierung und Prüfung eines optionalen Migrationsdeskriptors für Hot-Reload.
P-O13 [O] Replacement-Failure nach Cutover. Ein allgemeiner Rollback nach destruktiver Migration wird nicht zugesagt; konkrete Recovery-Strategien sind offen.
P-O14 [O] Remote-Registry und Signaturen. Discovery, Download, Trust und Signaturprüfung.
P-O15 [O] Paralleles Staging. Unabhängige Plugins dürfen später parallel vorbereitet werden, sofern Commit- und Dependency-Invarianten erhalten bleiben.
P-O16 [O] Post-Commit Activation Hook. Für irreversible externe Wirkungen, die bewusst erst nach Commit stattfinden dürfen.
P-O17 [O] Loader-Preflight für privilegierte Resources. Welche konkreten Resource-Voraussetzungen künftig zusätzlich früh aus Artefaktmetadaten ableitbar sein sollen und welche erst in Stage beziehungsweise bei Context-Aktivierung feststehen.
P-O18 [O, Fremdspec] Provider-Fault-Publikation. jade-compiler-db-spec muss die in §13.4.2 festgelegte Trennung konkret codieren: deterministische Fault-Diagnostic als cachebares QueryResult gegen operativen Abbruch ohne Publikation.
P-O21 [O] Detailschema von GenericDefinition. Die Anforderungen stehen mit P-29 fest — kanonisch, versioniert, ausreichend für jede zulässige Spezialisierung, ohne lauf-lokale Identitäten. Welche Knotenarten die Form konkret kennt, kann mit dem ersten echten Generic entschieden werden und muss nicht vorab erraten werden.
P-O22 [O] Multi-Target-Packaging. v0.1 löst target-abhängigen Code über vorgebaute VmModuleArtifacts pro Target (§1.1.1). Ob und wann sich das lohnt, gegen eine target-unabhängige Zwischenform zu tauschen, ist eine spätere Frage — und eine, die dann P-7 erneut berühren würde.
P-O19 [O, Fremdspec] Descriptor-Wachstum in langlebigen Compilerinstanzen. Wiederholtes Hot-Reload lässt alte kanonische Descriptoren zunächst inaktiv zurück. CompilerDB muss entscheiden, ob und wie nicht mehr erreichbare Descriptoren kompaktierbar sind, ohne Identität und Proof-Verträge zu brechen.
P-O20 [O, Fremdspec] Runtime-Layout-Eigentum. P-10 setzt voraus, dass JME/Runtime alle benötigten Layout-/Policy-Daten unabhängig von einer lebenden CompilerDB besitzt. Die konkrete Materialisierung und Lebenszeit dieser Runtime-Daten muss in Loader/JME festgelegt werden.
Änderungsprotokoll¶
0.3.7 — 2026-09-16¶
Einordnung semantischer Views und Proofs ohne neue Plugin-Mechanik.
- Semantic Views sind gewöhnliche Derived Queries; Proof-Provider sind gewöhnliche Compilersemantik über Queries. Keine
ViewProviderRegistry, keineProofRegistry. - Die Eindeutigkeitsregel für QueryProvider bleibt bestehen; mehrere logische Proof-Regeln werden innerhalb eines Providers oder über getrennte Query-Verträge komponiert.
- Plugins erhalten dadurch kein implizites Recht, sealed/core Sicherheitsurteile zu ersetzen; Provider-Autorität bleibt Sache der CompilerDB-/Fachspezifikation.
- Ob externe Plugins neue Query-Verträge definieren dürfen, bleibt der allgemeinen CompilerDB-Erweiterungsregel überlassen und wird nicht als Plugin-Sonderweg erfunden.
- Folgearbeiten für
jdl-meta-phaseundjade-compiler-db-specentsprechend ergänzt. - Nach Konsolidierung von
jdl-meta-phasev0.2.0 ist P-O8 auf die tatsächlich offene Meta-Allokation verengt; der Eigentümer des Totalitätsnachweises ist nicht mehr offen.
0.3.6 — 2026-09-15¶
Migration auf das refaktorierte Context-/Resource-Modell und Präzisierung der CompilerDB-Fassade.
ContextDescriptor.capabilities,CapabilitySetund die Compilerinstanz-Capability wurden aus dem aktuellen Plugin-Modell entfernt.- Konkrete Runtime-Autorität hängt an Resources und ihren Handles;
ResourceBindingSpecbeschreibt nur Provisionierung/Erreichbarkeit, keine lebenden Handles. requires.osCapswurde ersatzlos aus dem v0.1-Manifest entfernt. Loader-Preflight leitet nur konkrete Resource-Voraussetzungen aus realen Artefaktinhalten ab; eine universelle Rechteliste wird nicht vorgezogen.- Runtime-Load mit deferred Compilerarbeit setzt eine legitime
CompilerInstance-Resource voraus (P-26). - Native Nutzung wird auf provisionierte FFI-/NativeHost-Resources beziehungsweise Host-Provider bezogen.
- QueryProvider erweitern das Query-Substrat; Meta/Engines/Werkzeuge konsumieren bevorzugt die kuratierte
db.*-Fassade. Fassadenkomposition darf Query-Identität, Dependency-Tracking, Caching und Provenance nicht verschlucken.
0.3.5 — 2026-09-12¶
Zwei P-30-Vorläufer und eine Konvention.
- §13.6 listete beim Commit noch „Plugin-Broker-Lifecycle“. Seit P-30 besitzt der Broker den Lifecycle nicht; commitiert wird der Lifecycle der
PluginGenerationsamt ihrer Domänenprojektionen. - Anhang B, P-9 trug noch „Broker hält Lifecycle“. Korrekt ist die Runtime-Projektion. Die Ownership-Entscheidung steht in P-30, nicht in P-9.
- Anhang A ist jetzt ausdrücklich als abgeleiteter Invariantenindex gekennzeichnet. Damit ist eine Abweichung zum Haupttext ein Fehler und kein Auslegungsspielraum — eine Prioritätsregel hätte den Widerspruch stattdessen legalisiert.
0.3.4 — 2026-09-12¶
Zwei Nachzüge aus v0.3.3. Beides Synchronisation, keine neue Semantik.
- Verify-Punkt 11 prüfte nur die Schemaversion der
MetaStructuralForm.GenericDefinitionist nach P-29 ebenfalls versioniert, hatte im Lifecycle aber keinen Zeitpunkt, an dem eine unbekannte Version abgelehnt wird. Punkt 11 deckt jetzt beide Formen ab. - Invariante 37 trug noch die zu breite P-26-Fassung, die in §13.3 bereits auf Runtime-Load eingeschränkt worden war. Der Anhang ist die Stelle, aus der später Implementierungslisten abgeschrieben werden; eine dort stehengebliebene alte Regel ist teurer als eine im Fließtext.
0.3.3 — 2026-09-12¶
Drei Repräsentations- und Eigentumslücken geschlossen. Keine neuen Mechanismen.
Neu festgelegt:
- Deferred Generics haben mit
GenericDefinitioneine benannte Form (P-29). Bisher forderte P-5 die Materialisierungs-Closure, sagte aber nicht, worin der generische Rumpf selbst liegt —MetaStructuralFormklärte nur die Meta-Seite. DeferredCompilerArtifactist klassifiziert: zwei Formen plus ein deklarierter Aufschubgrund (P-28). Target-Abhängigkeit ist dabei ausdrücklich ein Grund und keine dritte Form, weil eine dritte Form für target-abhängigen Code genau die vorgelagerte Backend-Repräsentation wäre, die P-7 als Plugin-ABI ausschließt. v0.1 löst Multi-Target stattdessen über vorgebauteVmModuleArtifacts.GenericDefinitionreferenziert Meta-Rümpfe, statt sie einzubetten. Sonst läge derselbe Rumpf zweimal im Artefakt und die Proof-Prüfung hätte zwei Kandidaten.- Die
PluginGenerationgehört dem Loader (P-30). Plugin Broker und Compiler-Registries halten nur Projektionen. Vorher hätte ein reines Compiler-Plugin einen vollständigen Lifecycle ohne Eigentümer gehabt, weil §18 nur Runtime-Generationen führte.
Korrigiert:
- Verify-Punkt 12 galt für jedes Artefakt mit deferred Bestandteilen. P-26 ist aber eine Admission-Regel für Runtime-Load: Bei einem gewöhnlichen Build läuft der Compiler bereits und es gibt keinen anfordernden Context, dessen X-5-Capability man prüfen könnte. Punkt 12 ist entsprechend eingeschränkt.
0.3.2 — 2026-09-12¶
Gegenprüfung von v0.3.1. Keine neuen Mechanismen; sechs Präzisierungen, eine Reichweitenbegrenzung und zwei Klarstellungen.
Neu festgelegt:
- Der Totalitätsnachweis bekommt mit P-27 eine ausdrückliche Reichweite. Er gilt lokal; Terminierung über
db.*trägt die Zyklusbehandlung der CompilerDB (§7.3). Vorher las sich P-6/P-23 wie eine vollständige statische Zusage, die eindb.*-Aufruf nicht halten kann. PluginGenerationentsteht bereits mitSTAGEDals Kandidat, nicht erst mit dem Commit. P-25 verlangt einen Eigentümer für Init-Besitz; die alte Definition stellte ihn erst nach dem Zeitpunkt bereit, an dem er gebraucht wird.- Die Stage-Sichten sind auf drei getrennt: Resolution View, Coherence View, Replacement Compare View. Vorher standen Auflösung und Kohärenzprüfung in einer Liste, obwohl §8.4 sie bewusst verschieden weit fasst.
- Verify prüft zusätzlich die Schemaversion der
MetaStructuralFormund die Compilerautorität nach P-26. Beide waren normativ gefordert, hatten aber keinen Prüfzeitpunkt im Lifecycle. - Ein gescheiterter Kandidat durchläuft kein
DRAINING. Begründet, nicht bloß festgestellt:DRAININGschließt fremden Eintritt, und einen fremden Eintritt gab es nie. - Das Zustandsdiagramm zeigt jetzt auch die Fehlerpfade. Vorher standen sie nur im Fließtext darunter.
Klargestellt:
- Ein Plugin ohne
initEntrydurchläuftINIT, ohne Code auszuführen; der Rückbaupfad bleibt leer. - Beim Laden zur Laufzeit nach P-10 gibt es kein Linken eines Programmartefakts. Die Spurlosigkeitsgrenze aus §1.4.1 ist dort die Domänengrenze zwischen Compiler- und Runtime-Seite.
- Ein operativer Abbruch nach begonnenem Init verweist auf §13.5 statt nur auf das Verwerfen der Transaktion.
- Die Autoritätskette in §15.2 ist ausgeschrieben statt über Ringnummern angedeutet.
0.3.1 — 2026-09-12¶
Gegenprüfung der beiden neuen Achsen aus v0.3.0: deferred Compile-Zeit-Arbeit und Lifecycle in einem weiterlaufenden System.
Neu festgelegt:
- Deferred Meta besitzt mit
MetaStructuralFormeine benannte, kanonische und versionierte distributierte Darstellung oberhalb eines allgemeinen CFG/JBC. - Totalitäts-/Terminierungsbedingungen bleiben darin strukturell sichtbar; lokale IR-/JBC-Formen sind reine Consumer-Interna.
- Die Spurlosigkeit von Meta wird auf das ausgelieferte Laufzeitartefakt eingegrenzt; ein
PluginArtifactdarf deferred Meta tragen. - Proof-Prüfung erfolgt am Anfang von Stage nach lokaler Referenzauflösung und vor der ersten Meta-Ausführung.
- Ein Init-Fehler ist kein billiger Transaktionsrollback: Kandidatenbesitz wird über
DETACHING → RECLAIMING → FAILEDabgebaut. - Der rein buildzeitliche Kohärenz-Geltungsbereich entfällt.
(P,T)-Eindeutigkeit gilt über gleichzeitig resolver-erreichbare Beiträge einer CompilerDB-Instanz. - Eine Replacement-Stage maskiert die alte Generation aus Namens-/Provide-Auflösung, hält deren Verträge aber für den Kompatibilitätsvergleich lesbar.
- Runtime-Load mit deferred Compilerarbeit ist ausdrücklich an X-5, die Compilerinstanz-Capability des Contexts, gebunden.
- Provider-Faults werden für CompilerDB-Zwecke in deterministische Fault-Diagnostics und operative Abbrüche getrennt; nur erstere dürfen Query-Ergebnisse erzeugen.
- Der Vor-Cutover-Rollback wurde geschärft: Die alte Generation bleibt unangetastet, aber ein bereits initialisierter Kandidat kann eigenen Teardown benötigen.
- Descriptor-Wachstum langlebiger Compilerinstanzen und das Runtime-Layout-Eigentum wurden als explizite Fremdspec-Abhängigkeiten aufgenommen.
0.3.0 — 2026-09-12¶
Lifecycle und Runtime-Anschluss gegen Context-, Runtime- und Fault-Modell konsolidiert.
Neu festgelegt:
- Runtime-Aktivierung führt keine Type-Engine-Semantik aus; deferred Compilerarbeit bleibt Compilerarbeit.
- PluginId und konkrete PluginGeneration werden getrennt, damit Replacement ohne Identitätsakrobatik möglich ist.
- Deaktivierung folgt
ACTIVE → DRAINING → DETACHING → RECLAIMING → UNLOADED. - Neue Erreichbarkeit wird vor dem Drain geschlossen; Coupling-Kanten werden nach F-25 erst nach Ende der realen Kopplung entfernt.
Holdist ausdrücklich kein Pluginstatus. Es bleibt der ressourcenbezogene Freigabeschutz aus F-28/F-29.- Plugin-Unload löscht keine DescriptorStore-Einträge und nimmt keine TypeIds zurück.
- Compilerbeitragsentzug muss als CompilerDB-relevante Registry-/Input-Änderung invalidierbar sein.
- Manifest-Capabilities werden als Admission-Deklaration eingeordnet; tatsächliche Runtime-Autorität liegt im
ContextDescriptorund wird am Context-Gate geprüft. - Throw, Cancellation, Trap und Fatal bleiben beim Plugin-Shutdown getrennt.
- Native Codeabbildungen dürfen erst nach Ende aller relevanten Holds freigegeben werden.
- Hot-Reload wird architektonisch als vorbereiteter Generation-Cutover mit anschließendem Drain der alten Generation festgelegt.
- Zustandsmigration ist explizit; alte Handles sind kein Migrationsformat.
- Lifecycle-Deregistrierung wird vom verbotenen ad-hoc Runtime-Patching abgegrenzt.
0.2.0 — 2026-09-12¶
Konsolidierung von Artifact-, Meta-, Generic-, Context- und Fault-Semantik:
PluginArtifactträgt fertige und deferred Compile-Time-Anteile.- öffentliche Generics tragen eine Materialisierungs-Closure.
- deferred Meta benötigt lokal akzeptierten Terminierungs-/Totalitätsproof.
- Artefakt-/Compilervertrag, Interface-Fingerprint und CompilerDB-Cache-Identität wurden getrennt.
- native Payloads wurden explizit.
StagingTransactionwurde vom Ausführungs-Contextgetrennt.- Runtime-Anwendungszustand wurde in Contexts statt in den Broker gelegt.
- EngineProvider wurden als QueryProvider-Art eingeordnet.
- Frontend-Parser und Datenparser wurden getrennt.