ogar-render-askama: render_python — Python import-shape emit PoC (SDK + package)#154
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 89f6b18. Configure here.
| for (is_a, sources) in isas { | ||
| let fname = pyify(is_a); | ||
| if !seen.insert(fname.clone()) { | ||
| continue; |
There was a problem hiding this comment.
Colliding pyify drops actions
Medium Severity
When two distinct is_a rail keys in the same part_of module normalize to the same Python name via pyify, the emitter skips later entries with continue and never merges their controller sources into the surviving stub. Rails predicates ending in ? are especially at risk because snake adds a trailing underscore that trim_matches('_') then removes, so foo? and foo can share one function name and one tile vanishes from the package.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 89f6b18. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89f6b1855f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Some(concept) = &c.canonical_concept { | ||
| if let Some(id) = canonical_concept_id(concept) { | ||
| models.push_str(&format!( | ||
| " CLASS_ID: int = 0x{id:04X} # canonical concept `{concept}`\n" |
There was a problem hiding this comment.
Mark generated CLASS_ID as a ClassVar
For grounded classes this emits CLASS_ID as a dataclass instance field, so it becomes part of __init__, repr, equality, and positional construction; for example the generated Node(5) sets CLASS_ID=5 and leaves changeset unset. Since this value is intended to be stable per-class identity, generate it as ClassVar[int] (and import ClassVar) so model instances cannot silently override or serialize it as mutable record data.
Useful? React with 👍 / 👎.
…singulariser) Delete the singulariser entirely. It was inventing a namespace out of string-munged controller names — arbitrary, lossy (English plurals aren't invertible by heuristic: `Searches → searche`, `Capabilities → capabilitie`), and irreversible. The controller's own name IS the faithful identity; the semantics live in the classid, not in a prettified string. The DO arm is now a faithful `controllers` module that mirrors `app/controllers/` 1:1: - `container_of` snake-cases the controller stem **verbatim**, no singularisation: `NodesController → nodes`, `MapsController → maps`, `SearchesController → searches`, `ChangesetCommentsController → changeset_comments`. Reversible; matches Rails' own resource routes. - `controller_to_model` likewise stops singularising (returns `Nodes`). - render_osm emits `generated/controllers.rs` (was `actions.rs`); osm-domain does `#[path=…] pub mod controllers;` + `pub use controllers::*;`, so both `osm_domain::controllers::nodes::show(input)` and the re-exported `osm_domain::nodes::show(input)` resolve. The (part_of:is_a) rail + the `is_a` archetype axis (index→list, …) are unchanged — only the invented singular container naming is gone. Regenerated the parked snapshot; osm-domain builds; `container_of` verbatim test replaces the deleted singulariser test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
… + package) The Python counterpart of render_osm: same ruff → OGAR substrate, a language-pluggable emit back-end. Proves the pull-back side of the transpiler is not Rust-specific — OSM (and odoo) come out of the one substrate in Python import shape, just like Rust. Emits under python/: - osm/models.py — one @DataClass per THINK class; associations → typed id fields; each grounded model has CLASS_ID. - osm/controllers/<name>.py — the DO arm as a FAITHFUL mirror of app/controllers/, one module per source controller by its verbatim (snake) name — `osm.controllers.nodes.show(inp)`, standalone, not methods. No singularisation (mirrors the Rust `controllers` module). - osm/__init__.py — re-exports each controller at the package root (`osm.nodes` ≡ `osm.controllers.nodes`) + CLASS_IDS. - ogar_sdk.py — the substrate-pull SDK (Python mirror of Rust lance_graph_contract::ogar_codebook): CODEBOOK + class_id(concept) + render_classid(concept, prefix) (canon-high). Ships OSM (0x0F) AND odoo/commerce (0x02) — the pull is domain-agnostic. Verified: example builds clean; py_compile green on package + SDK; functional smoke — osm.controllers.nodes.show + re-exported osm.nodes.show both callable, class_id('osm_node')=0xf01, render_classid('account_move',0x0002)=0x2020002, osm.nodes.show({}) raises NotImplementedError('port Api::NodesController#show'). Stacks on #153 (the faithful-controllers / no-singulariser change) so the container module names are verbatim (nodes/searches/capabilities, not searche/capabilitie). Typed params (dict Input → typed) remain the ruff brick. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
89f6b18 to
1f62666
Compare
render_osm/render_python: merge colliding rail tiles + CLASS_ID as ClassVar (#154 review)


What
A Python emit back-end for the same ruff → OGAR harvest that
render_osmrenders to Rust. Proves the pull-back side of the transpiler is
language-pluggable: OSM (and odoo) come out of the one substrate in Python
import shape, just like Rust.
Emits
python/osm/— the packagemodels.py— one@dataclassper THINK class; associations → typed idfields (
belongs_to/has_one→Optional[int],has_many/habtm→List[int]); each grounded model carries itsCLASS_ID.controllers/<name>.py— the DO arm as a faithful mirror ofapp/controllers/, one module per source controller by its verbatim (snake)name:
osm.controllers.nodes.show(inp), standalone, not methods. Mirrors theRust
controllersmodule — no singularisation.__init__.py— re-exports each controller at the package root(
osm.nodes≡osm.controllers.nodes) +CLASS_IDS.python/ogar_sdk.py— the substrate-pull SDK (Python mirror of Rustlance_graph_contract::ogar_codebook):Ships OSM (
0x0F) and odoo/commerce (0x02) — the pull is domain-agnostic.Verified
render_pythonbuilds clean;py_compilegreen on package + SDK.osm.controllers.nodes.showand re-exportedosm.nodes.showboth callable;class_id('osm_node')=0xf01;render_classid('account_move',0x0002)=0x2020002;Node.CLASS_ID=0xf01;osm.nodes.show({})→NotImplementedError('port Api::NodesController#show').Depends on
Stacks on #153 (the delete-the-singulariser / faithful-controllers change),
so the container module names are verbatim (
nodes,searches,capabilities— not
searche,capabilitie). Merge #153 first.Follow-up
Inputisdict(the Rails params bag); typed params remain the ruffparam-harvest brick (same gap as the Rust DO arm).
🤖 Generated with Claude Code
https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP