|
20 | 20 | ogar_vocab::Class (typed: attributes + family edges + canonical_concept) |
21 | 21 | │ |
22 | 22 | ─────────┼───────────────────────────────────────────────────── |
23 | | - │ BUILD-TIME (codegen) │ RUN-TIME (SoA projection) │ |
24 | | - ▼ ▼ │ |
25 | | - ogar-render-askama ogar-class-view (OgarClassView) │ |
26 | | - consumes &Class consumes ClassView trait │ |
27 | | - emits .rs / .ts / .surql produces RenderRow stream │ |
28 | | - via askama templates via lance-graph-contract::ClassView │ |
29 | | - │ │ │ |
30 | | - ▼ ▼ │ |
31 | | - source files late-bound display rows │ |
32 | | - (calibrated by (calibrated by │ |
33 | | - target compilers) consumer renderers) │ |
34 | | - └──────────────┬───────────────┘ │ |
35 | | - ▼ │ |
36 | | - same canonical contract │ |
37 | | - (codebook id, N3 field order) │ |
38 | | - ───────────────────────────────────────────────────────────────── |
| 23 | + │ │ |
| 24 | + ▼ ogar-render-askama (one crate, two flavours) │ |
| 25 | + │ │ │ |
| 26 | + │ ├── codegen-flavour templates │ |
| 27 | + │ │ consume &Class, emit .rs / .surql source files │ |
| 28 | + │ │ for downstream compilers / DB engines │ |
| 29 | + │ │ (T1 RustStruct ✅, T5 SurrealqlTable) │ |
| 30 | + │ │ │ |
| 31 | + │ └── render-flavour templates │ |
| 32 | + │ consume &Class (+ rows at run time), │ |
| 33 | + │ emit final HTML output read directly by the user │ |
| 34 | + │ via askama_axum or equivalent │ |
| 35 | + │ (T2 HtmlListView, T3 HtmlDetailView, T4 HtmlForm) │ |
| 36 | + │ │ |
| 37 | + │ No TypeScript codegen layer. Askama IS the output, per │ |
| 38 | + │ the WoA-rs pattern. See Anti-pattern #8. │ |
| 39 | + │ │ |
| 40 | + │ Sister concern (run-time SoA projection on rows-from-cache): │ |
| 41 | + │ ogar-class-view → ClassView trait → Vec<RenderRow> │ |
| 42 | + │ (when the SoA row has no `Class` in scope, only class_id + │ |
| 43 | + │ presence mask; the cache resolves labels late) │ |
| 44 | + │ │ |
| 45 | + └─── same canonical contract (codebook id, N3 field order) ───── |
39 | 46 | ``` |
40 | 47 |
|
41 | 48 | **Two pipelines, one canonical source, one codebook. They do not compete. |
@@ -174,17 +181,32 @@ respective `NamespaceBridge`s, both hit `entity_type_id() == 0x0102` |
174 | 181 |
|
175 | 182 | ### +5 askama templates — the artifact-kind kit (in `ogar-render-askama`) |
176 | 183 |
|
177 | | -| PR | ArtifactKind | Template | Target syntax | |
178 | | -|---|---|---|---| |
179 | | -| T1 ✅ | `RustStruct` | `dispatch/rust_struct.askama` | Rust struct + `pub const CLASS_ID` (PR #78 ships this as proof-of-shape) | |
180 | | -| T2 | `TsInterface` | `dispatch/ts_interface.askama` | TypeScript `interface` + matching `class_ids.ts` entry | |
181 | | -| T3 | `SurrealqlTable` | `dispatch/surrealql_table.askama` | `DEFINE TABLE` + per-field `DEFINE FIELD` | |
182 | | -| T4 | `OpenapiSchema` | `dispatch/openapi_schema.askama` | OpenAPI 3.1 `components.schemas.{X}` JSON | |
183 | | -| T5 | `NodeGuidRoutingArm` | `dispatch/node_guid_routing_arm.askama` | Rust `match` arm dispatching on `ClassId` | |
| 184 | +**Two flavours of "artifact"** the kit handles, per the WoA-rs pattern: |
| 185 | + |
| 186 | +- **Build-time codegen** — emit `.rs` / `.surql` source files. The compiler |
| 187 | + / DB engine downstream is the final consumer. Example: WoA's |
| 188 | + `crates/codegen` emitting axum handler stubs. |
| 189 | +- **Run-time HTML render** — render the canonical shape directly to the |
| 190 | + user's browser via `askama_axum` or equivalent. The askama template |
| 191 | + body **is** the output the human reads. Example: WoA's |
| 192 | + `templates/_dispatch/list_view.html` rendering a tenant list page. |
| 193 | + |
| 194 | +**There is no TypeScript codegen layer.** Askama is the output, not a |
| 195 | +producer of `.ts` source files. The OP / Redmine frontend in the |
| 196 | +canonical pattern is server-rendered HTML through askama, not a separate |
| 197 | +TS app the codegen feeds. See Anti-pattern #8. |
| 198 | + |
| 199 | +| PR | ArtifactKind | Template | Flavour | What it emits | |
| 200 | +|---|---|---|---|---| |
| 201 | +| T1 ✅ | `RustStruct` | `dispatch/rust_struct.askama` | codegen | Rust `struct` + `pub const CLASS_ID` (proof-of-shape, PR #78) | |
| 202 | +| T2 | `HtmlListView` | `dispatch/html_list_view.askama` | render | Tabular HTML over a `(class, rows)` pair — WoA list_view shape | |
| 203 | +| T3 | `HtmlDetailView` | `dispatch/html_detail_view.askama` | render | Definition-list HTML for one canonical instance, with family-edge sections | |
| 204 | +| T4 | `HtmlForm` | `dispatch/html_form.askama` | render | Create/edit form, input element per attribute keyed by typed slot (text/number/checkbox/datetime) | |
| 205 | +| T5 | `SurrealqlTable` | `dispatch/surrealql_table.askama` | codegen | `DEFINE TABLE` + per-field `DEFINE FIELD` | |
184 | 206 |
|
185 | 207 | Each PR: 1 template + 1 binding struct + 1 emitter impl + tests |
186 | | -(round-trip + target-toolchain compile + golden-snapshot of the first |
187 | | -emitted concept). |
| 208 | +(target-toolchain compile or HTML well-formedness check + golden-snapshot |
| 209 | +of the first emitted concept). |
188 | 210 |
|
189 | 211 | ### +5 consumer wirings (across repos) |
190 | 212 |
|
@@ -293,9 +315,32 @@ exactly this. Fixed; future surfaces should iterate |
293 | 315 | ones. `project_actor()` declares `type` (Rails STI); the unescaped form |
294 | 316 | emits `pub type:` (illegal). Codex P1 on PR #78 caught this. Fix is |
295 | 317 | `escape_rust_ident` mapping reserved words to `r#name`. Same hazard on |
296 | | -other targets (TS reserved words, SurrealQL keywords); every emitter |
| 318 | +other targets (SurrealQL keywords); every emitter that produces source |
297 | 319 | needs the equivalent. |
298 | 320 |
|
| 321 | +### Anti-pattern #8: Generate TypeScript source from askama |
| 322 | + |
| 323 | +**Form**: "emit `.ts` interface files / type declarations / class-id |
| 324 | +consts from askama so the frontend imports typed canonical shapes". |
| 325 | + |
| 326 | +**Why wrong**: askama **is** the rendering layer in the WoA-rs pattern. |
| 327 | +The consumer reads askama-rendered HTML directly (server-side via |
| 328 | +`askama_axum` or equivalent); there is no separate TypeScript frontend |
| 329 | +in the canonical architecture. Generating TS source means treating |
| 330 | +askama as a codegen-to-other-language pipeline, which contradicts its |
| 331 | +role as the output layer. |
| 332 | + |
| 333 | +Codegen flavour (T1 `RustStruct`, T5 `SurrealqlTable`) is fine — those |
| 334 | +target compilers / DB engines that consume Rust / SQL. **TS / OpenAPI |
| 335 | +schemas are not in that set**, because the consumer of those would be a |
| 336 | +JS-side frontend the pattern doesn't have. PR #80 (TsInterface) was |
| 337 | +closed for this reason; T2 was rewritten as `HtmlListView` (a render-side |
| 338 | +template). |
| 339 | + |
| 340 | +If a downstream actually wants typed TS (e.g. a third-party SDK |
| 341 | +consumer outside the WoA pattern), they can implement their own |
| 342 | +`ClassView`-based generator — but it sits *outside* the canonical kit. |
| 343 | + |
299 | 344 | ## 5. Epiphany log — high-signal architectural moments |
300 | 345 |
|
301 | 346 | Captured verbatim because future sessions will ask "why". |
@@ -361,7 +406,7 @@ These repos converged on the same northstar from different angles: |
361 | 406 |
|
362 | 407 | | Item | What | When | |
363 | 408 | |---|---|---| |
364 | | -| T6 | `A2uiPayload` artifact kind — emit any promoted concept as an A2UI v0.8 intent payload. A2UI's "catalog of trusted components" maps 1:1 to the OGAR codebook. Existing A2UI renderers (Flutter/Angular/Lit) consume the canonical layer with zero Ruby/Rails coupling. | After T1–T5 stabilises | |
| 409 | +| T6 | `A2uiPayload` artifact kind — emit any promoted concept as an A2UI v0.8 intent payload. **Strict: only if a consumer outside the WoA-pattern actually asks** (Flutter/Angular/Lit frontends). The default canonical UI is server-rendered HTML via T2–T4; A2UI is an alternative output for non-server-rendered consumers. Same canonical input; new bag-of-variables shaped to A2UI's component schema. | Demand-driven, after T1–T5 stabilises | |
365 | 410 | | T7 | Calibration-loop receipt aggregator — collect every PR's three gates into one CI artifact so coverage can be reasoned about as a ratio | After several Cn land | |
366 | 411 | | T8 | DOLCE upper-category live wiring — `OgarClassView::dolce_category_id` currently returns `0`; live values come from `lance-graph-ontology` when the consumer chooses to wire them | Independent; opportunistic | |
367 | 412 | | T9 | Promote more OpenProject-only concepts (Costs, Budgets, BIM, Meetings, Storages — the 880 OP-only classes from the corpus diff). Each gets its own codebook block (likely `0x03XX`+) | Demand-driven | |
|
0 commit comments