Skip to content

Commit 1192a7e

Browse files
authored
Merge pull request #107 from AdaWorldAPI/claude/medcare-bridge-lance-graph-wmx76z
vocab/exports/ skeleton + Odoo-digest framing corrections
2 parents e578fbe + 55e5475 commit 1192a7e

4 files changed

Lines changed: 255 additions & 35 deletions

File tree

.claude/board/EPIPHANIES.md

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

18+
## 2026-06-22 — Three corrections to the Odoo digest framing: producer name, storage location, latent re-vendor bug
19+
**Status:** FINDING
20+
**Scope:** producer architecture × vocab/ tree layout × re-vendor safety × digest-to-OGIT
21+
22+
Three corrections to the framing in `docs/ODOO-DIGEST-TO-OGIT.md`
23+
(originally landed in commit `7d68042`) surfaced from operator
24+
questions on which producer to use and where digests should live.
25+
26+
**Correction 1 — producer name.** The doc named the producer
27+
"`ogar-from-python`" (a crate that doesn't exist and that we'd be
28+
duplicating effort to build). The actual pipeline is the existing
29+
**`ruff_python_spo`** (Python AST frontend, sibling of
30+
`ruff_ruby_spo` / `ruff_elixir_spo` in the `ruff/` workspace)
31+
producing `ruff_spo_triplet::Model`, then the existing
32+
**`ogar-from-ruff`** crate mechanically projecting that IR into
33+
`ogar_vocab::Class`. `ogar-from-ruff` already exists and works for
34+
Ruby; what's missing is the `ruff_python_spo` frontend itself.
35+
36+
Same correction applies to medcare-rs digestion: the right pipeline
37+
is **`ruff_rust_spo` (queued) + `ogar-from-ruff`**, not a fictional
38+
`ogar-from-rust`. Symmetric with the other frontends; the projector
39+
is shared.
40+
41+
Lesson for the next architecture-doc draft: NAME THE ACTUAL CRATE
42+
that exists, don't invent producer names. The cross-repo `ruff`
43+
`ogar-from-ruff` projection pattern is the standard; any new source
44+
language goes through it.
45+
46+
**Correction 2 — `lance-graph-arm-discovery` is not a producer.**
47+
The "lancegraph arm crate" the operator asked about is
48+
`lance-graph-arm-discovery`, which is a streaming Association Rule
49+
Mining engine (Aerial+ paper transcode) that DISCOVERS new SPO rules
50+
from tabular data via NARS revision. It is **orthogonal** to schema
51+
digestion. The lance-graph-side OGAR bridge is
52+
`lance-graph-ogar` (re-export + activation, consumer-side wiring).
53+
Neither is a digester for source code or schemas.
54+
55+
**Correction 3 — digests belong in `vocab/exports/`, not
56+
`vocab/imports/`.** The original doc said digests land in
57+
`vocab/imports/ogit/NTO/<Domain>/`**wrong**. The `imports/`
58+
re-vendor recipe is a destructive `cp -r /upstream/. vocab/imports/`
59+
that would silently nuke any OGAR-produced content sitting there.
60+
The fix is a sibling tree `vocab/exports/ogit/` mirroring the
61+
upstream layout 1:1; digests land in `exports/`, mirror stays
62+
read-only in `imports/`.
63+
64+
The split exists for three reasons:
65+
- **Re-vendor safety**`cp -r` to `imports/` can't clobber what's
66+
in `exports/`. Structural fix, not a discipline fix.
67+
- **License/governance**`imports/` inherits MIT from arago/almato;
68+
`exports/` inherits OGAR's own license.
69+
- **Upstream-contribution path** — files in `exports/` are PR
70+
candidates back to OGIT upstream; files in `imports/` are
71+
immutable.
72+
73+
**Latent bug surfaced.** The current `vocab/imports/ogit/NTO/Accounting/`
74+
carries 11 OGAR-produced TTLs from a prior `Claude (AdaWorldAPI/lance-graph
75+
3-hop optim)` session sitting alongside Viktor Voss's 23 originals.
76+
Those 11 are at re-vendor-overwrite risk today. Migration to
77+
`vocab/exports/ogit/NTO/Accounting/` is queued — `vocab/exports/PROVENANCE.md
78+
§ Migration note` carries the file list.
79+
80+
This session lands the scaffold (empty `exports/` skeleton +
81+
provenance doc + doc corrections); the 11-file migration is a
82+
separate PR (operator decision: do we keep the original commit
83+
hashes for those files via `git mv`, or re-author them under the
84+
current author? — migration approach decides).
85+
86+
Evidence: `vocab/exports/PROVENANCE.md` (the split rationale),
87+
`vocab/exports/ogit/README.md` (the layout), `docs/ODOO-DIGEST-TO-OGIT.md`
88+
(updated with all three corrections + producer pipeline + storage
89+
path + blocker table).
90+
1891
## 2026-06-22 — extract_classes.py transcoded to Rust byte-faithfully; XSD↔TTL bijection closed; Python dependency removed from the oracle
1992
**Status:** FINDING
2093
**Scope:** XSD front-end × calibration self-containment × the queued bijection

docs/ODOO-DIGEST-TO-OGIT.md

Lines changed: 75 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,38 @@
33
> **For colleagues building anything that needs Odoo as a typed ontology
44
> outside the Odoo Python runtime.** This is the architectural shape:
55
> digest Odoo source once into OGIT-shaped TTL templates (stored
6-
> in-tree at `vocab/imports/ogit/NTO/<Domain>/`), then relive any model
6+
> in-tree at `vocab/exports/ogit/NTO/<Domain>/`), then relive any model
77
> or workflow action agnostically via `ogar-render-askama`.
88
>
99
> Status: **FRAMING v0** (2026-06-22). Companion to
1010
> `docs/ODOO-TRANSCODING.md` (the producer spec) and
1111
> `docs/VERB-AS-CLASS-TEMPLATE.md` (the askama-template framing this
1212
> reuses).
1313
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.**
14+
The shape in one sentence: **the `ruff_python_spo + ogar-from-ruff`
15+
pipeline digests Odoo source once into the OGAR IR; `ttl_emit` writes
16+
the IR back as OGIT-shaped TTL templates stored at
17+
`vocab/exports/ogit/NTO/<Domain>/`; consumers re-instantiate any model
18+
or workflow action by rendering against the TTL via
19+
`ogar-render-askama`, never touching Odoo Python.**
20+
21+
> **Producer naming correction (2026-06-22):** earlier drafts of this
22+
> doc called the producer "`ogar-from-python`" — that name is wrong.
23+
> The correct pipeline is the existing **`ruff_python_spo`** AST
24+
> frontend (sibling of `ruff_ruby_spo` / `ruff_elixir_spo` in the
25+
> `ruff/` workspace) producing `ruff_spo_triplet::Model`, then the
26+
> existing **`ogar-from-ruff`** crate mechanically projecting that IR
27+
> into `ogar_vocab::Class`. A `ruff_python_spo` frontend is queued
28+
> (the projector `ogar-from-ruff` already exists and works for Ruby).
29+
> Building a new `ogar-from-python` from scratch would duplicate the
30+
> projection that's already shipping.
31+
>
32+
> **Storage location correction (2026-06-22):** earlier drafts said
33+
> digests land in `vocab/imports/ogit/NTO/<Domain>/`. That was wrong
34+
> — it would put OGAR-produced content at re-vendor risk (the
35+
> `imports/` re-vendor recipe is a destructive `cp -r`). Correct
36+
> location: **`vocab/exports/ogit/NTO/<Domain>/`** — see
37+
> `vocab/exports/PROVENANCE.md` for the split rationale.
1938
2039
---
2140

@@ -25,21 +44,26 @@ TTL via `ogar-render-askama`, never touching Odoo Python.**
2544
Odoo Python source
2645
(addons/<module>/models/*.py)
2746
28-
│ ogar-from-python (AST schema filter)
47+
│ ruff_python_spo (sibling of ruff_ruby_spo / ruff_elixir_spo;
48+
│ queued — Python AST → ruff_spo_triplet::Model)
2949
│ — keeps structural arm: _name, _inherit, fields.*, selections
3050
│ — keeps behavioural-arm SIGNATURES (decorators, action def names)
3151
│ — drops bodies (computed methods, action implementations)
3252
53+
ruff_spo_triplet::Model
54+
55+
│ ogar-from-ruff (existing — mechanical projection)
56+
3357
OGAR Class IR (in memory)
3458
3559
│ ttl_emit::emit_entity (semantic bijection)
3660
│ — entity-as-class for models
3761
│ — verb-as-class for workflow action signatures
3862
3963
OGIT-shaped TTL templates
40-
(vocab/imports/ogit/NTO/<Domain>/<DigestedClass>.ttl)
64+
(vocab/EXPORTS/ogit/NTO/<Domain>/<DigestedClass>.ttl)
4165
— dcterms:creator = bus-compiler (digester provenance)
42-
alongside upstream arago TTL (Viktor Voss et al)
66+
distinct tree from imports/ ogit/ (re-vendor safety)
4367
4468
│ ogar-render-askama (entity render → views; verb render → actions)
4569
@@ -54,26 +78,36 @@ The Python runtime is **only** touched at digest time. Consumers
5478
(`woa-rs`, `smb-office-rs`, `medcare-rs`, `q2`, any future renderer)
5579
never depend on Odoo Python, only on TTL + the askama renderer.
5680

57-
## §2. Why store digests in OGIT NTO (not a parallel `vocab/imports/odoo/`)
81+
## §2. Why digests live in `vocab/exports/ogit/`, not `vocab/imports/ogit/`
5882

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:
83+
`imports/` is a READ-ONLY mirror of upstream OGIT (the re-vendor
84+
recipe is a destructive `cp -r /upstream/. vocab/imports/ogit/`).
85+
Putting OGAR-produced content in `imports/` would silently nuke
86+
those files on the next re-vendor. **The split exists for re-vendor
87+
safety, license/governance, and upstream-contribution path** — see
88+
`vocab/exports/PROVENANCE.md` for the full rationale.
6289

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.
90+
The digest **mirrors the upstream layout** so consumers see one shape:
7391

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.
92+
| Concept | Upstream OGIT path (READ-only mirror) | Digest target (OGAR-produced) |
93+
|---|---|---|
94+
| `Accounting` | `vocab/imports/ogit/NTO/Accounting/` (23 files, Viktor Voss) | `vocab/exports/ogit/NTO/Accounting/` (Odoo-digested) |
95+
| `SalesDistribution` | `vocab/imports/ogit/NTO/SalesDistribution/` (23 files, Marek Meyer) | `vocab/exports/ogit/NTO/SalesDistribution/` (Odoo sale.* digest) |
96+
| `Transport` | `vocab/imports/ogit/NTO/Transport/` (27 files, chris.boos@almato.com) | `vocab/exports/ogit/NTO/Transport/` (Odoo stock.* digest) |
97+
|| upstream-mirrored | OGAR-produced, OGIT-shape-compatible |
98+
99+
`dcterms:creator` provenance is now a SECONDARY check (the directory
100+
split is the primary). The `OGIT-DOMAIN-LIFT-CATALOGUE.md §
101+
Verifying domain authorship` scan still runs and still discriminates
102+
authors, but the destructive-overwrite risk is structurally gone.
103+
104+
**Migration note for the existing 11 stranded files.** A prior
105+
session's `Claude (AdaWorldAPI/lance-graph 3-hop optim)` digest left
106+
11 OGAR-produced files in `vocab/imports/ogit/NTO/Accounting/`
107+
alongside Viktor Voss's 23 originals. Those 11 belong in
108+
`vocab/exports/ogit/NTO/Accounting/`. The migration is a separate
109+
PR; `vocab/exports/PROVENANCE.md § Migration note` carries the
110+
list.
77111

78112
## §3. The four shapes the digester produces
79113

@@ -123,12 +157,14 @@ the v0 producer adds rows; concept-mint passes work in parallel.
123157

124158
```
125159
Day 1 — digest Odoo at SHA-A
126-
ogar-from-python addons/account → vocab/imports/ogit/NTO/Accounting/*.ttl
160+
ruff_python_spo addons/account → ruff_spo_triplet::Model
161+
ogar-from-ruff → Class IR
162+
ttl_emit::emit_entity → vocab/exports/ogit/NTO/Accounting/*.ttl
127163
git commit (the TTL set is the frozen contract)
128164
129165
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/
166+
same pipeline → /tmp/odoo-shaB-digest/
167+
diff -r vocab/exports/ogit/NTO/Accounting/ /tmp/odoo-shaB-digest/
132168
133169
Any output line is a structural change Odoo just made:
134170
- added field → diff shows a new ogit:optional-attributes entry
@@ -148,15 +184,19 @@ license fee.
148184

149185
| Piece | Status |
150186
|---|---|
151-
| Storage location (`vocab/imports/ogit/NTO/<Domain>/`) | exists; 72 domains imported, MARS oracle proven |
187+
| Read-only upstream mirror (`vocab/imports/ogit/`) | exists; 72 NTO + SGO + ogit.ttl + SDF imported, MARS oracle proven |
188+
| OGAR-produced export tree (`vocab/exports/ogit/`) | **skeleton exists** (this commit); content populates as digests run |
152189
| TTL emitter for the structural arm | exists (`ttl_emit::emit_entity`); semantic bijection proven on 29 MARS + 176 SGO TTLs |
153190
| 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`) |
191+
| Author-provenance discriminator | exists (`dcterms:creator` scan in `OGIT-DOMAIN-LIFT-CATALOGUE.md`); now a secondary check behind the directory split |
192+
| `ogar-from-ruff` (mechanical projector from `ruff_spo_triplet::Model``Class`) | exists for Ruby AR; same projector handles Python and Elixir once their `ruff_*_spo` frontends ship |
193+
| `ruff_python_spo` (Python AST frontend, sibling of `ruff_ruby_spo`) | **does not exist** — needs `libcst` or `rustpython-parser`; ~1500 LOC for the structural-arm filter |
194+
| `ruff_rust_spo` (Rust AST frontend, for digesting medcare-rs / woa-rs / etc.) | **does not exist** — needs `syn` walker; symmetric with the other ruff frontends |
155195
| `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 |
157196
| Concept mints for non-Accounting Odoo models | needs the 5+3 codebook pass per `APP-CLASS-CODEBOOK-LAYOUT.md` |
197+
| Migration of the 11 stranded Accounting files (`imports/``exports/`) | **queued** — separate PR (see `vocab/exports/PROVENANCE.md § Migration note`) |
158198

159-
`ogar-from-python` and `ogar-render-askama::actions` are independent
199+
`ruff_python_spo` and `ogar-render-askama::actions` are independent
160200
and can ship in parallel PRs. Concept mints are the slow path
161201
(codebook discipline) and don't block the digest — a digested model
162202
without a minted concept_id just gets `Class.name = "sale.order"` and
@@ -170,8 +210,8 @@ this architecture:
170210

171211
| Foundry layer | Vendor cost | Our equivalent | Marginal cost |
172212
|---|---|---|---|
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 |
213+
| Ingest (vendor pipelines) | $ | `ruff_python_spo + ogar-from-ruff` digest (one-shot per Odoo upgrade) | engineer-hours per frontend ~1500 LOC (projector exists) |
214+
| Storage (vendor platform) | $$ | `vocab/exports/ogit/NTO/<Domain>/` TTL templates (mirrors upstream OGIT layout) | zero (skeleton shipped) |
175215
| Render (vendor UI) | $$ | `ogar-render-askama::{views, actions}` | engineer-hours per render path ~200 LOC each |
176216
| 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) |
177217
| Ontology change management (vendor feature) | $$$ | `diff -r` of digest output (§5) | zero |

vocab/exports/PROVENANCE.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# PROVENANCE — `vocab/exports/`
2+
3+
> **OGAR-produced TTL templates, in OGIT-compatible shape.** Distinct
4+
> from `vocab/imports/` (read-only mirror of upstream OGIT, MIT
5+
> licensed by arago/almato). Files in this tree are AUTHORED in OGAR
6+
> — either digested from source by a producer (`ruff_*_spo +
7+
> ogar-from-ruff`, future `ruff_rust_spo`, etc.) or hand-authored —
8+
> and the OGAR license applies.
9+
>
10+
> Status: **SKELETON v0** (2026-06-22). Scaffold lands empty; content
11+
> populates as producers digest into it.
12+
13+
## Why the split
14+
15+
`vocab/imports/` and `vocab/exports/` exist for three reasons:
16+
17+
1. **Re-vendor safety.** The `imports/` re-vendor recipe is a
18+
destructive `cp -r /upstream/. vocab/imports/...`. Putting
19+
OGAR-produced content in `imports/` would be silently nuked on
20+
the next re-vendor. The split makes the producer-output tree
21+
immune.
22+
23+
2. **License + governance.** `imports/` inherits MIT (Almato AI GmbH,
24+
2013–2024) from OGIT upstream. `exports/` inherits OGAR's own
25+
license. Authorship discriminator (`dcterms:creator`) becomes a
26+
secondary check rather than the primary one.
27+
28+
3. **Upstream-contribution path.** Files in `exports/` are candidates
29+
for PR back to OGIT upstream (or onward distribution to consumers
30+
expecting OGIT shape). Files in `imports/` are immutable mirrors.
31+
The directory split makes the contribution flow a one-line check.
32+
33+
## Layout
34+
35+
```
36+
vocab/exports/
37+
└── ogit/ ← OGIT-shape (consumer-compat)
38+
├── NTO/
39+
│ ├── <Domain>/ ← mirrors upstream OGIT NTO layout
40+
│ │ ├── entities/ ← entity TTLs (a rdfs:Class)
41+
│ │ ├── attributes/ ← datatype-property TTLs (a owl:DatatypeProperty)
42+
│ │ ├── verbs/ ← verb TTLs (a owl:ObjectProperty
43+
│ │ │ OR a rdfs:Class for askama-template verbs)
44+
│ │ └── PROVENANCE.md ← per-domain source provenance (which
45+
│ │ producer ran, which upstream input,
46+
│ │ which Odoo/Medcare/etc. revision)
47+
│ ├── ...
48+
└── PROVENANCE.md (this file)
49+
```
50+
51+
The layout intentionally mirrors `imports/ogit/` 1:1 so consumers
52+
discovering both trees see one shape; the choice between
53+
"upstream-mirrored" and "OGAR-produced" is the path prefix, nothing
54+
else.
55+
56+
## What lives here today
57+
58+
Empty. Producers haven't run yet; content populates as digests land:
59+
60+
| Source | Producer (planned) | Lands at |
61+
|---|---|---|
62+
| Odoo `addons/account/*` | `ruff_python_spo + ogar-from-ruff` | `exports/ogit/NTO/Accounting/` |
63+
| Odoo `addons/sale/*` | same | `exports/ogit/NTO/SalesDistribution/` |
64+
| Odoo `addons/stock/*` | same | `exports/ogit/NTO/Transport/` |
65+
| Odoo workflow `def action_*` | same (verb-as-class shape) | `exports/ogit/NTO/<Domain>/verbs/` |
66+
| Medcare-rs domain types | `ruff_rust_spo + ogar-from-ruff` (queued) | `exports/ogit/NTO/Healthcare/` |
67+
| Medcare-rs MongoDB schemas | `ogar-from-schema` (XSD/JSON-Schema) | `exports/ogit/NTO/Healthcare/` |
68+
| Hand-authored OGAR Class views | direct authoring | `exports/ogit/NTO/<Domain>/` |
69+
70+
## Migration note — the 11 stranded Accounting files
71+
72+
The current `vocab/imports/ogit/NTO/Accounting/` carries 11 TTLs
73+
authored by a prior session (`dcterms:creator = "Claude
74+
(AdaWorldAPI/lance-graph 3-hop optim)"`) sitting alongside 23 upstream
75+
files by Viktor Voss. **Those 11 belong in `exports/ogit/NTO/Accounting/`**
76+
— at re-vendor risk where they sit today. Migration is a separate
77+
decision and a separate PR; the scaffold here doesn't move them yet.
78+
The list:
79+
80+
```
81+
vocab/imports/ogit/NTO/Accounting/verbs/hasProductCategory.ttl
82+
vocab/imports/ogit/NTO/Accounting/verbs/hasPickingType.ttl
83+
vocab/imports/ogit/NTO/Accounting/verbs/hasFiscalCountry.ttl
84+
vocab/imports/ogit/NTO/Accounting/attributes/productCategoryComplete.ttl
85+
vocab/imports/ogit/NTO/Accounting/attributes/iso3166Alpha2.ttl
86+
+ 6 more (run the dcterms:creator scan in
87+
docs/OGIT-DOMAIN-LIFT-CATALOGUE.md § Verifying domain authorship
88+
to list all 11)
89+
```
90+
91+
## License + contribution
92+
93+
OGAR repository license (see top-level `LICENSE`). Files here are
94+
authored by OGAR — re-publishing back to OGIT upstream requires
95+
explicit relicensing or arago/almato acceptance.

0 commit comments

Comments
 (0)