|
| 1 | +# OGAR — Open Graph of Active Record |
| 2 | + |
| 3 | +> [English](README.md) · **Deutsch** · [Philosophie](docs/PHILOSOPHIE.md) |
| 4 | +
|
| 5 | +Das [Active-Record-Muster](https://martinfowler.com/eaaCatalog/activeRecord.html) |
| 6 | +(Martin Fowler, 2003) als kanonische 32-Bit-`classid`. Ein Codebook; |
| 7 | +jeder Consumer zieht, prägt niemals neu. |
| 8 | + |
| 9 | +```rust |
| 10 | +let cid: u16 = HealthcarePort::class_id("Patient"); // 0x0901 |
| 11 | +let render = HealthcarePort::APP_PREFIX | (cid as u32); // 0x0005_0901 |
| 12 | +authorize(actor, cid, Op::Read); // geteilter Grant auf lo u16 |
| 13 | +// // lokal anreichern — deins |
| 14 | +``` |
| 15 | + |
| 16 | +``` |
| 17 | +classid : u32 = 0xAAAA ‖ 0xDDCC beide Hälften sind ADRESSE |
| 18 | + │ │ |
| 19 | + │ └─ lo u16 — WELCHES KONZEPT (geteilte Identität) |
| 20 | + └─────────── hi u16 — WESSEN RENDER (App-eigene Haut) |
| 21 | +
|
| 22 | + ──────► löst auf zu ──────► |
| 23 | + ├─ ClassView die HAUT (pro App) |
| 24 | + ├─ Class die FORM (kanonisch) |
| 25 | + └─ ActionDef + KausalSpec die MAGIE (Verhalten — im Core, |
| 26 | + nie in der Adresse) |
| 27 | +``` |
| 28 | + |
| 29 | +## Active-Record-Querverweis |
| 30 | + |
| 31 | +| OGAR-Vokabular | Rails ActiveRecord | Odoo `models.Model` | |
| 32 | +|-------------------------------|------------------------------------------|------------------------------------------------------| |
| 33 | +| `ogar/Class` | `class WorkPackage < ApplicationRecord` | `class sale_order(models.Model): _name='sale.order'` | |
| 34 | +| `ogar/Association(BelongsTo)` | `belongs_to :project` | `fields.Many2one('res.partner')` | |
| 35 | +| `ogar/Association(HasMany)` | `has_many :line_items` | `fields.One2many('sale.order.line', 'order_id')` | |
| 36 | +| `ogar/Association(HabTm)` | `has_and_belongs_to_many :tags` | `fields.Many2many('res.partner.category')` | |
| 37 | +| `ogar/Mixin` | `include Mentionable` | `_inherit = 'mail.thread'` | |
| 38 | +| `ogar/Enum` | `enum status: { open: 0, closed: 1 }` | `fields.Selection([('draft','Draft'), ...])` | |
| 39 | +| `ogar/Field` | `t.string :subject` (in Migration) | `subject = fields.Char(required=True)` | |
| 40 | +| `ogar/Validation` ¹ | `validates :subject, presence: true` | `@api.constrains('subject')` | |
| 41 | +| `ogar/Callback` ¹ | `before_save :touch_parent` | `@api.depends('field_x')` / `@api.onchange` | |
| 42 | +| `ogar/Scope` | `scope :open, -> { where(state:'open')}` | Such-Domain `[('state','=','open')]` | |
| 43 | + |
| 44 | +¹ Validation/Callback sind **Verhalten** — `ActionDef` + `KausalSpec`, |
| 45 | +erreicht *über* die classid, nie in ihr. Rails, Odoo, Sequel, Django, |
| 46 | +Prisma — gleiches Vokabular, ein IR. |
| 47 | + |
| 48 | +## Render-Linse — ein Konzept, viele Apps |
| 49 | + |
| 50 | +| classid | Konzept (lo u16) | Render (hi u16) | App | |
| 51 | +|---------------|-----------------------------|--------------------------|----------------------| |
| 52 | +| `0x0001_0102` | `0x0102` project_work_item | `0x0001` OpenProject | openproject-nexgen-rs | |
| 53 | +| `0x0007_0102` | `0x0102` project_work_item | `0x0007` Redmine | (Consumer TBD) | |
| 54 | +| `0x0005_0901` | `0x0901` patient | `0x0005` Medcare | medcare-rs | |
| 55 | +| `0x0003_0103` | `0x0103` billable_work_entry | `0x0003` WoA | woa-rs | |
| 56 | +| `0x0004_0103` | `0x0103` billable_work_entry | `0x0004` SMB | smb-office-rs | |
| 57 | +| `0x0001_0103` | `0x0103` billable_work_entry | `0x0001` OpenProject | openproject-nexgen-rs | |
| 58 | +| `0x0007_0103` | `0x0103` billable_work_entry | `0x0007` Redmine | (Consumer TBD) | |
| 59 | +| `0x0002_0103` | `0x0103` billable_work_entry | `0x0002` Odoo | odoo-rs | |
| 60 | + |
| 61 | +Gleiche `0x0103` → gleicher RBAC-Grant, gleiche Ontologie, gleiche |
| 62 | +Cross-Fork-Identität. Fünf Apps rendern *abrechenbare Zeit* auf fünf |
| 63 | +Arten; Planer-Stunden stimmen mit der Abrechnung per Codebook-Lookup |
| 64 | +überein, nicht per Übersetzung. |
| 65 | + |
| 66 | +## Consumer-Muster — vier Züge, nur einer ist deiner |
| 67 | + |
| 68 | +| Zug | Aufruf | Deiner? | |
| 69 | +|-----------|--------------------------------------------|-----------| |
| 70 | +| pull | `Port::class_id(name) -> Option<u16>` | nein — reine Funktion | |
| 71 | +| render | `Port::APP_PREFIX \| (cid as u32)` | nein — typisierter Helfer | |
| 72 | +| authorize | `authorize(actor, cid, op)` ² | nein — geteiltes Grant-Gitter | |
| 73 | +| enrich | deine Domänen-Logik, an `cid` verschlüsselt | **ja** | |
| 74 | + |
| 75 | +² `lance-graph-rbac::authorize` ist `[H]`, gegated auf |
| 76 | +`PROBE-OGAR-RBAC-AUTHORIZE`. Bis es ausliefert, behalte die bestehende |
| 77 | +Auth — führe KEINE `*Bridge` als Notlösung wieder ein. |
| 78 | + |
| 79 | +## Design-Wächter |
| 80 | + |
| 81 | +| Wächter | Lesen vor | |
| 82 | +|---|---| |
| 83 | +| [Consumer Best Practices](docs/OGAR-CONSUMER-BEST-PRACTICES.md) | jeder Consumer-Aufrufstelle (classid · `APP_PREFIX` · `ClassView` · `*Bridge`) | |
| 84 | +| [SurrealQL-AST-Fallen-Pre-Flight](docs/SURREAL-AST-TRAP-PREFLIGHT.md) | Producer→IR · Transcode · Codegen · `.surql`-Authoring | |
| 85 | +| [SurrealQL-AST als Adapter](docs/SURREAL-AST-AS-ADAPTER.md) | Entscheidung Rückgrat vs. Adapter | |
| 86 | +| [Die Firewall](docs/THE-FIREWALL.md) | jeder Hot-Path-Serialisierung (ADR-022/023) | |
| 87 | +| [APP‖class-Codebook-Layout](docs/APP-CLASS-CODEBOOK-LAYOUT.md) | Prägen einer classid oder eines App-Präfix | |
| 88 | +| [classid-RBAC-Keystone](docs/CLASSID-RBAC-KEYSTONE-SPEC.md) | Verdrahten der Autorisierung | |
| 89 | +| [Consumer-Migrations-Anleitung](docs/CONSUMER-MIGRATION-HOWTO.md) | Umzug eines Consumers weg von einer `*Bridge` | |
| 90 | + |
| 91 | +Der Fallen-Pre-Flight (Producer-seitig) und der Best-Practices-Leitfaden |
| 92 | +(Consumer-seitig) sind die zwei Arme einer Grenze: der Pre-Flight hält |
| 93 | +einen *Producer* davon ab, DDL für den Core einzusetzen; der Best- |
| 94 | +Practices-Leitfaden hält einen *Consumer* davon ab, den Core lokal neu |
| 95 | +zu implementieren. |
| 96 | + |
| 97 | +## Architektur |
| 98 | + |
| 99 | +``` |
| 100 | + ─── Quell-ASTs (Producer) ───────────────────────────────────── |
| 101 | + Ruby AR Python Odoo SQL DDL ... |
| 102 | + lib-ruby-parser libcst / ast sqlparser-rs |
| 103 | + │ │ │ |
| 104 | + └──────────┬─────────┴──────────┬─────────┘ |
| 105 | + ▼ ▼ |
| 106 | + OGAR IR (kanonisch) ⟷ ogar-extensions/* |
| 107 | + │ Class · ActionDef · Identity · KausalSpec |
| 108 | + │ |
| 109 | + │ validiert von lance-graph-ontology (+ Cache) |
| 110 | + │ geplant von lance-graph-planner |
| 111 | + │ gespeichert als SoA-spaltenförmig (Arrow IPC, append-only Lance) |
| 112 | + ▼ |
| 113 | + lance-graph-Tripel |
| 114 | + │ |
| 115 | + ├─ Projektionen (Consumer — gleiches IR, andere Ziele): ← ADAPTER |
| 116 | + │ • SurrealQL DDL AST (DEFINE TABLE / FIELD) nur struktureller Arm; |
| 117 | + │ • PostgreSQL DDL (CREATE TABLE / Migration) Verhaltens-Arm |
| 118 | + │ • OpenAPI / JSON-Schema (API-Verträge) lebt im Core |
| 119 | + │ • TypeScript-Typen (Frontend-Interfaces) (Verhaltens-Arm: nur Core) |
| 120 | + │ • Prisma / Drizzle (andere ORMs) |
| 121 | + │ |
| 122 | + └─ Laufzeit (Aktor-Schicht — Pragmatik): |
| 123 | + lance-graph-callcenter (BEAM-Äquivalent über OGAR) |
| 124 | +``` |
| 125 | + |
| 126 | +### Voller Schlüssel (16-Byte-Zoom) |
| 127 | + |
| 128 | +``` |
| 129 | +key = classid(4) │ HEEL · HIP · TWIG (6) │ family(3) │ identity(3) |
| 130 | + ▲ ▲ ▲ ▲ |
| 131 | + Klassen- HHTL-Kaskaden-Tiers Basin- Instanz |
| 132 | + Adresse (256×256 Zentroid- Gruppierung im Basin |
| 133 | + (dieses Doc) Kacheln, O(1)-Distanz) |
| 134 | +``` |
| 135 | + |
| 136 | +## Repository-Layout |
| 137 | + |
| 138 | +``` |
| 139 | +OGAR/ |
| 140 | +├── crates/ |
| 141 | +│ ├── ogar-vocab/ — Rust-Typen: kanonisches IR + Codebook (class_ids, ports, APP_PREFIX) |
| 142 | +│ └── ogar-ontology/ — Präfix-Konventionen, NiblePath-kompatible Identität |
| 143 | +├── vocab/ |
| 144 | +│ ├── ogar.ttl — Turtle/RDF-Kanonform |
| 145 | +│ ├── ogar.json-ld — JSON-LD-Kanonform (geplant) |
| 146 | +│ └── ogar.surql — SurrealQL-DDL-Projektion (nur struktureller Arm) |
| 147 | +└── docs/ — die sieben Wächter oben + das Discovery- / Integration- / ADR-Ledger |
| 148 | +``` |
| 149 | + |
| 150 | +## Producer / Consumer |
| 151 | + |
| 152 | +**Producer** (Quell-AST → OGAR IR): `ruff_ruby_spo` (Ruby AR, liefert in |
| 153 | +[`openproject-nexgen-rs`](https://github.com/AdaWorldAPI/openproject-nexgen-rs) |
| 154 | +aus) · `ogar-python` (Django + Odoo, geplant) · `ogar-sql-ddl` (geplant) |
| 155 | +· `ogar-typescript` (geplant). |
| 156 | + |
| 157 | +**Consumer** (OGAR IR → Ziel): `op-codegen-pipeline` (liefert aus) · |
| 158 | +`ogar-to-postgres` · `ogar-to-surrealql` · `ogar-to-openapi` · |
| 159 | +`ogar-to-typescript` (alle geplant). Alle folgen dem Consumer-Muster: |
| 160 | +**pull · render · authorize · enrich**. |
| 161 | + |
| 162 | +## Laufzeit — lance-graph-callcenter |
| 163 | + |
| 164 | +OGIT ↔ HIRO ↔ OTP/BEAM ist *Ontologie ↔ Laufzeit ↔ Aktor-Substrat*. Das |
| 165 | +OGAR-Analogon ist der Vier-Crate-`lance-graph`-Stack: |
| 166 | + |
| 167 | +| Schicht | OGIT-Welt | OGAR-Welt | |
| 168 | +|---------------------------|-------------------------|--------------------------------------------------------------------| |
| 169 | +| Substrat-Primitive | (roher Graph) | **lance-graph-contract** — NiblePath, Identität, Versionen, Codebook | |
| 170 | +| Ontologie-Schicht + Cache | OGIT-Vokabular + Ext. | **lance-graph-ontology** — OGAR registriert; schnelle Typ-Auflösung | |
| 171 | +| Query / Plan | HIRO-Query-Planner | **lance-graph-planner** — ontologie-bewusste Traversierungs-Optimierung | |
| 172 | +| Aktor-Laufzeit | HIRO-Automation + BEAM | **lance-graph-callcenter** — Dispatch an `ogar/Class`-Aktoren | |
| 173 | + |
| 174 | +`subClassOf` ist der Supervisions-Baum; Ontologie-Updates sind |
| 175 | +Hot-Code-Reload. Die Klasse ist die Aktor-Spezifikation; der Aktor ist |
| 176 | +die laufende Klasse. |
| 177 | + |
| 178 | +## Status |
| 179 | + |
| 180 | +**v0 — auslieferndes Codebook.** [`crates/ogar-vocab`](crates/ogar-vocab) |
| 181 | +ist der sprachneutrale Lift der stabilen C17a–c-Form aus |
| 182 | +`ruff_ruby_spo`. `class_ids`, `ports::*Port`, `APP_PREFIX` sind live; |
| 183 | +die TTL- / SurrealQL-Projektionen in [`vocab/`](vocab) sind generierte |
| 184 | +Artefakte (stabiles URI-Präfix `ogar/`). Vokabular-Repo zuerst, |
| 185 | +Code-Crate zweitens — wie FOAF oder SKOS. |
| 186 | + |
| 187 | +## Lizenz |
| 188 | + |
| 189 | +MIT — siehe [`LICENSE`](LICENSE). |
0 commit comments