Skip to content

Commit 7d68042

Browse files
committed
docs(odoo): digest → OGIT TTL templates → relive agnostically — the Foundry-parity collapse
The operator's framing — "digest Odoo and store it in TTL Jinja Templates in OGIT and relive it agnostically for any verb/entity as a class" — crystallizes the four pieces shipped across this PR (TTL mirror, schema lift, verb-as-class template, dcterms:creator provenance) into one coherent pipeline: Odoo source → ogar-from-python (AST schema filter) → Class IR → ttl_emit → OGIT TTL templates at vocab/imports/ogit/NTO/<Domain>/ with dcterms:creator = bus-compiler → ogar-render-askama → any consumer instantiates any entity-or-verb-as-class with a fresh binding; Python runtime touched only at digest time. Why store digests in OGIT NTO (not a parallel vocab/imports/odoo/): the dcterms:creator author-scan from this same session gives clean provenance — internal-agent authors (bus-compiler, family-codec-smith) signal "ours to revise"; external-human authors signal "upstream-arago, coordinate". The precedent exists today: Accounting/ already has 11 Claude-digested files alongside 23 Viktor Voss originals. The Foundry-parity collapse (the punchline) — Foundry's four-layer platform pitch maps to four pieces in this repo: Foundry ingest → ogar-from-python digest (~1500 LOC, queued) Foundry storage → vocab/imports/ogit/NTO/ TTL (exists) Foundry render → ogar-render-askama::{views, actions} (~200 LOC for actions) Foundry IAM+audit → verb-as-class requires-perm slot + Lance-version-as-audit (exists) Foundry change mgmt → diff -r of digest re-runs (exists) Total marginal code: <2000 LOC for what Foundry charges $$$ for. Adds: * docs/ODOO-DIGEST-TO-OGIT.md (FRAMING v0, 8 sections): pipeline diagram; why-store-in-OGIT-NTO; the four shapes the digester produces (entity-as-class, datatype attribute, association, verb-as-class); v0 mapping table from the 6 already-minted commerce concepts + 9 queued for codebook mint; drift-detector recipe; blockers; Foundry-parity collapse table; cross-references. * .claude/board/EPIPHANIES.md FRAMING entry: the pipeline, the storage-location rationale, the parity-collapse table, and the two concrete next-step deliverables (ogar-from-python + ogar-render-askama::actions) that ship independently in follow-up PRs. No code changes. The architecture has been latent the whole time; this commit makes it visible as one shape.
1 parent b598460 commit 7d68042

2 files changed

Lines changed: 269 additions & 0 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,69 @@
1515
1616
## Entries (newest first)
1717

18+
## 2026-06-22 — OGIT is the canonical template store; Odoo (and any source-AST producer) digests INTO it; consumers relive agnostically via askama
19+
**Status:** FRAMING
20+
**Scope:** Foundry-parity collapse × cross-consumer architecture × digest-once-relive-N
21+
22+
The operator's framing — *"basically digest Odoo and store it in TTL
23+
'Jinja' Templates in OGIT and relive it agnostically for any
24+
'verb/entity as a class'"* — crystallizes the four pieces shipped
25+
across this PR (TTL mirror, schema lift, verb-as-class template,
26+
author-provenance discriminator) into one coherent flow:
27+
28+
```
29+
Odoo source → ogar-from-python → Class IR → ttl_emit → OGIT TTL templates
30+
(stored at
31+
vocab/imports/ogit/NTO/<Domain>/,
32+
dcterms:creator = bus-compiler)
33+
34+
35+
ogar-render-askama
36+
37+
38+
any consumer (woa-rs, smb-office-rs, medcare-rs, q2, future renderers)
39+
re-instantiates any entity/verb-as-class with a fresh binding;
40+
never touches Odoo Python
41+
```
42+
43+
The Python runtime is **only** touched at digest time. After that,
44+
every consumer talks to TTL templates plus the askama engine.
45+
46+
**Why store in OGIT NTO (not a parallel `vocab/imports/odoo/`):** the
47+
`dcterms:creator` author-scan finding from this same session makes
48+
provenance unambiguous without a separate namespace. The precedent
49+
exists today — `Accounting/` already has 11 Claude-digested files
50+
sitting alongside 23 Viktor Voss originals.
51+
52+
**Foundry-parity collapse** (the punchline). Foundry's four-layer
53+
platform pitch (ingest / storage / render / IAM+audit) maps to four
54+
free open-source pieces already in this repo:
55+
56+
| Foundry layer | Our equivalent | New code needed |
57+
|---|---|---|
58+
| Ingest | `ogar-from-python` digest | ~1500 LOC (queued) |
59+
| Storage | `vocab/imports/ogit/NTO/<Domain>/` TTL with `dcterms:creator` | zero (exists) |
60+
| Render | `ogar-render-askama::{views, actions}` | ~200 LOC for actions submodule |
61+
| IAM + audit | verb-as-class `requires-perm` slot + Lance-version-as-audit | zero (exists) |
62+
| Ontology change mgmt | `diff -r` of digest re-runs | zero |
63+
64+
Total marginal code: <2000 LOC for what Foundry charges $$$ for. The
65+
architecture was latent the whole time; the digest→relive framing is
66+
what makes it visible as a single shape.
67+
68+
Concrete next steps (independent, can ship in parallel PRs):
69+
70+
1. `ogar-from-python` digester — structural-arm filter (`_name`,
71+
`_inherit`, `fields.*`, selections); behavioural-arm signatures
72+
(decorator names + action method signatures); drops method bodies
73+
2. `ogar-render-askama::actions` — verb-as-class render path, mirroring
74+
the existing `views/` submodule
75+
76+
Doc: `docs/ODOO-DIGEST-TO-OGIT.md` (FRAMING v0) carries the full
77+
pipeline, the v0 mapping table (6 minted concepts + ~9 queued for
78+
codebook mint), the drift detector recipe, and the Foundry-parity
79+
collapse table.
80+
1881
## 2026-06-22 — Verb-as-class is an ontological askama template — compile-time-validated action declaration, not a quirk
1982
**Status:** FINDING
2083
**Scope:** WorkOrder convention × `ogar-render-askama` integration × Foundry action-type parity

docs/ODOO-DIGEST-TO-OGIT.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# Odoo digest → OGIT TTL templates → agnostic relive
2+
3+
> **For colleagues building anything that needs Odoo as a typed ontology
4+
> outside the Odoo Python runtime.** This is the architectural shape:
5+
> digest Odoo source once into OGIT-shaped TTL templates (stored
6+
> in-tree at `vocab/imports/ogit/NTO/<Domain>/`), then relive any model
7+
> or workflow action agnostically via `ogar-render-askama`.
8+
>
9+
> Status: **FRAMING v0** (2026-06-22). Companion to
10+
> `docs/ODOO-TRANSCODING.md` (the producer spec) and
11+
> `docs/VERB-AS-CLASS-TEMPLATE.md` (the askama-template framing this
12+
> reuses).
13+
14+
The shape in one sentence: **`ogar-from-python` digests Odoo source
15+
once into the OGAR IR; `ttl_emit` writes the IR back as OGIT-shaped
16+
TTL templates stored in the existing OGIT NTO tree; consumers
17+
re-instantiate any model or workflow action by rendering against the
18+
TTL via `ogar-render-askama`, never touching Odoo Python.**
19+
20+
---
21+
22+
## §1. The pipeline
23+
24+
```
25+
Odoo Python source
26+
(addons/<module>/models/*.py)
27+
28+
│ ogar-from-python (AST schema filter)
29+
│ — keeps structural arm: _name, _inherit, fields.*, selections
30+
│ — keeps behavioural-arm SIGNATURES (decorators, action def names)
31+
│ — drops bodies (computed methods, action implementations)
32+
33+
OGAR Class IR (in memory)
34+
35+
│ ttl_emit::emit_entity (semantic bijection)
36+
│ — entity-as-class for models
37+
│ — verb-as-class for workflow action signatures
38+
39+
OGIT-shaped TTL templates
40+
(vocab/imports/ogit/NTO/<Domain>/<DigestedClass>.ttl)
41+
— dcterms:creator = bus-compiler (digester provenance)
42+
— alongside upstream arago TTL (Viktor Voss et al)
43+
44+
│ ogar-render-askama (entity render → views; verb render → actions)
45+
46+
Materialized output (any consumer, any medium)
47+
— HTML view (per-app skin)
48+
— JSON / OpenAPI surface
49+
— SurrealQL DDL / Postgres CREATE TABLE
50+
— SPO triple emit + ACL gate + audit record (action render)
51+
```
52+
53+
The Python runtime is **only** touched at digest time. Consumers
54+
(`woa-rs`, `smb-office-rs`, `medcare-rs`, `q2`, any future renderer)
55+
never depend on Odoo Python, only on TTL + the askama renderer.
56+
57+
## §2. Why store digests in OGIT NTO (not a parallel `vocab/imports/odoo/`)
58+
59+
The `dcterms:creator` author-scan (`OGIT-DOMAIN-LIFT-CATALOGUE.md
60+
§ Verifying domain authorship`) gives clean provenance without
61+
needing a separate storage namespace:
62+
63+
| `dcterms:creator` value | Meaning | Who can change |
64+
|---|---|---|
65+
| `Viktor Voss`, `chris.boos@almato.com`, `fotto@arago.de`, … | Upstream arago/almato | Re-vendor requires arago PR; OGAR consumes via the SHA pin |
66+
| `bus-compiler`, `family-codec-smith`, `Claude (...)`, … | Internal agent digest | Re-run the digester; no external coordination |
67+
68+
The precedent exists today: `vocab/imports/ogit/NTO/Accounting/`
69+
already has 11 files from a prior `Claude (AdaWorldAPI/lance-graph
70+
3-hop optim)` digest sitting alongside Viktor Voss's 23 originals. The
71+
two co-exist, the author-scan distinguishes them, and lifting both
72+
into OGAR's `Class` IR is symmetric.
73+
74+
So **the digest lives where the concept belongs**`account.move`
75+
`Accounting/`, `sale.order``SalesDistribution/`, `stock.picking`
76+
`Transport/` — and the author scan is the discriminator.
77+
78+
## §3. The four shapes the digester produces
79+
80+
| Shape | Source pattern | TTL form | Render path |
81+
|---|---|---|---|
82+
| **Entity-as-class** | `class Foo(models.Model): _name = '...'; <fields>` | `rdfs:Class` + `mandatory-attributes` enumerating field names | view render (`ogar-render-askama::views`) |
83+
| **Datatype attribute** | `field_name = fields.Char/Integer/Selection(...)` (with selection) | `owl:DatatypeProperty` + `ogit:validation-type "fixed"` + `validation-parameter "a,b,c"` | binding validation |
84+
| **Association** | `partner_id = fields.Many2one('res.partner', ...)` | `owl:ObjectProperty` (lifted as SGO verb) OR an entry in the parent class's `ogit:allowed (...)` block | edge in lifted SPO graph |
85+
| **Verb-as-class** | `def action_confirm(self)` workflow method | `rdfs:Class` + slot list (subject = SaleOrder, object = confirmation event) + policy attrs (`requires-perm`, `emits-audit`) | action render (`ogar-render-askama::actions`, queued) |
86+
87+
The last shape is what makes "relive agnostically" possible for
88+
workflow actions: the Python method body stays in Odoo (behavioural
89+
arm), but the **action contract** (what it requires, what it emits,
90+
what it depends on) becomes a typed template any consumer can render
91+
against.
92+
93+
## §4. The mapping table — v0 from the existing codebook
94+
95+
The six commerce/ERP concepts already minted in `ogar-vocab::class_ids`
96+
give the digester its first six targets. Concepts beyond these need
97+
the 5+3 codebook pass (per `docs/APP-CLASS-CODEBOOK-LAYOUT.md §4`)
98+
before mint; the digester can lift them in the meantime with
99+
`Class.name` carrying the Odoo model name and no concept_id assigned.
100+
101+
| Odoo model | Source addon path | OGIT NTO target | OGAR concept | Shape | Concept-mint status |
102+
|---|---|---|---|---|---|
103+
| `res.partner` | `addons/base/models/res_partner.py` | `Accounting/` | `BILLING_PARTY` `0x0204` | entity-as-class | minted |
104+
| `account.move` | `addons/account/models/account_move.py` | `Accounting/` | `COMMERCIAL_DOCUMENT` `0x0202` | entity-as-class | minted |
105+
| `account.move.line` | `addons/account/models/account_move_line.py` | `Accounting/` | `COMMERCIAL_LINE_ITEM` `0x0201` | entity-as-class | minted |
106+
| `account.tax` | `addons/account/models/account_tax.py` | `Accounting/` | `TAX_POLICY` `0x0203` | entity-as-class | minted |
107+
| `account.payment` | `addons/account/models/account_payment.py` | `Accounting/` | `PAYMENT_RECORD` `0x0205` | entity-as-class | minted |
108+
| `res.currency` | `addons/base/models/res_currency.py` | `Accounting/` | `CURRENCY_POLICY` `0x0206` | entity-as-class | minted |
109+
| `sale.order` | `addons/sale/models/sale_order.py` | `SalesDistribution/` | TBD `0x02??` | entity-as-class | needs mint |
110+
| `sale.order.line` | `addons/sale/models/sale_order_line.py` | `SalesDistribution/` | TBD | entity-as-class | needs mint |
111+
| `stock.picking` | `addons/stock/models/stock_picking.py` | `Transport/` | TBD | entity-as-class | needs mint |
112+
| `hr.employee` | `addons/hr/models/hr_employee.py` | `HR/` | TBD | entity-as-class | needs mint |
113+
| `crm.lead` | `addons/crm/models/crm_lead.py` | (new NTO?) | TBD | entity-as-class | needs mint + domain decision |
114+
| `product.product` | `addons/product/models/product_product.py` | (new NTO?) | TBD | entity-as-class | needs mint + domain decision |
115+
| `account.move::action_post` | workflow method on `account.move` | `Accounting/verbs/Post.ttl` | TBD | **verb-as-class** | needs mint |
116+
| `sale.order::action_confirm` | workflow method on `sale.order` | `SalesDistribution/verbs/Confirm.ttl` | TBD | **verb-as-class** | needs mint |
117+
| `stock.picking::action_assign` | workflow method on `stock.picking` | `Transport/verbs/Assign.ttl` | TBD | **verb-as-class** | needs mint |
118+
119+
The table grows additively. Each new Odoo addon module digested by
120+
the v0 producer adds rows; concept-mint passes work in parallel.
121+
122+
## §5. The drift detector — Foundry's "ontology change management" for free
123+
124+
```
125+
Day 1 — digest Odoo at SHA-A
126+
ogar-from-python addons/account → vocab/imports/ogit/NTO/Accounting/*.ttl
127+
git commit (the TTL set is the frozen contract)
128+
129+
Day N — Odoo upstream releases SHA-B
130+
ogar-from-python addons/account → /tmp/odoo-shaB-digest/
131+
diff -r vocab/imports/ogit/NTO/Accounting/ /tmp/odoo-shaB-digest/
132+
133+
Any output line is a structural change Odoo just made:
134+
- added field → diff shows a new ogit:optional-attributes entry
135+
- renamed column → diff shows the rename
136+
- extended selection → diff shows the new validation-parameter values
137+
- changed _inherit → diff shows the rdfs:subClassOf rewire
138+
- dropped a model → diff shows file deletion
139+
```
140+
141+
Same diff fires in CI on the same PR if a contributor edits an Odoo
142+
model without re-running the digest. **The TTL templates are the
143+
contract; the digest re-run is the audit; the diff is the gate.**
144+
Foundry sells this as "ontology change management" for a recurring
145+
license fee.
146+
147+
## §6. What blocks doing it today
148+
149+
| Piece | Status |
150+
|---|---|
151+
| Storage location (`vocab/imports/ogit/NTO/<Domain>/`) | exists; 72 domains imported, MARS oracle proven |
152+
| TTL emitter for the structural arm | exists (`ttl_emit::emit_entity`); semantic bijection proven on 29 MARS + 176 SGO TTLs |
153+
| Verb-as-class template surface | exists (WorkOrder convention; `docs/VERB-AS-CLASS-TEMPLATE.md`) |
154+
| Author-provenance discriminator | exists (`dcterms:creator` scan in `OGIT-DOMAIN-LIFT-CATALOGUE.md`) |
155+
| `ogar-render-askama::actions` (verb-as-class render path) | **does not exist**~200 LOC mirroring the existing `views/` path |
156+
| `ogar-from-python` (the digester) | **does not exist** — needs `libcst` or `rustpython-parser` to walk Python AST; ~1500 LOC for the structural-arm filter |
157+
| Concept mints for non-Accounting Odoo models | needs the 5+3 codebook pass per `APP-CLASS-CODEBOOK-LAYOUT.md` |
158+
159+
`ogar-from-python` and `ogar-render-askama::actions` are independent
160+
and can ship in parallel PRs. Concept mints are the slow path
161+
(codebook discipline) and don't block the digest — a digested model
162+
without a minted concept_id just gets `Class.name = "sale.order"` and
163+
gets the id assigned later.
164+
165+
## §7. The Foundry-parity collapse
166+
167+
This section is the punchline. Foundry's platform pitch decomposes
168+
into four layers; each layer maps to a free, open-source piece in
169+
this architecture:
170+
171+
| Foundry layer | Vendor cost | Our equivalent | Marginal cost |
172+
|---|---|---|---|
173+
| Ingest (vendor pipelines) | $ | `ogar-from-python` digest run (one-shot per Odoo upgrade) | engineer-hours per digester ~1500 LOC |
174+
| Storage (vendor platform) | $$ | `vocab/imports/ogit/NTO/<Domain>/` TTL templates with `dcterms:creator` provenance | zero |
175+
| Render (vendor UI) | $$ | `ogar-render-askama::{views, actions}` | engineer-hours per render path ~200 LOC each |
176+
| Access control / audit (vendor IAM) | $$$ | verb-as-class `requires-perm` slot + `emits-audit` + Lance-version-as-audit (ADR-013) | zero (the substrate already does it) |
177+
| Ontology change management (vendor feature) | $$$ | `diff -r` of digest output (§5) | zero |
178+
179+
The substrate eats every layer the platform sells, **using artifacts
180+
that already exist in this repo**, in less than 2000 lines of new
181+
code. The architecture has been latent the whole time — your
182+
"digest → relive agnostically" framing is what makes it visible as
183+
one shape.
184+
185+
## §8. Cross-references
186+
187+
- `docs/ODOO-TRANSCODING.md` — the existing producer spec (sections 1-18:
188+
module discovery, field type mapping, attribute kwargs, association
189+
kwargs, enum sources, class-level metadata, state machines, `_inherit`
190+
resolution, decorator mapping, CRUD overrides, registered prefixes,
191+
conformance corpus). The "how" of the digest.
192+
- `docs/VERB-AS-CLASS-TEMPLATE.md` — the askama-template framing the
193+
digest reuses for workflow actions.
194+
- `docs/HIRO-IN-CLASSES.md` — the bardioc-efficiency story (the same
195+
digest pattern applied to MARS; this doc generalises it to Odoo).
196+
- `docs/FOUNDRY-ODOO-MARS-LENS.md` — the three-postures cross-reading
197+
(MARS frozen schema, Odoo extensible source, Foundry vendor platform).
198+
- `docs/OGIT-DOMAIN-LIFT-CATALOGUE.md` — coverage register; new Odoo
199+
digests advance the rows from Imported → Lift-tested → Cross-walked.
200+
- `docs/APP-CLASS-CODEBOOK-LAYOUT.md` — the 5+3 mint protocol for
201+
new concept_ids (the §6 slow path).
202+
- `crates/ogar-from-schema/` — the schema-arm producer (TTL today;
203+
XSD queued) that the Odoo digester pairs with on the structural
204+
boundary.
205+
- `crates/ogar-render-askama/` — the askama renderer (views today;
206+
actions queued).

0 commit comments

Comments
 (0)