Skip to content

Commit b598460

Browse files
committed
docs(verb-as-class): the rdfs:Class-as-verb convention is an askama-shaped action template, not a quirk
WorkOrder's 12 verbs/*.ttl are declared as `a rdfs:Class`, not `owl:ObjectProperty`. The prior commit framed this as "an unusual convention we're free to revise" — wrong framing. The encoding is load-bearing. A `rdfs:Class` verb carries a typed slot list (`ogit:mandatory-attributes`), an inheritance chain (`rdfs:subClassOf`), and policy metadata (`ogit:requires-perm`, `ogit:emits-audit`). That makes each verb a compile-time-validated action template — the ontological counterpart to askama (Rust) and jinja (Python) HTML templating. Structural correspondence is exact: * TTL file = template (.html.j2 equivalent) * mandatory-attributes = struct field list (askama context shape) * per-call binding = struct instance * render = SPO triple emit + declared side effects (audit, ACL gate) * rdfs:subClassOf = template inheritance ({% extends %}) * lift-time slot validation = askama's compile-time {{ field }} check The existing ogar-render-askama crate is the natural landing — it already renders Class views (noun-shaped); the verb-as-class encoding is the parallel render path for Class actions (verb-shaped). Same engine, different output medium. Foundry-parity sharpens: Foundry's "action types" carry typed parameters + slot validation + declared side effects + inheritance — sold as a paid platform feature. Verb-as-class TTL + ogar-render-askama gives the same four from open-source schemas and Rust templates. Adds: * docs/VERB-AS-CLASS-TEMPLATE.md (FRAMING v0) — analogy table, worked example on WorkOrder/verbs/AccessesPortal.ttl, render flow, cross-references to the existing render-askama crate. * docs/OGIT-DOMAIN-LIFT-CATALOGUE.md (WorkOrder row): walks back the "we can re-author to owl:ObjectProperty" framing — the convention is deliberate and load-bearing for the action-render path. * .claude/board/EPIPHANIES.md: FINDING entry with the correction citing the prior commit, the analogy table, and the implications for the ogar-render-askama actions submodule. No code changes. Lift-tested round-trip count unchanged (16/16); verb-as-class TTLs already parse cleanly via ttl::parse_file as entities and round-trip via ttl_emit::emit_entity.
1 parent f44f712 commit b598460

3 files changed

Lines changed: 210 additions & 1 deletion

File tree

.claude/board/EPIPHANIES.md

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

18+
## 2026-06-22 — Verb-as-class is an ontological askama template — compile-time-validated action declaration, not a quirk
19+
**Status:** FINDING
20+
**Scope:** WorkOrder convention × `ogar-render-askama` integration × Foundry action-type parity
21+
22+
WorkOrder's 12 `verbs/*.ttl` are declared as `rdfs:Class`, not
23+
`owl:ObjectProperty`. The earlier framing (commit `cce8420`) called
24+
this "an unusual convention we're free to revise toward standard
25+
`owl:ObjectProperty`" — **that framing was wrong** and is hereby
26+
corrected.
27+
28+
The verb-as-class encoding is **load-bearing**: it makes each verb a
29+
typed template carrying its own slot list (`ogit:mandatory-attributes`),
30+
inheritance chain (`rdfs:subClassOf`), and policy metadata
31+
(`ogit:requires-perm`, `ogit:emits-audit`). That's not a flat predicate;
32+
that's a **compile-time-validated action declaration** — the ontological
33+
counterpart to askama (Rust) and jinja (Python) HTML templating.
34+
35+
The structural correspondence is exact:
36+
37+
- TTL file = template (`.html.j2` equivalent)
38+
- `ogit:mandatory-attributes` = struct field list (askama context shape)
39+
- Per-call binding = struct instance (askama render input)
40+
- Render = SPO triple emit + declared side effects (audit, ACL gate)
41+
- `rdfs:subClassOf` = template inheritance (`{% extends %}`)
42+
- Lift-time slot validation = askama's compile-time `{{ field }}` check
43+
44+
This is the integration point `ogar-render-askama` was always going
45+
to need for actions. The crate currently renders `Class` *views*
46+
(noun-shaped: HTML/JSON/OpenAPI); a parallel `actions/` submodule
47+
renders `Class` *actions* (verb-shaped: SPO triple + side-effect spec).
48+
Same engine, same compile-time-validated context model, different
49+
output medium.
50+
51+
**Foundry-parity sharpening:** Foundry's "action types" carry exactly
52+
the four properties this encoding gives — typed parameters, slot
53+
validation, declared side effects, inheritance. Foundry sells it as a
54+
paid platform feature; verb-as-class TTL + `ogar-render-askama` gives
55+
the same four from open-source schemas and Rust templates.
56+
57+
Implications:
58+
- **WorkOrder's convention stays.** Don't normalise to `owl:ObjectProperty`.
59+
- **WorkOrder is the natural prototyping ground** (we're upstream per
60+
`dcterms:creator` = `bus-compiler` + `family-codec-smith`) for new
61+
verb-as-class predicates before pitching the pattern to OGIT upstream.
62+
- **`ogar-render-askama::actions` is the next natural module**
63+
~200 LOC mirroring the existing `views/` render path.
64+
65+
Doc: `docs/VERB-AS-CLASS-TEMPLATE.md` (FRAMING v0) carries the full
66+
analogy table + worked example + render flow.
67+
1868
## 2026-06-22 — Author provenance via `dcterms:creator` discriminates "ours to revise" from "upstream-coordinated"
1969
**Status:** FINDING
2070
**Scope:** OGIT NTO governance × multi-domain lift × who-can-change-what

docs/OGIT-DOMAIN-LIFT-CATALOGUE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ arago/almato coordination."
149149
| `Transport` | 5 | 14 | 8 | Lift-tested | `chris.boos@almato.com` (sole author, 27 files) — pure upstream-arago |
150150
| `UserMeta` | 4 | 0 | 4 | Imported | |
151151
| `Version` | 0 | 3 | 0 | Imported | Used by MARS Machine for OS version |
152-
| **`WorkOrder`** | 27 | 0 | 0 | **Lift-tested** | **Our extension** (`dcterms:creator` = `bus-compiler` + `family-codec-smith` — internal agent authors, zero external). Authored for `woa-rs`. All 27 TTLs declared as `rdfs:Class`, including the 12 in `verbs/` (unusual `rdfs:Class`-as-verb convention). Round-trips cleanly. **Since we're upstream**, the verb files can be re-authored as `owl:ObjectProperty` for the AST predicate registry without external coordination. Previous catalogue row split 15 entities + 12 verbs by directory; content-driven count is 27 entities (what `ogar-from-schema` actually sees). |
152+
| **`WorkOrder`** | 27 | 0 | 0 | **Lift-tested** | **Our extension** (`dcterms:creator` = `bus-compiler` + `family-codec-smith` — internal agent authors, zero external). Authored for `woa-rs`. All 27 TTLs declared as `rdfs:Class`, including the 12 in `verbs/`. **The `rdfs:Class`-as-verb convention is deliberate, not a quirk** — it makes each verb a typed template (slots, inheritance, policy metadata) that `ogar-render-askama` can compile-time-validate against a binding, the same way askama validates HTML templates against a Rust struct. See `docs/VERB-AS-CLASS-TEMPLATE.md`. Previous catalogue row split 15 entities + 12 verbs by directory; the content-driven count is 27 first-class typed declarations (entities + verb-as-class templates), which is what `ogar-from-schema` sees and what the action-render path consumes. |
153153
| **TOTALS** | **549** | **599** | **241** || + 42 other (Medical sql_mirror, etc.) |
154154

155155
## Adjacent imports (not NTO)

docs/VERB-AS-CLASS-TEMPLATE.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Verb-as-class — the ontological askama/jinja
2+
3+
> **Insight (operator, 2026-06-22).** When a verb is encoded as
4+
> `rdfs:Class` instead of `owl:ObjectProperty`, the TTL file becomes
5+
> a **compile-time-validated action template** — the ontological
6+
> counterpart to askama (Rust) and jinja (Python) HTML templating.
7+
> WorkOrder uses this convention (12 verbs declared as classes); the
8+
> existing `ogar-render-askama` crate is the natural integration
9+
> point.
10+
>
11+
> Status: **FRAMING v0** (2026-06-22). Companion to
12+
> `docs/OGIT-DOMAIN-LIFT-CATALOGUE.md` (WorkOrder row) and
13+
> `docs/FOUNDRY-ODOO-MARS-LENS.md` (the Foundry-parity angle).
14+
15+
The convention in one sentence: **a `rdfs:Class` verb declares a typed
16+
slot list; a render takes a context binding and produces a materialised
17+
SPO triple + declared side effects; the engine validates slot↔binding
18+
at the same point askama validates `{{ name }}` against the struct
19+
field.**
20+
21+
## The two encodings compared
22+
23+
| | `owl:ObjectProperty` verb | `rdfs:Class` verb |
24+
|---|---|---|
25+
| Carries | name + description | name + description + **slot list** + **inheritance** + **policy attributes** |
26+
| Subject/object types | implied (by domain/range, often missing in OGIT) | explicit (`ogit:mandatory-attributes` enumerates the slots) |
27+
| Inheritance | not native | `rdfs:subClassOf` gives template inheritance |
28+
| Policy metadata | not native | any attribute the consumer wants (`ogit:requires-perm`, `ogit:emits-audit`) |
29+
| Compile-time slot validation | no — verbs are flat names | yes — `ogar-from-schema` lifts the slot list; the renderer checks bindings |
30+
| Round-trip with `ogar-from-schema` | yes (via `sgo::parse_verb`) | yes (via `ttl::parse_file` as Entity) |
31+
| **Renders as** | a label on an edge | a typed action with side effects |
32+
33+
Both encodings round-trip cleanly today. The choice is about **what the
34+
verb is *for*** — a flat predicate (use `owl:ObjectProperty`, like
35+
SGO's 176) or a typed action template (use `rdfs:Class`, like WorkOrder's 12).
36+
37+
## Term-for-term with askama / jinja
38+
39+
```
40+
askama / jinja │ ontology (verb-as-class)
41+
───────────────────────────── │ ─────────────────────────────────────────
42+
template.html.j2 │ vocab/imports/ogit/NTO/<Domain>/verbs/<Verb>.ttl
43+
│ a rdfs:Class
44+
struct Context { name: String │ ogit:mandatory-attributes (
45+
│ ogit:subject
46+
│ ogit:object
47+
│ …
48+
│ )
49+
context binding │ per-call value map
50+
{% extends "base.html.j2" %} │ rdfs:subClassOf ogit:AuditableAction
51+
filters / macros │ ogit:requires-perm, ogit:emits-audit
52+
compile-time slot check │ ogar-from-schema validates the slot list
53+
│ against the binding at lift time
54+
render() → HTML string │ render() → (SPO triple, audit record,
55+
│ ACL decision, side effects)
56+
```
57+
58+
The structural correspondence is exact. Both are
59+
**compile-time-validated declarative templates**; both separate
60+
"template" (TTL file / `.html.j2`) from "context" (binding / struct);
61+
both surface invalid bindings before render. The output medium
62+
differs (HTML string vs. graph delta) but the engine shape is the same.
63+
64+
## A worked example — `WorkOrder/verbs/AccessesPortal.ttl`
65+
66+
```turtle
67+
ogit.WorkOrder:AccessesPortal
68+
a rdfs:Class; # VERB-AS-CLASS
69+
rdfs:subClassOf ogit:AuditableAction; # inherits audit slot
70+
rdfs:label "AccessesPortal";
71+
ogit:mandatory-attributes ( # SLOTS — like askama struct fields
72+
ogit:subject # who
73+
ogit:object # what portal
74+
ogit:timestamp # when
75+
);
76+
ogit:requires-perm "portal_login"; # template metadata
77+
ogit:emits-audit "true"; # render-time side effect
78+
.
79+
```
80+
81+
When `User#42 accesses Portal#1` at time `T`:
82+
83+
1. **Lookup** — the renderer resolves `AccessesPortal` to its lifted
84+
`Class` (the template).
85+
2. **Bind** — the binding `{ subject: User#42, object: Portal#1,
86+
timestamp: T }` is type-checked against the slot list. Missing
87+
`timestamp` ⇒ render-time error (same as askama: missing struct field
88+
⇒ compile error).
89+
3. **Render** — emit:
90+
- SPO triple `(User#42, AccessesPortal#<id>, Portal#1)` with
91+
`timestamp = T`
92+
- Audit record (because `emits-audit = true`)
93+
- ACL gate (must satisfy `requires-perm = "portal_login"`)
94+
95+
The output is a **graph delta + side-effect spec**, not a string.
96+
97+
## Why this is the right integration point for `ogar-render-askama`
98+
99+
The crate already does askama-template rendering for `Class` **views**
100+
(per-app skin per `docs/OGAR-CONSUMER-BEST-PRACTICES.md`). Verb-as-class
101+
is the parallel render path for `Class` **actions**:
102+
103+
```
104+
ogar-render-askama/
105+
├── views/ — Class views (HTML, JSON, OpenAPI) ← EXISTING
106+
└── actions/ — Class actions (verb-as-class render) ← NEW (this framing)
107+
```
108+
109+
Same `Class` IR, same askama engine, same compile-time-validated
110+
template/binding pattern. Dispatch is on what shape the `Class`
111+
declares: noun-shaped (entity fields) → view render; verb-shaped
112+
(`mandatory-attributes` = subject/object/timestamp/…) → action render.
113+
114+
## The Foundry-parity sharpening
115+
116+
Foundry's "action types" carry exactly the four properties listed in
117+
the comparison table:
118+
119+
1. Typed parameters (slots)
120+
2. Compile-time validation of bindings
121+
3. Declared side effects (audit, ACL, downstream effects)
122+
4. Inheritance / composition
123+
124+
Foundry sells those as a paid platform feature. Verb-as-class TTL +
125+
`ogar-render-askama` gives the same four properties from
126+
**open-source schemas and Rust templates** — no vendor, no lock.
127+
128+
## What this changes for the next session
129+
130+
Three small implications:
131+
132+
1. **Don't normalise WorkOrder's convention** to `owl:ObjectProperty`.
133+
The earlier framing (in the commit message of `cce8420`) was wrong;
134+
re-encoding would strip the template surface.
135+
136+
2. **The `actions/` submodule in `ogar-render-askama` is the natural
137+
landing** for the verb-as-class renderer. Not implemented today;
138+
~200 LOC mirroring the existing `views/` render path.
139+
140+
3. **The verb-as-class convention is a candidate for prototyping
141+
in WorkOrder first** (since we're upstream — `dcterms:creator` =
142+
`bus-compiler` / `family-codec-smith`), then pitching to OGIT
143+
upstream once `ogar-render-askama`'s actions path has proven the
144+
pattern.
145+
146+
## Cross-references
147+
148+
- `crates/ogar-render-askama/` — the existing askama renderer (views
149+
today; actions tomorrow)
150+
- `crates/ogar-from-schema/` — the lift that turns TTL into the IR
151+
the renderer consumes
152+
- `docs/OGIT-DOMAIN-LIFT-CATALOGUE.md` — WorkOrder row with the
153+
verb-as-class convention note
154+
- `docs/FOUNDRY-ODOO-MARS-LENS.md` — the Foundry-parity argument
155+
that verb-as-class sharpens
156+
- `docs/OGAR-CONSUMER-BEST-PRACTICES.md` — the per-app `ClassView`
157+
pattern that views-side rendering already follows
158+
- `vocab/imports/ogit/NTO/WorkOrder/verbs/*.ttl` — the 12 verb-as-class
159+
templates this framing describes

0 commit comments

Comments
 (0)