Wie Jade ausführt¶
Art: Erklärung, nicht Spezifikation. Zweck: Die Laufzeithälfte am Stück verstehen — RuntimeBus, Context, Scheduler, JME und was
instantiateeigentlich tut. Gegenstück:wie-jade-uebersetzterklärt, wie Bedeutung entsteht. Hier steht, wie sie wirkt. Verhältnis zu den Specs: Wo hier etwas anders klingt als in einer normativen Spec, gilt die Spec. Stand: 2026-10-05 — Änderungen gegenüber der Vorfassung am Ende des Dokuments.
1. Der eine Satz¶
Zur Laufzeit wird nichts mehr abgeleitet. Es wird gelesen, gebunden und ausgeführt.
Das ist kein Optimierungsziel, sondern eine Eigenschaft: Das Material zum Ableiten ist gar nicht da. Was der Compiler an Meta-Records, Proof Traces und Refinements hatte, steht im Artefakt nicht mehr.
Das gilt auch für Contexts, die erst zur Laufzeit dazukommen. Sie werden nicht beschrieben, sondern als fertiges Artefakt zugelassen und aktiviert (§5.5).
2. Zwei Substrate, symmetrisch gebaut¶
Jade hat zwei zentrale Kommunikationsschichten, und sie sind absichtlich spiegelbildlich:
CompilerDb → Queries → deterministisch, seiteneffektfrei, cachebar
RuntimeBus → Commands → reihenfolgenabhängig, seiteneffektbehaftet, nie gecacht
Meta-Phase Laufzeit
def ... ->Meta{} +----> def ... ->{}
| | |
v | v
CompilerDb | RuntimeBus
(Queries) | (Commands)
| | |
v | v
Deskriptor -----------------+ Wirkung
Artefakt aus Excerpts
Eine Query darf man wiederholen; das Ergebnis ist dasselbe. Ein Command darf man nicht wiederholen; die Reihenfolge ist Teil seiner Bedeutung.
Deshalb wird das eine gecacht und das andere nie.
2.1 Die Fassaden sind die Kompositionsebene¶
CompilerDB und RuntimeBus stellen das atomare Substrat bereit. Konsumenten sollen dieses Substrat im Normalfall nicht als rohe Endpoint-Sammlung benutzen.
CompilerDB
primitive / derived Queries
↑
kuratierte db.*-Fassade
↑
Meta · Engines · LSP · Werkzeuge
RuntimeBus
primitive Commands
↑
Runtime-Fassaden / Command-Komposition
↑
Engines · Runtime-Dienste · Control Surface
Eine Fassadenoperation darf mehrere Queries beziehungsweise Commands komponieren, ohne deren Identität zu verschlucken.
Bei der CompilerDB bedeutet das: Eine Operation wie db.describe(subject) darf intern resolve, layout, provides und weitere Queries verwenden; Dependency-Kanten, Caching, Early Cutoff und Provenance bleiben trotzdem auf den wirklichen Query-Knoten sichtbar.
Beim RuntimeBus ist die Algebra strenger. Commands sind seiteneffektbehaftet; Komposition muss deshalb Reihenfolge, Fehlergrenzen und gegebenenfalls Rollback-/Atomizitätsgrenzen explizit erhalten. Eine höhere Operation wie storage.openRegion(...) oder resources.create(...) darf auf mehrere Commands lowern, aber nicht heimlich an Ports oder Subsystemzustand vorbeigreifen.
Die Fassade komponiert das Substrat; sie ersetzt es nicht und umgeht es nicht.
3. Der RuntimeBus¶
3.1 Kein Subsystem greift direkt auf ein anderes zu¶
Der Bus routet Commands an Ports. Es gibt keine Abkürzung zwischen zwei Subsystemen.
JDL-Code: jade::vm::pin(handle)
|
v
INTRINSIC-Opcode
|
v
VM-Loop dispatch
|
v
Implementierung
|
v
rt.cmd(JmeRetain)
|
v
RuntimeBus
|
v
JmePort
Die Domänen des Busses:
JME Handles, Resources, MemoryRegions, Allocation/Reclamation-Mechanik
Scheduler Tasks, Yield, Wakeup, Cancellation
Signal Signale und Unterbrechungen
FFI Hostgrenze und native Resources
Messaging Kanäle, Mailboxen, Bridge-Endpunkte
Sync Atomics und Synchronisation
Warum das streng gehalten wird: Jede Abkürzung wäre eine Stelle, an der ein Subsystem den Zustand eines anderen kennt. Genau daran zerfallen Laufzeiten nach ein paar Jahren.
Die höhere Runtime-Fassade ändert daran nichts. Sie darf Commands zusammenstellen, aber sie darf keinen neuen direkten Zugriffspfad zu JME, Scheduler oder anderen Ports schaffen. Der Bus bleibt die Stelle, an der die mechanischen Wirkungen tatsächlich geordnet werden.
3.2 Intrinsics sind die JDL-Seite, Commands die Maschinenseite¶
Ein Intrinsic ist das, was JDL-Code aufruft. Ein Command ist das, was innen daraus wird. Die Trennung erlaubt, die Implementierung auszutauschen, ohne die Sprachoberfläche anzufassen.
3.3 Ein REPL-Kommando ist kein RuntimeBus-Command¶
Das sieht nach Wortklauberei aus und ist der Kern des Missverständnisses.
Ein :… ist zunächst nur ein Control-Surface-Kommando. Erst der Dispatcher entscheidet, welches Substrat zuständig ist:
:why <subject> → <descriptor> → CompilerDB · ProvenanceStore
:type <subject> → CompilerDB
:type <runtime-handle> → RuntimeBus, ggf. plus CompilerDB
:tasks → RuntimeBus
:handles → RuntimeBus
:cost → beides, je nach Frage
Die Form der :why-Frage ist dabei nicht zufällig. Sie lautet nicht „warum dieser Deskriptor", sondern „warum löst dieses Subjekt auf diesen Deskriptor auf" — weil derselbe Deskriptor aus vielen Subjekten folgen kann und die Erklärung pro Subjekt verschieden ist (wie-jade-uebersetzt §4.5).
Die Intrinsics-Spec beschreibt die REPL-Meta-Commands als strukturell Commands über den RuntimeBus. Das stimmt für die Laufzeitseite — und es hat „REPL-Kommando" mit „Runtime-Command" verschmolzen. Genau solche Begriffsverschiebungen erzeugen später Architekturen, die niemand mehr erklären kann.
:why kann gar nicht anders. Es zeigt die Kette, aus der ein Deskriptor folgt — und der Proof Trace wird ausdrücklich nicht in den Excerpt projiziert. In einem ausgelieferten Modul existiert er nicht. In einer REPL schon, weil ein ReplContext eine lebende CompilerDB führt.
:type fällt in beide. Der Laufzeittyp eines Handles kommt über den Bus; die Typbeschreibung dahinter aus der Datenbank.
Dahinter steht dieselbe Regel wie bei semanticPayload und provenance (wie-jade-uebersetzt §4.5 und §4.8), nur auf einer anderen Achse:
Was zur Ausführung gebraucht wird, darf nicht davon abhängen, was zur Erklärung gebraucht wird.
Deshalb läuft die Laufzeit vollständig ohne Compilerwissen. Und deshalb gilt:
Aus einem normalen Artefakt allein, wohlgemerkt. Debug- und REPL-Bündel sind eine eigene Artefaktform und dürfen mehr mitnehmen; ein separates Provenance- oder Symbolbündel wäre ebenfalls denkbar. Die Regel verbietet nicht, Erklärung mitzuliefern — sie verbietet, dass die Ausführung davon abhängt.
Widerspruch zur Spec:
08-intrinsics-vm-api§Gruppe 6 nennt:whyund das Proof-Trace-System als Aufsatz aufjade::vm::handle_policy— also RuntimeBus. Das passt nicht dazu, dass Proof Traces im Artefakt fehlen. Eine der beiden Stellen muss nachziehen.
4. Von der Row zum Context¶
Das ist die Naht, an der die beiden Hälften zusammenkommen.
4.1 Eine Row wird von innen nach außen abgetragen¶
innere Funktion ->{Db, Cache, Throw[E]}
|
v
Aufrufer erbt die Row
|
v
catch eliminiert Throw[E]
|
v
lokaler Handler bindet Cache
|
v
an der Contextgrenze bleibt: {Db}
|
v
Context liefert {Db} per provide (beschrieben im ContextDescriptor)
|
v
main ->{}
Jede Handler-Installation eliminiert die Elemente, die sie beantwortet. Was oben ankommt, hat niemand beantwortet.
Was „oben ankommt“, muss niemand zusammentragen. Die Summe aller deklarierten Rows unter einem Einstiegspunkt ist beim Übersetzen eine Abfrage an die CompilerDB (wie-jade-uebersetzt §6.6, jdl-effect-rows-und-throw §4.7).
4.2 Ein Context ist ein deklarierter Handler-Satz¶
Der Context ist das Oben. Er installiert keine Handler in Form von Code — er deklariert, welche gebunden sind. Der Unterschied zu einem geschriebenen Handler ist die Schreibweise, nicht die Wirkung.
Seit Rows Typen sind (jdl-effect-rows-und-throw §3.6), ist dieses Deklarieren ein gewöhnliches provide auf die Row: Der Context sagt damit, für welche Services er einsteht. Geliefert werden nur Services, nie Throw (§4.3).
Damit ist die leere Row von main kein „dieses Programm benutzt nichts", sondern: An der äußeren Grenze existiert keine unbezahlte semantische Schuld mehr.
4.3 Was der Context nicht bindet¶
Throw nicht. Fehler werden durch catch eliminiert, nicht durch eine Umgebung. Ein Context, der Throw binden könnte, wäre ein globaler Fehlerhandler — genau der unsichtbare Kontrollfluss, den Jade sonst ausschließt.
Resources stehen ebenfalls nicht als parallele Row-Elemente neben Services. Eine Row sagt, welche semantische API Code benötigt. Eine Resource ist dagegen eine konkrete laufzeitgebundene Entität, die durch Context-Aktivierung, einen Service oder eine explizite Operation erreichbar wird.
->{FileSystem} semantische Anforderung
|
v
provide FileSystem for LinuxFileSystem konkrete Bindung
|
v
ResourceBindingSpec z.B. DirectoryResource
|
v
ContextInstance lebender Resource-Handle
|
v
ggf. konkrete Autorität read-only / read-write
Eine Funktion schreibt also weiterhin ->{FileSystem}. Sie schreibt nicht zusätzlich ->{DirectoryResource} oder ->{FsReadCapability}, nur weil die konkrete Implementierung mit einer Directory-Resource arbeitet.
Row Was braucht der Code semantisch?
Binding Welche Implementierung erfüllt es?
ResourceSpec Welche Runtime-Resource wird dafür provisioniert/gebunden?
ResourceHandle Welche konkrete Entität existiert und ist erreichbar?
Autorität Welche konkrete Macht trägt dieses Handle gegebenenfalls?
Nicht jede Resource ist sicherheitsrelevant. Aber wenn Jade konkrete Runtime-Capabilities modelliert, hängen sie an Resources beziehungsweise deren Handles und nicht an einem separaten globalen CapabilitySet.
Services und Autorität liegen getrennt. Im Context stehen die Services, geordnet nach ihrem Typ. Welche konkrete Datenbank, welcher Socket, welches Verzeichnis dahintersteht, steht in den Resource-Bindungen. Ein Service-Name allein erzeugt nie Autorität.
Die konkreten Werte der Resource-Bindungen stehen nicht im Artefakt. Sie kommen bei der Zulassung dazu (§11).
4.4 Vom Context in den Code: Row und Pipe-Liste¶
Ein Context stellt Services bereit. Wie kommen sie in den Code, der im Context arbeitet?
Den Bedarf nennt immer die Row. Das gilt für jede Funktion: Sie schreibt die Services, die sie braucht, nach Typ in ihre Row. Bei gewöhnlichen Funktionen stellt der Aufrufer sie bereit. Suchwort: Evidence Passing.
Bei Funktionsfeldern eines Contexts — etwa seinem run — sagt zusätzlich die Pipe-Liste, woher die Row gebunden wird. .services.* in der Pipe-Liste bietet alle Services an, die der Context-Blueprint registriert hat. Jedes Row-Element wird über seinen Typ an einen dieser Services gebunden.
ShopContext
services: Orders, Payments, Mailer <- Angebot
run: Row ->{Orders, ...} <- Bedarf
|
v
Orders gebunden; Payments und Mailer bleiben ungebunden
Das Angebot darf größer sein als der Bedarf, nie kleiner. Fehlt für ein Row-Element ein passender Service, ist das kein Laufzeitfehler: Die Services eines Context-Blueprints stehen in der Meta-Phase fest, also scheitert schon describe. Eindeutig ist die Zuordnung, weil Services nach Typ registriert sind.
Die Pipe-Liste ist eine Erlaubnisliste. Sie nennt Felder des Blueprints; was nicht darin steht, ist im Rumpf nicht verfügbar. Gewöhnliche Felder werden beim Materialisieren als Werte eingebacken. Zwei Ausnahmen: services wird nicht eingebacken, sondern der Row zur Bindung angeboten. Und für ein Feld mit Blueprint-Typ steht zur Laufzeit, was das instantiate seiner Engine geliefert hat (jdl-blueprint-spec §3.7). Das darf keine Resource-Handles enthalten; Lebendes kommt auch hier nur über die Row.
Beschrieben wird die Bindung im Deskriptor, vollzogen bei der Aktivierung. Der Deskriptor enthält nur, was gebunden wird, und bleibt damit serialisierbar. Erst wenn der Context aktiviert wird, bekommt die Funktion ihre konkreten Services.
Was gebunden wurde, überlebt seinen Context nicht. Endet der Context, endet auch der Zugriff.
Zur Laufzeit entsteht keine neue Bindung. Wer einen Service weder über den Aufrufer noch über die Pipe-Liste seines Contexts bekommen hat, bekommt ihn nicht.
Folge für main: main liegt außerhalb jedes Contexts und hat eine leere Row. Es kann die Services eines Contexts nicht selbst benutzen. Es baut den Context auf und reicht an ihn weiter (§5.5).
Die Einzelheiten stehen in jdl-blueprint-spec §5.4.
4.5 Das Row-Bündel¶
Eine Row ist eine Menge: Sie hat keine Reihenfolge, {Orders, Payments} und {Payments, Orders} sind dieselbe Row. Zur Laufzeit wird eine gelieferte Row aber als Bündel von Bindungen an Aufrufe weitergereicht, und ein Bündel hat Plätze in fester Reihenfolge. Die aufgerufene Funktion findet Orders nicht über eine Suche nach dem Namen, sondern an einem bestimmten Platz (Evidence Passing, jdl-effect-rows-und-throw A-6).
Die Reihenfolge ist die der Normalisierung. Dieselbe feste Ordnung, nach der die Row beim Übersetzen verglichen und gehasht wird (wie-jade-uebersetzt §6.6), bestimmt auch die Plätze des Bündels. Es gibt also keine zweite Regel, die man pflegen müsste.
Trusted Core. Compiler und VM müssen diese Ordnung nach exakt derselben Regel berechnen. Weicht sie an einer Stelle ab, landet Orders im Platz von Payments, und die Funktion ruft eine falsche Implementierung auf, ohne dass ein Typfehler auffällt. Der Verifier prüft deshalb, dass ein Aufruf das Bündel in der kanonischen Ordnung übergibt (jdl-effect-rows-und-throw G-8).
Die Summe bestimmt das Bündel eines Contexts. Was ein Context bei der Aktivierung bindet, ist genau die Summe der Rows seines Aufrufgraphen (§4.1). Mehr braucht er nicht zu binden; weniger wäre schon beim Übersetzen ein Fehler.
Im Bündel stehen nur Laufzeit-Rows. Jede Row gehört zu genau einer Phase (jdl-effect-rows-und-throw §3.7). Meta-Rows werden in der Meta-Phase erfüllt, im Compiler, und erreichen die VM nie. Der Lader nimmt nur Deskriptoren an, deren Rows Laufzeit-Rows sind (jdl-effect-rows-und-throw G-11). Weil die Phase ein Bit im Row-Deskriptor ist, ist das eine einfache Prüfung; die VM muss dafür keinen einzigen Meta-Service kennen.
Das Format des Bündels ist Implementierungsdetail. Festgelegt sind nur die Ordnung und die Phase.
5. Der Context als Umgebung¶
5.1 Was er ist und was nicht¶
Ein Context ist die geprüfte, descriptor-materialisierte Umgebungsgrenze einer Ausführungsdomäne.
Er ist keine zweite Runtime und kein Service-Locator. Er führt nichts aus. Er liefert die geprüfte Verdrahtung.
Context = was darf hier existieren und benutzt werden
Engine = was geschieht innerhalb dieser Grenze
Ring 0 = wer führt es mechanisch aus
5.2 Die ContextApi ist ein internes Gate¶
Niemand bekommt die ContextApi in die Hand, auch Engines nicht. Autorität kommt in Jade ausschließlich über die Row (§4.4, jdl-blueprint-spec §9.1). Ein zweiter Zugang daneben wäre genau der ambiente Locator, den Jade sonst ausschließt: Wer nach Typ fragen kann, bekommt alles, was sein Context hat.
Die ContextApi ist deshalb kein Parameter, sondern die Stelle in Ring 0 und Ring 1, durch die Bindungen laufen, wenn ein Context aktiviert wird. Ein Context-Name oder Resource-Typ allein erzeugt keine Autorität. Eine Resource wird legitim durch Aktivierungsbindung, Ableitung oder Service-Ergebnis erreichbar. Über eine Bridge wandert sie nicht; dort wandern Operationen zu ihr (§5.4).
Was wann geprüft wird. Seit Row und Pipe-Liste steht vieles schon beim Übersetzen fest:
beim Übersetzen (describe) Service-Binding: Row gegen Angebot, fehlender Service ist ein Fehler;
Summe der Rows des Aufrufgraphen gegen das, was der Context liefert
bei der Zulassung Resources nach den Bindungsangaben herstellen, Bedarf gegen Envelope;
bei geladenen Contexts: deren Summe gegen das Angebot des Elternteils
bei der Aktivierung Rows an die Services binden, Resources an die Domäne hängen;
vorher verschachtelte Blueprints instanziieren, innere zuerst
zur Laufzeit Handle-Gültigkeit, Lifetime, Scheduler- und Storage-Limits
Was statisch bekannt ist, meldet der Compiler beziehungsweise Materializer. Was erst zur Laufzeit sichtbar wird, wird an der Handle-Gültigkeitsprüfung abgewiesen.
Zulassung und Aktivierung sind zwei Momente. Bei der Zulassung stellt Ring 0 die Resources her, die die Bindungsangaben verlangen, etwa einen lauschenden Socket (§11.2). Bei der Aktivierung bekommt der Context sie gereicht, und die Rows seiner Funktionsfelder werden gebunden (§4.4). Dazwischen läuft kein JDL-Code des Contexts.
Das Envelope ist nicht Teil davon. Die ContextApi prüft die Grenzen des Contexts. Das Envelope legt die Grenzen der Maschine fest; es wird beim Start gegen den Bedarf geprüft und danach über die Sandbox des Betriebssystems durchgesetzt (§5.6, §11.3), nicht über die ContextApi.
5.3 Sie ist nicht dasselbe wie eine Intrinsic¶
Die beiden liegen auf verschiedenen Ebenen.
Code mit seiner Row
|
v
ContextApi (intern) hat die Bindung bei der Aktivierung hergestellt
|
v
Service- oder Resource-Bindung
|
v
Intrinsic jade::intrinsics
|
v
RuntimeBus führt Command aus
|
v
JmePort, SchedulerPort, ...
Eine Intrinsic ist der Weg zur Maschine. Was sie prüft, ist konkrete Gültigkeit: lebt der Handle noch, stimmt sein Generationszähler, ist die Operation für diese Resource mechanisch zulässig?
Die ContextApi ist das Gate davor. Sie hat bei der Aktivierung festgelegt, welche Bindungen und Resources in dieser Domäne legitim erreichbar sind — und zwar genau die, die die Rows verlangen.
Beispiel: Ein PluginContext besitzt keinen gebundenen Netzwerk-Root beziehungsweise keinen daraus abgeleiteten Socket-Endpunkt. Dann kann er nicht dadurch Netzwerkzugriff erhalten, dass er einen Service-Namen kennt. Hat er dagegen einen legitimen Socket-Handle erhalten, prüft Ring 0 weiterhin dessen konkrete Gültigkeit; ein bereits geschlossener Socket trappt unabhängig von der Context-Beschreibung.
ContextApi / Binding ist diese Entität hier legitim erreichbar?
Resource-Handle welche konkrete Entität/Autorität besitze ich?
Intrinsic / Ring 0 ist diese konkrete Maschinenoperation gültig?
Wo die ContextApi liegt: in Ring 0 und Ring 1 (Namensraum jade::), nicht in jdl::. Sie liest materialisierte Contextdaten und vermittelt danach an die unteren Mechanismen. Weder Anwendercode noch Engines sehen sie. Beide arbeiten mit Services aus ihrer Row und mit explizit erhaltenen Resource-Handles.
5.4 Bridges¶
Zwischen zwei Contexts gibt es ohne Bridge keinen Pfad — nicht „verboten, aber technisch möglich", sondern nicht adressierbar.
Die beiden Wege sind verschieden:
| Parent-Expose | Bridge | |
|---|---|---|
| Richtung | nach unten | zwischen Domänen |
| Was fließt | Aktivierungsbindungen: Services, ResourceBindingSpecs, Policies | Nachrichten, Queries, Commands; Operationen auf Resources, nie die Resources selbst |
| Vertrauen | dieselbe Domäne | isolierte Domänen |
Und Feldkomposition verleiht kein Vertrauen: Ein PluginContext als Feld eines ApplicationContext ist strukturell dessen Kind, aber kein Trust-Descendant. Parent-Expose ist deshalb nur Aktivierungsbindung an einen ausdrücklich vertrauenswürdigen Descendant. Nach der Aktivierung ist es kein allgemeiner Handle-Kanal; neuer Cross-Context-Zugriff läuft über eine Bridge.
Zwei Schichten. „Bridge“ ist das Wort für das Ganze, so wie „das Netz“. Darunter liegen die Exchange für die Bedeutung — Anschlüsse, Rollen, Codec, Fehler — und das Fabric für die Mechanik: den Transport über jede Entfernung, hardwarenah wie das Fabric einer CPU (jdl-nachtrag-offene-dokumentation H1).
Die Mechanik variiert — local, thread, process, node, mock —, die Semantik der Oberfläche nicht. Gewöhnliche Werte gehen über Codec/Kopie. Lebende Resources wandern nicht: Sie bleiben bei ihrem Besitzer, Operationen wandern zu ihnen, Ergebnisse kommen als Werte zurück. Der Code hält dabei keine fremden Handles, sondern benutzt Services, deren Implementierung lokal oder über die Bridge arbeitet (Nachtrag H8). Ein Resource-Handle ist kein Wire-Wert. Transport-Agnostik bedeutet deshalb gleiche semantische Oberfläche, nicht identische Mechanik.
Verschieben darf nur das Fabric. Bei kurzer Entfernung (local, thread) darf es eine Resource intern trotzdem verschieben, etwa eine angenommene Verbindung an einen Worker, statt jede Operation darauf zurückzuschicken. Für den Code ist das unsichtbar und ändert keine Zusage: Eine Line verspricht über jede Entfernung nur, was sie auch über die größte halten kann (Nachtrag H7).
Trusted Core (Vorschlag, Nachtrag H8): Ein solches Verschieben ist zugleich Autoritätsübertragung. Die Zielseite darf danach niemals stärkere Autorität tragen als die Quelle. Ein Verweis aus der Ferne trägt die Generation der Resource, und der Besitzer prüft sie bei jeder Operation. Bei FFI entscheidet zusätzlich die Isolationsform, ob ein Absturz die VM mitnimmt — das ist kein Performanceunterschied, sondern Teil des Vertrauensmodells.
Widerspruch zu älteren Specs:
spec-context-bridge§1.4 und §2 (Regeln B-3, B-5) sowiespec-context-model-refactoredX-10 führen Lease, Transfer und geteilte Handles noch als Wege, auf denen Code eine Resource über eine Bridge reicht. Nach der Entscheidung in Nachtrag H8 müssen sie nachziehen.
5.5 Die ContextEngine und der Einstieg¶
Die ContextEngine ist eine RuntimeEngine. Ihr describe baut in der Meta-Phase den ContextDescriptor. Ihr instantiate läuft zur Laufzeit und erzeugt aus dem Excerpt dieses Deskriptors die laufende Domäne; die Bindungen aus der Zulassung kommen bei der Aktivierung dazu (§5.2). Es kann scheitern und meldet das als gewöhnlichen, behandelbaren Fehler.
Zur Laufzeit wird nie beschrieben, nur zugelassen und aktiviert. Das gilt auch für Contexts, die erst zur Laufzeit geladen werden, etwa ein Plugin. Ein Loader lädt ein fertiges Artefakt, Ring 0 lässt es zu (§11), und erst dann wird es aktiviert.
Beim Laden nennt der Aufrufer die Schnittstelle, die er erwartet. Zugelassen wird nur, was genau diese Schnittstelle erfüllt — geprüft über ihren Hash (wie-jade-uebersetzt §4.9).
Ein geladener Context bringt seinen Bedarf mit. Seinen Aufrufgraphen kannte der Compiler beim Bauen des Elternteils nicht; die Summe seiner Rows (§4.1) konnte dort also niemand prüfen. Deshalb steht sie im Excerpt seines Deskriptors. Bei der Zulassung vergleicht Ring 0 sie mit dem, was der Elternteil freigeben kann. Passt es nicht, wird der Start abgelehnt — nicht erst der erste Aufruf, der einen fehlenden Service bräuchte. Das ist dieselbe Haltung wie beim Envelope (§5.6): Bedarf gegen Angebot, vor der ersten Zeile JDL-Code (jdl-effect-rows-und-throw G-10).
Services des Elternteils erhält ein geladener Context nur, wenn der Elternteil sie ausdrücklich freigibt (Parent-Expose, §5.4).
main ist die Haustür. Es enthält keine Auswahllogik und hat eine leere Row. Es nimmt die Argumente als Daten entgegen, baut per with den Root-Context über dessen ContextEngine auf und reicht an dessen run weiter.
Welcher Einstiegspunkt gemeint ist, entscheidet der Root-Context selbst — sichtbar in seinem run, nicht versteckt in einem Dispatcher davor.
Auch die ContextEngine bekommt ihre Autorität über die Row. Zum Anlegen einer Domäne braucht sie einen privilegierten Service aus jade::, und der steht in der Row ihres instantiate wie jeder andere. Damit nicht jede Bibliothek Domänen anlegen kann, darf diesen Service nur Code aus Ring 1 in seine Row schreiben; prüfen muss das der Materializer.
Offen: Woher kommt dieser Service für den ersten Context?
mainhat eine leere Row und baut den Root-Context perwithauf. Die Bootstrap-Frage ist damit auf genau ein Row-Element geschrumpft, dessen Herkunft noch fehlt. Name und Form des Service sind ebenfalls offen.
5.6 Das Envelope¶
Ein Envelope ist eine Hülle um einen oder mehrere Contexts. Es ist für einen Context, was ein Context für eine Funktion ist: die äußere Grenze.
Es verengt nur. Ein Envelope legt Maschinenwahrheiten fest, die höchstens enger werden dürfen: welche Dateien, welches Netz, wie viel Speicher, wo ausgeführt werden darf. Es schreibt nie in einen Context hinein und fügt nichts hinzu.
Bedarf gegen Erlaubnis. Der Context bringt seinen Bedarf mit, das Envelope die Erlaubnis. Passt der Bedarf nicht in die Erlaubnis, wird der Start abgelehnt — nicht erst der erste unerlaubte Zugriff zur Laufzeit.
Voreinstellung: nur der Bedarf (Vorschlag, Nachtrag C2). Ohne ausdrückliche Angabe erlaubt ein Envelope genau das, was die umschlossenen Contexts brauchen.
Was nicht ins Envelope gehört. Fairness und Scheduler-Konfiguration stehen im ContextDescriptor (§9.1). Wünsche zur Platzierung darf das Envelope nur als Einschränkung ausdrücken, nicht als Entscheidung. Entscheiden tut der Scheduler.
Offen: die Algebra, mit der mehrere Envelopes und ihre Einschränkungen kombiniert werden.
6. Engines zur Laufzeit¶
6.1 instantiate leitet nichts ab¶
EngineDescriptor EngineRuntimeConfig
(beim Übersetzen geprüft) |
| v
v Scheduler
instantiate(excerpt) |
| |
v |
EngineInstance <-------------------+
(lebt, hat Zustand)
|
v
Handle (gehört der JME)
Am Beispiel einer HTTP-Engine (wie-jade-uebersetzt §7.2):
Eine gewöhnliche Effect Row. Keine Meta-Phase. Die Row nennt, was instantiate braucht, etwa Netz und Storage, und welche Fehler es werfen kann. Die Service-Elemente bindet der Context wie bei jeder anderen Funktion. Die Fehler nicht; die muss der Aufrufer behandeln (§4.3).
Im Engine-Protokoll steht für diese Services ein Row-Platz, den die HTTP-Engine über ihre Signatur füllt (jdl-blueprint-spec §9.1). Wer die Engine benutzt, sieht dort die konkreten Services, keinen Platzhalter.
Storage steht nur da, wo es gebraucht wird. Die Engine verlangt Storage ausdrücklich, weil sie eine benannte Region kontrolliert. Gewöhnliche Allokation würde nicht deshalb jede Row um Storage erweitern (§10.4).
Kein Zugang an der Row vorbei. instantiate bekommt den Excerpt seines Descriptors und sonst nur, was seine Row nennt. Einen Parameter für den Zugang zum Context gibt es nicht (§5.2).
Umgebungsabhängiges kommt gebunden. Der Socket, auf dem der Server lauscht, wird bei der Zulassung hergestellt (§11.2). Die Engine sieht ihn nur über einen Service in ihrer Row, den der Context bei der Aktivierung an diese Resource bindet; die Portnummer sieht sie nie.
Sie liest, statt zu rechnen. Die Routentabelle entstand beim Übersetzen und steht im Excerpt.
6.2 Nur die Instanz lebt¶
EngineDefinition Code, hat nie Zustand
EngineDescriptor geprüfte Daten
EngineRuntimeConfig geprüfte Daten für Ring 0
EngineInstance lebt, hat Zustand, sichtbar über ein Handle
Eine EngineDefinition darf keine Scheduler-Queues, keine Handle-Tabellen, keinen JME-Speicher besitzen. Nicht als Verbot — sie hätte keinen Ort dafür.
6.3 Was über die Phasengrenze nicht geht¶
Kein Zustand. Was describe sich gemerkt hat, ist weg.
Keine Werte. Ein MetaRecord war ein JME-Wert in jenem Lauf; im Artefakt liegt eine Bytefolge.
Keine Ids. Eine Id der CompilerDB ist ein Arena-Index. Im Excerpt steht an ihrer Stelle der Excerpt-Hash des Ziels, und der wird beim Laden auf eine Laufzeit-Id abgebildet.
7. Proto und Frame¶
Dieselbe Form wie bei Deskriptor und Instanz, nur eine Ebene tiefer und für Funktionen statt für Typen.
7.1 Der Proto entsteht beim Übersetzen¶
Ein Proto ist das kompilierte Funktionsobjekt: code[], constPool[], regLayout, debugSideTable. Er ist unveränderlich, und mehrere Ausführungen derselben Funktion teilen sich denselben.
Type Engine (liefert Fakten)
|
v
Generator (reiner Leser)
|
v
Proto (unveränderlich, geteilt)
|
| Call
v
Frame (eine Ausführung)
Die Type Engine liefert Typauflösung und Internierung, semantische Typfakten aus den Refinements, Layout mit size, align und Feld-Offsets sowie die materialisierte Repräsentationsklassifikation: liegt ein Wert inline im Registerfile oder wird er über einen Handle repräsentiert? Storage-Backing, Allocator und Region-Lifetime sind davon getrennte Context-/Storage-Semantik.
Der Generator ist reiner Leser. Er ruft Layout- und Policy-Queries ab, senkt die Semantik vollständig in Core-Instruktionen ab und schreibt den Proto. Er entscheidet nichts über Bedeutung — er transformiert bewiesene Artefakte.
Die JME hat dabei keine Rolle. Es läuft noch nichts.
7.2 Der Frame entsteht beim Call¶
Die VM baut ihn: Header plus Registerfile, aus VM-eigenem Speicher. Dann interpretiert sie code[] und arbeitet auf den Bytes.
proto Referenz auf den Function Prototype
pc Program Counter
caller Link zum aufrufenden Frame
Registerfile flacher Byte-Buffer, Layout aus dem Proto
Das Registerfile ist kein Array uniformer Slots. Jedes Register hat Offset und Größe aus dem Proto. JadeValue kommt nur an Systemgrenzen vor — Call, Return, Intrinsics, FFI —, innerhalb einer Funktion gar nicht.
Die JME wird nur bei handle-relevanten Instruktionen aktiv: NewStruct, GetField, Drop und Verwandte. Bei Arithmetik auf Immediates sieht sie den Frame nie.
Die Type Engine ist zur Laufzeit nicht beteiligt. Ihre Entscheidungen sind im Proto materialisiert.
7.3 Ein Frame ist kein Block¶
Scopes, with-Blöcke und if-Zweige sind lexikalische Semantik und erzeugen keine Frames. Was von einem Scope übrig bleibt, sind Drop-Instruktionen im Strom, die der Compiler dort platziert hat, wo eine Lebensdauer endet — und der Verifier stellt sicher, dass niemand danach noch zugreift.
Ein Frame gehört zu genau einer Funktionsausführung, von Call bis Return oder Trap.
8. Es gibt kein Kabel zwischen den Substraten¶
Zwischen CompilerDB und RuntimeBus fließt nichts. Was hinübergeht, geht durch das Artefakt, und das ist ein Bruch, kein Kanal.
CompilerDB (Deskriptoren, Ids, Kanten, Traces)
|
v
Selektion, Projektion
|
v
Artefakt (Excerpts, Verweise über Hash)
|
v
Laden und Zulassen
|
v
Laufzeitdaten
|
v
RuntimeBus (Commands)
Der RuntimeBus sieht nie einen CompilerDB-Eintrag. Er sieht Commands, und die tragen Werte, die längst aus dem Artefakt gelesen wurden.
Warum die Trennung nötig ist, sieht man an dem, was sonst hinüberginge: Eine Id der CompilerDB ist ein Arena-Index und zeigt im nächsten Lauf woandershin. Ein Handle wäre lauf-lokal. Ein Proof Trace verweist auf Quelldateien, die auf der Zielmaschine nicht existieren.
Der Fall, der wie eine Ausnahme aussieht und keine ist: Beim Übersetzen läuft eine VM, also existieren beide Substrate gleichzeitig im selben Prozess. Eine Meta-Funktion, die ein MetaRecord baut, alloziert über die JME — und die hängt am RuntimeBus. Aber auch dort fließt nichts direkt: Die Meta-Funktion liest über die kuratierte db.*-Fassade und schreibt über die vorgesehenen Patch-/Materializer-Wege. Die Fassade darf Queries komponieren, ohne deren Dependency-/Cache-Identität zu verschlucken. Dass ihre Zwischenwerte JME-Objekte sind, ist eine Eigenschaft der Ausführung, keine Brücke.
In der Gegenrichtung gibt es nur eine Tür, und sie ist beschriftet.
Laufzeitzustand wird niemals implizit CompilerDB-Zustand. Laufzeitdaten können ausschließlich über eine ausdrückliche Input-Grenze zu neuen Compiler-Eingaben werden.
Der Determinismus bleibt damit heil: Ein Input ist ein Input, er bekommt eine Revision, und alles, was darauf beruht, invalidiert nach den üblichen Regeln. Eine Query, die heimlich einen Laufzeitwert läse, wäre dagegen nicht mehr cachebar.
Und der Weg für aufgezeichnete Profile, Traces oder Benchmarkdaten bleibt offen — sie wären dann gesetzte Inputs wie eine geänderte Quelldatei, nicht ein Kanal am Modell vorbei.
9. Der Scheduler¶
9.1 Er kennt keine Semantik¶
Der Scheduler kennt ausschließlich engineId → EngineRuntimeConfig. Was eine HttpEngine bedeutet, weiß er nicht.
Reduction-Limits, Timer-Wheels, Fairness- und andere Scheduler-Policies hängen an dieser Config, nicht an der Definition.
Woher die Config kommt: aus dem ContextDescriptor, zur Laufzeit aus dessen Excerpt. Der SchedulerEngineDescriptor beschreibt die Mechanik — welche Policies es gibt, welche Konfiguration gültig ist. Was ein einzelner Context davon benutzen darf, steht in seinem eigenen Deskriptor.
SchedulerEngineDescriptor
↑ referenziert
┌────┴────┬─────────┐
Context A Context B Context C
└────┬────────┘
↓
ein Scheduler, ein Worker-Pool
Viele Contexts teilen sich einen Scheduler. Ein Scheduler pro Context hieße, dass jeder RequestContext einen Threadpool bekommt. Es gibt einen Scheduler pro Prozess, und er gehört Ring 0.
Damit wählt der Context die Strategie, der Scheduler führt sie aus, und keiner kennt die Semantik des anderen.
Offen: Ein
maxParallelismist eine Obergrenze, keine Zusage. Mehrere Contexts an einem Pool können sich gegenseitig aushungern. Wer eine Untergrenze braucht — ein Plugin, das nicht verhungern darf —, braucht eine Reservierung oder eine Fairness-Klasse im ContextDescriptor. Ins Envelope gehört sie nicht (§5.6).
9.2 M:N und zwei getrennte Fragen¶
Event-/I-O-Poller Master Clock
| (Engine Timer-Wheel)
| |
+------------+-------------+
|
v
Runnable
|
v
Worker / Run Queue
|
v
VM Loop
Wann wird eine Task wieder lauffähig? Event oder Zeit
Wann bekommt eine lauffähige Task CPU? Run Queue, Worker, Reduction-Budget
Work-Stealing ist die vorgesehene Mehrworker-Strategie: lokale Queue, globale Inject-Queue, Stehlen, parken. Deque-Typ und Victim-Auswahl sind Implementierungsdetail — die beobachtbare Semantik darf davon nicht abhängen.
Es gibt keinen globalen Heartbeat. Die Master Clock bedient nur aktive Timer-Wheels. Ohne Arbeit, Events und Timer darf die Runtime schlafen.
9.3 Suspension ist transparent¶
Eine Funktion, die eine Konfigurationsdatei liest und parst, nennt in ihrer Row das Dateisystem und einen möglichen IO-Fehler — sonst nichts.
Das Lesen der Datei darf die Task suspendieren. Die Funktion bleibt trotzdem eine gewöhnliche JDL-Funktion — es gibt kein async, kein await und keine zweite Funktionsklasse.
Suspendierbarkeit ist kein Row-Element. Es gibt keinen
Async-Effekt.
Das sind zwei verschiedene Fragen, und Jade beantwortet sie an verschiedenen Stellen. Die Folge ist bequem und hat eine Kehrseite: Man kann einer Signatur nicht ansehen, ob ein Aufruf anhalten kann. Wo das gebraucht wird — etwa bei einem Callback, der zwischen fremden Stackframes läuft — braucht es einen anderen Mechanismus.
9.4 Vier Ausgänge, die nicht dasselbe sind¶
spawn
[*] ------------> Runnable <--------------------------+
| ^ |
dispatch | | yield | wakeup
v | |
Running ------ event / timer ----> Waiting
|
| return / throw / trap / cancel
v
Completed ---> [*]
Schedulerzustand und Ausgang sind getrennte Dimensionen. Completed heißt nicht Erfolg.
Ein Throw[E] unwindet nie durch den Stack einer anderen Task. An der Taskgrenze wird es zu einem materialisierten Failure; eine Join-Abstraktion darf es im wartenden Task lokal wieder als Throw[E] erzeugen.
Throw, Trap und Cancellation bleiben drei verschiedene Dinge. Ein Fehler ist behandelbar, ein Trap ist es nicht, ein Abbruch ist keins von beidem.
10. Die JME¶
10.1 Kein tracing Garbage Collector¶
Jade braucht keinen aufspürenden Heap-Sammler als universelle Speichersemantik. Aber daraus folgt nicht mehr die alte Typ-Policy-Liste Value | RefCounted | Arena | Pool.
Die Achsen sind getrennt:
Repräsentation inline oder Handle
Ownership Unique / Rc / Weak / …
Storage welche MemoryRegion liefert Speicher?
Reclamation individuell / BulkReset / ReturnToPool / …
Lifetime Scope / Context / persistent / …
Rc und Weak sind Ownership-Mechanismen. Arena und Pool sind zusammengesetzte Storage-/Region-Muster. Sie sind keine gleichrangigen MemoryPolicy-Varianten eines Typs.
Unabhängig davon sichern Generationszähler die Handles. Jeder trägt mindestens eine laufzeitgültige Identität wie {index, generation}; wird ein Slot wiederverwendet, steigt der Zähler. Ein veralteter Handle wird erkannt und trappt, statt still auf fremden Speicher zuzugreifen.
10.2 Sie ist mechanische Eigentümerin¶
Compiler / Descriptoren liefern geprüfte Layout-, Ownership- und Storage-Fakten
Context / Storage binden StorageRoles an geprüfte Profile/Regionen
JME materialisiert und erzwingt die mechanischen Invarianten
Speicheridentität, Handles, Generationszähler, MemoryRegions, Allocation-/Reclamation-Mechanik und dort notwendige Refcounts gehören der JME beziehungsweise Ring 0. Kein Usercode, Plugin oder Ring-2-Code fasst diese Tabellen an.
Bei Lebensdauer und Cleanup ist die Verantwortung geteilt:
Compiler / Verifier sichern lexikalische Lifetime- und Escape-Regeln
ResourceEngine trägt Scopeable-/with-Lifecycle
StorageEngine löst StorageRole/Profile/Region-Semantik
JME / VM vollziehen die materialisierte Mechanik
Drop(Custom) ist semantisches Ressourcen-Cleanup und nicht synonym mit „Speicher freigeben“. Mechanisches Reclaim kann andere Regeln besitzen und muss auch nach einem Trap seine eigenen Invarianten halten.
10.3 Was das kostet¶
JDL kennt keine rohen Pointer. Damit sind Cache-Zeilen-Ausrichtung und lock-freie Strukturen in JDL nicht ausdrückbar — sie werden in Zig gebaut und über Handles angeboten.
Das ist kein Mangel, sondern dieselbe Grenze, die die Handles und Generationszähler überhaupt erst ermöglicht. Man kann nicht beides haben.
10.4 Storage wird über Rollen gebunden¶
Nicht: Der Typ wählt Arena, Pool, mmap oder FreeList.
Nicht: Der Context bekommt die JME.
Sondern: Der Context bindet semantische StorageRoles
an geprüfte StorageProfile / MemoryRegions.
Zum Beispiel:
ContextDescriptor
└── storageBindings
├── Default -> GeneralProfile
├── Scratch -> ScratchProfile
└── Request -> RequestProfile
Die Region selbst setzt orthogonale Bausteine zusammen:
MemoryRegion
├── Backing
├── Allocation
├── Reclamation
├── Lifetime
├── Budget
├── Accounting
└── Guarantees
Ein Request-Profil könnte also Mmap + Bump + BulkReset + Scope sein; ein anderer Context kann dieselbe semantische Rolle anders binden. Der Anwendungscode muss diese Mechanik nicht kennen.
Explizite Kontrolle über eine Region kann über den Storage-Service sichtbar werden, etwa ->{Storage} plus Storage.region[Scratch]. Normale Allokation über die Default-Region erzeugt dagegen nicht automatisch einen Storage-Eintrag in jeder Effect Row.
Die operative Speicherhoheit bleibt bei JME/Ring 0. Der Context entscheidet, welche geprüfte Storage-Semantik in dieser Domäne gilt, nicht wie mmap, eine FreeList oder ein Bump-Allocator mechanisch implementiert ist.
11. Das Artefakt als Eingang¶
Hier treffen sich die beiden Dokumente.
DescriptorStore (semanticPayload) wie-jade-uebersetzt
|
v
Selektion, Projektion (gefiltert)
|
v
Artefakt: Excerpts
Kopf, Inhalt, Verweise über Hash
Bindungen offen
|
v
Laden und Zulassen wie-jade-ausfuehrt
Verweise abbilden, Bindungen herstellen
|
v
instantiate, Scheduler, JME
Was im Artefakt liegt: die Excerpts aller Deskriptoren, die von den Wurzeln des Artefakts erreichbar sind — Einstiegspunkte und Exporte. Jeder Excerpt trägt seinen Kopf mit Hash, Rolle und Schema (wie-jade-uebersetzt §4.4 und §4.9).
Was nicht ankommt: Meta-Records, Proof Traces, AST, Spans, Constitution-Regeln, Refinements, Provider-Interna — und keine Werte, die von der Umgebung abhängen.
Aus einem Excerpt lässt sich der Deskriptor nicht rekonstruieren. Das ist der Grund, warum zur Laufzeit keine TypeEngine läuft — das Material dafür ist nicht da.
11.1 Was beim Laden passiert¶
Verweise abbilden. Ein Excerpt zeigt auf andere, und zwar über deren Hash. Beim Laden werden diese Hashes auf Laufzeit-Ids abgebildet. Ein Hash ohne Ziel wird abgelehnt, nicht geraten.
Das ist der einzige verbleibende semantische Abbildungsschritt beim Laden eines fertigen Moduls. Die Prüfung von Hashes und Schemata und das Anlegen der Laufzeitstrukturen kommen mechanisch dazu. Bindungen stellt Ring 0 bei der Zulassung her (§11.2).
Verweise in ein anderes Artefakt zeigen nur auf Schnittstellen. Ring 0 schlägt das Ziel über seinen Namen nach und vergleicht dann den Hash. Passt er nicht, wird abgelehnt. Über den Namen allein wird nie etwas zugelassen.
Vom Export zum Code. Im Ziel-Artefakt zeigt jeder Rumpf auf den Export, den er umsetzt. Ring 0 baut daraus beim Laden einmal ein Verzeichnis „Export → Rumpf“ und prüft, dass jeder Export genau einen Rumpf mit derselben Signatur hat (wie-jade-uebersetzt §4.9). Über dieses Verzeichnis findet ein zugelassener Verweis seinen Code.
Das Schema prüfen. Jeder Excerpt nennt im Kopf sein Schema. Ein Empfänger, der es nicht lesen kann, lehnt ab.
11.2 Bindungen kommen bei der Zulassung¶
Das Artefakt ist über Umgebungen hinweg identisch. Welche Datenbank, welcher Socket, welches Verzeichnis, welches Geheimnis — nichts davon steht im Artefakt. Dort sind die Resource-Bindungen offen.
Gefüllt werden sie bei der Zulassung. Die Angaben dafür liegen außerhalb des Artefakts, eine pro Umgebung. Ring 0 liest sie, stellt die Resources her und reicht dem Context nur die fertigen Handles.
Der Context braucht die Portnummer oder die Adresse der Datenbank damit nie zu kennen. Wer nur einen Handle hat, kann nicht auf die Idee kommen, einen anderen Port zu öffnen.
Gewöhnliche Konfiguration ist keine Bindung. Ein Feature-Schalter oder eine Worker-Zahl sind Laufzeitdaten. Sie kommen als Argumente oder werden zur Laufzeit gelesen. Bindungen sind nur die Werte, aus denen Autorität folgt.
Geheimnisse haben einen eigenen Weg über einen Secrets-Service. Im Artefakt kann höchstens ein Verweis auf ein Geheimnis stehen, nie das Geheimnis selbst.
11.3 Die Reihenfolge beim Start¶
1 Artefakt laden Excerpts, Hashes und Schemata prüfen, Verweise abbilden
2 Bindungen herstellen Resources nach den Angaben der Umgebung bereitstellen
3 Bedarf berechnen was die Contexts mit diesen Bindungen brauchen; für Services
ist das die Summe der Rows aus den Excerpts (§4.1, §5.5)
4 gegen Envelope prüfen passt der Bedarf nicht, wird der Start abgelehnt
5 Sandbox ableiten Einschränkungen des Betriebssystems aus dem Bedarf
6 festschreiben ab hier ändern sich Bindungen und Erlaubnisse nicht mehr
7 JDL-Code läuft
Vor Schritt 7 läuft kein JDL-Code. Nichts, was die Autorität bestimmt, kann deshalb von JDL-Code abhängen.
Offen: das Format der Bindungsangaben, wer sie liefert und wie sie bei der Aktivierung an die Services gelangen, die in den Rows stehen. Das gehört in eine eigene Spec der Startphase.
12. Eine Form, vier Ebenen¶
Wer bis hierher gelesen hat, ist viermal demselben begegnet:
| geprüfte Beschreibung | was daraus lebt | wer baut sie | wer baut das Lebende |
|---|---|---|---|
| TypeDescriptor | Wert | Type Engine | JME |
| EngineDescriptor | EngineInstance | Engine, describe |
Engine, instantiate |
| ContextDescriptor | laufende Domäne | ContextEngine, describe |
ContextEngine, instantiate, über Ring 0 |
| Proto | Frame | Generator | VM |
Vier verschiedene Ebenen, dieselbe Form: Das Linke ist geprüft, unveränderlich und geteilt. Das Rechte lebt, hat Zustand und gehört einer Ring-0-Domäne. Bei den drei Deskriptoren liest die lebende Seite zur Laufzeit deren Excerpt; den Deskriptor selbst gibt es nur in der CompilerDB (wie-jade-uebersetzt §4.4).
Die Regel darunter: Eine Beschreibung wird einmal geprüft und materialisiert; die lebende Seite darf danach nur konsumieren und ausführen.
Das ist vermutlich wichtiger als jede einzelne Deskriptorart. In keinem der vier Fälle leitet die rechte Seite etwas ab — sie liest.
Das ist der Grund, warum das Ganze so wenig Sonderfälle hat. Es wurde nicht viermal etwas Ähnliches gebaut. Es ist einmal eine Form, die viermal passt.
13. Wie sich das anfühlt¶
Vier Eigenschaften, die aus der Bauart fallen statt eingebaut zu sein.
Nicht materialisierte Reflexion kostet zur Laufzeit nichts, weil ihre Daten im Artefakt schlicht nicht existieren. Ein nicht-reified Typ hat nichts, was man abfragen könnte — es gibt keine Tabelle, die man erst weglassen müsste.
Das ist eine Aussage über Metadaten, nicht über die Laufzeit insgesamt. JME, Scheduler, Handle-Prüfungen, Context-Gates und Bridges kosten selbstverständlich etwas.
Nebenläufigkeit braucht keine zweite Sprachhälfte. Kein async, kein await, keine Funktionsfarbe. Eine Funktion, die suspendiert, sieht aus wie eine, die es nicht tut.
Ein Fehler bleibt lokal. Traps sind definiert, Resource-/Context-Grenzen und Handle-Gültigkeit liefern natürliche Fehlergrenzen. Prozessabbruch ist legitim bei Ring-0-Korruption und JME-Invariantenverletzung — nicht, weil eine Query fehlschlug.
Und was nicht passt, startet gar nicht erst. Fehlende Bindungen, ein falscher Schnittstellen-Hash, ein Bedarf über dem Envelope, ein geladener Context, dessen Rows der Elternteil nicht decken kann: All das fällt vor der ersten Zeile JDL-Code auf, nicht mitten im Betrieb.
14. Was offen ist¶
Das Memory-Consistency-Modell. Die Storage-/MemoryRegion-Taxonomie ist inzwischen getrennt; offen bleibt die Sichtbarkeit konkurrierender Speicherzugriffe. Sobald zwei Tasks denselben Wert sehen, muss feststehen, in welcher Reihenfolge sie Schreibvorgänge des jeweils anderen beobachten. Ohne ein klares Atomics-/Ordering-Modell ist lock-freier Code bestenfalls zufällig richtig.
Zyklische Verweise. Verweise im Artefakt laufen über Hashes; wie ein Zyklus gehasht wird, ist offen (wie-jade-uebersetzt §11). Die Zulassung muss dieselbe Regel anwenden wie der Compiler.
Schema-Versionen. Wann ein Empfänger ein anderes Schema lesen darf als das, für das er gebaut wurde, ist offen. Diese Entscheidung trifft Ring 0 bei der Zulassung.
Die Startphase. Format und Herkunft der Bindungsangaben und wie sie bei der Aktivierung an die Services gelangen, sind nicht festgelegt (§11.3).
Die Envelope-Algebra. Wie mehrere Envelopes und ihre Einschränkungen kombiniert werden, ist nicht festgelegt (§5.6).
Abbau nach einem Trap. Der Austritt eines with ist JDL-Code und läuft nach einem Trap nicht. Der Abbau eines Contexts und die Rückgabe geliehener Resources müssen dann mechanisch in Ring 0 passieren. Wie Ring 0 dafür an die nötigen Informationen kommt, ohne JDL-Code auszuführen, ist nicht beschrieben.
Suspendierbarkeit ist nicht ablesbar. §9.3 — kein Row-Element, kein Effekt. Wo es darauf ankommt, etwa bei einem Callback zwischen fremden Stackframes, fehlt ein Mechanismus.
Cross-Context-Resources. Ohne Bridge kein Pfad ist eine starke Aussage. Gewöhnliche Werte können kopiert/kodiert werden; Resources bleiben beim Besitzer, und Operationen wandern zu ihnen (§5.4). Offen sind die Form eines Verweises aus der Ferne je Entfernung, wie lange ein Lease über Entfernung gilt, unter welchen weiteren Bedingungen das Fabric bei local und thread verschieben darf und die formale Abschwächung autoritätstragender Resources.
Callback-Eintritt. Ruft fremder Code nach JDL zurück, betritt er eine Umgebung ohne normalen JDL-Aufrufer. Welcher Context, welche gebundenen Resources und welcher Task/Nursery-Rahmen gelten, ist noch nicht beschrieben.
Was :why ohne Provenance-Quelle tut. Dass die Grundlage fehlen kann, ist geklärt; das Verhalten nicht. Schweigen wäre die schlechteste Antwort — es lässt den Nutzer rätseln, ob das Werkzeug kaputt ist. Eine ausdrückliche Meldung hält die Grenze sichtbar. Das ist eine Produktentscheidung, keine Sprachsemantik.
Und die Zahlen. Wie viele Queries eine mittlere Datei auslöst, wie oft Early Cutoff greift, was ein Meta-Aufruf kostet. Keine davon steht in einer Spezifikation, und alle drei entscheiden, ob das Modell trägt.
Änderungsprotokoll¶
2026-10-05
- §4.1: Der Context liefert die verbleibende Row per
provide; die Summe über den Aufrufgraphen ist eine Abfrage beim Übersetzen. - §4.2: Deklarieren der Handler ist ein
provideauf die Row; geliefert werden nur Services. - §4.5 neu: Das Row-Bündel — Menge gegen feste Plätze, Ordnung aus der Normalisierung, Trusted Core: identische Regel in Compiler und VM, Verifier prüft die Ordnung; die Summe bestimmt das Bündel eines Contexts.
- §5.2: Prüftabelle um die Summe der Rows beim Übersetzen und bei geladenen Contexts ergänzt.
- §5.5: Ein geladener Context bringt die Summe seiner Rows im Excerpt mit; Ring 0 prüft sie bei der Zulassung gegen das Angebot des Elternteils und lehnt sonst den Start ab.
- §6.1: Row-Platz im Engine-Protokoll.
- §11.3: Schritt 3 nennt die Summe der Rows als Service-Bedarf.
- §13: Geladener Context mit ungedeckten Rows als weiterer Fall, der nicht startet.
- §4.5, zweiter Durchgang: Im Bündel stehen nur Laufzeit-Rows; der Lader prüft die Phase (G-11).
2026-10-02
- §5.4: Exchange und Fabric als die zwei Schichten der Bridge eingeführt. Resources wandern nicht, Operationen wandern zu ihnen; Lease, Transfer und SharedHandle als Wege für den Code gestrichen. Nur das Fabric darf bei kurzer Entfernung intern verschieben, unsichtbar für den Code. §5.2 und §14 entsprechend angepasst.
- §4.4: Felder mit Blueprint-Typ als zweite Ausnahme vom Einbacken. §6.3: Excerpt-Hash.
- §11.1: Wie ein zugelassener Verweis im Ziel-Artefakt zum Rumpf findet.
- §5.2: Bei der Aktivierung werden zuerst verschachtelte Blueprints instanziiert (
jdl-blueprint-spec§3.7). - §4.4: Über die Pipe-Liste kommt aus einem verschachtelten Blueprint nur, was keine Resource-Handles enthält.
- §5.3: Ort der ContextApi einheitlich mit §5.2 angegeben (Ring 0 und Ring 1, Namensraum
jade::). - §4.3:
provide FileSystem for LinuxFileSystemin der entschiedenen Form. - §4.1, §5.5, §9.1, §12: Zur Laufzeit wird der Excerpt gelesen, nicht der Deskriptor.
- §5.6: Voreinstellung „nur der Bedarf“ als Vorschlag gekennzeichnet.
- §6.1: Die Engine sieht den Socket über einen Service in ihrer Row.
-
§11.3, §14: Offene Frage zu den Bindungen ohne das alte Parametermodell formuliert.
-
§5.2 neu: Die ContextApi ist ein internes Gate und kein Parameter; Autorität kommt nur über die Row. Was beim Übersetzen, bei der Zulassung, bei der Aktivierung und zur Laufzeit geprüft wird. Zulassung und Aktivierung als zwei Momente. Abgrenzung zum Envelope.
- §5.3 angepasst: Code erreicht Bindungen über seine Row; die ContextApi sieht niemand.
- §5.5: Auch die ContextEngine bekommt ihre Autorität über die Row; offene Bootstrap-Frage für den ersten Context.
- §6.1: Diagramm und Text ohne
api-Parameter;instantiatebekommt den Excerpt. - §12: Tabelle angepasst.
2026-09-29
- Code-Beispiele entfernt und durch technische Beschreibungen ersetzt: §6.1 (
instantiateeiner HTTP-Engine, jetzt mit Bindungsregel statt Port im Deskriptor) und §9.3 (Suspension beim Lesen einer Datei). - Begriffe vereinheitlicht: Mehrzahl „Contexts“, „Deskriptor“ im Diagramm von §2. §3.3: „nicht in den Excerpt projiziert“ statt „Artefakt-Projektion“. §11.1: „Binding-Admission“ durch den Verweis auf die Zulassung in §11.2 ersetzt.
- Alle Mermaid-Diagramme durch reine ASCII-Diagramme ersetzt. Inhalt und Kanten unverändert; Sequenzdiagramme sind als Ablaufbaum dargestellt.
- §1: Klargestellt, dass auch zur Laufzeit geladene Contexts nicht beschrieben, sondern zugelassen und aktiviert werden.
- §4.3: Trennung von Services (nach Typ) und Autorität (Resource-Bindungen) ergänzt; Bindungswerte stehen nicht im Artefakt.
- §4.4 neu: Row und Pipe-Liste — die Row nennt den Bedarf, bei Funktionsfeldern eines Contexts bietet
.services.*in der Pipe-Liste die Services zur Bindung an; Angebot darf größer sein als der Bedarf; statische Prüfung beidescribe; gebunden bei der Aktivierung; Folge fürmain. Ersetzt die frühere Idee einesscope-Feldes. (Korrigiert: Eine erste Fassung stellte Row und Capture-Liste fälschlich als zwei getrennte Wege dar.) - §5.5 neu: ContextEngine als RuntimeEngine; Laden zur Laufzeit nur als Zulassung und Aktivierung;
mainals Haustür, Auswahl im Root-Context. - §5.6 neu: Envelope als verengende Hülle, Bedarf gegen Erlaubnis beim Start, Voreinstellung „nur der Bedarf", Abgrenzung zu Fairness und Platzierung.
- §6.3, §8: Auf Excerpts und Hash-Verweise umgestellt.
- §9.1: Ein Scheduler pro Prozess in Ring 0; Fairness gehört in den ContextDescriptor, nicht ins Envelope.
- §11: Neu gegliedert — Inhalt des Artefakts (Selektion von den Wurzeln, Excerpts mit Kopf), Laden (Hash-Verweise, Schnittstellen-Prüfung über Name und Hash, Schema), Bindungen bei der Zulassung, Startreihenfolge.
- §12: ContextEngine mit
describeundinstantiatein die Tabelle übernommen. - §13: Eigenschaft „was nicht passt, startet nicht" ergänzt.
- §14: „Relokierbare Referenzen" durch „Zyklische Verweise" ersetzt. Neu offen: Schema-Versionen, Startphase, Envelope-Algebra, Abbau nach Trap.