Skip to content

OSM Rails → OGAR transpile: THINK structs, Geo codebook, DO-arm action rail, map mapping#152

Merged
AdaWorldAPI merged 11 commits into
mainfrom
claude/medcare-bridge-lance-graph-wmx76z
Jul 4, 2026
Merged

OSM Rails → OGAR transpile: THINK structs, Geo codebook, DO-arm action rail, map mapping#152
AdaWorldAPI merged 11 commits into
mainfrom
claude/medcare-bridge-lance-graph-wmx76z

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

End-to-end ruff → OGAR transpile of openstreetmap/openstreetmap-website
(Ruby/Rails) into .claude/harvest/osm-website-rs/, plus the reusable drivers.
Proves both arms of Open Graph of Active Record — Model and Controller —
on a real corpus. All generated Rust compiles + tests green.

The pipeline

openstreetmap-website/app/{models,controllers}   (Ruby, canonical)
   │  ruff_ruby_spo::{extract, extract_tree_with}      (ruff #42)
   ▼
ruff_spo_triplet::ModelGraph
   │  ogar_from_ruff::{lift_model_graph, lift_actions}
   ▼
Vec<Class> (THINK)  +  Vec<ActionDef> (DO)
   │  ogar-render-askama
   ▼
osm-domain: structs (THINK) + osm::<part_of>::<is_a>(input) fns (DO)

What's in it

  • THINK arm — 50 domain structs (generated/*.rs): associations become
    typed edge fields (belongs_to → Option<u64>, has_many → Vec<u64>) + a
    new(..) ctor + CLASS_ID.
  • Geo codebook mintConceptDomain::Geo (0x0FXX) + 10 concepts
    (osm_node 0x0F01osm_user 0x0F0A). Full codebook contract satisfied
    (CODEBOOK + class_ids::{consts,ALL} + all_promoted_classes() + domain map +
    COUNT_FUSE 68→78); 96 ogar-vocab tests green. Render emits CLASS_ID for
    the 20 grounded geodata files.
  • lance-graph wiringharvest/osm_graph.spo: the association graph as 154
    classid-keyed SPO edges (osm_node[0x0F01] --BelongsTo:changeset--> [0x0F04]).
  • DO arm — controllers harvested to the (part_of : is_a) action rail
    (harvest/osm_actions.rail, 353 tiles = 92 part_of × 140 is_a), emitted as
    standalone osm::<part_of>::<is_a>(input) functions (e.g.
    osm::node::{create,show,list,update,delete}, osm::map::show) — not
    methods on the record. CRUD archetypes at stable low ids; castable by fixing
    one axis and walking the other; view controllers (map, site, dashboard)
    captured.
  • Map architecture docdocs/MERCATOR-HHTL-HELIX-MAP.md: WebMercator z/x/y
    → HHTL Morton (canon already binds "OSM: literal x/y"), helix = the globe
    index vs cesium = the display, ndarray simd_wasm + WebGL render path.
  • Reusable drivers committed into OGAR proper: ogar-from-rails
    (extract_actions + extract_action_rail), harvest_osm.rs, render_osm.rs.

⚠ Operator gate — before merge

  • Confirm the 0x0F / Geo domain allocation. This is a canonical,
    cross-repo codebook mint (RBAC-adjacent, consumed by medcare/odoo/woa/…). It is
    additive (new domain, no existing concept touched) and test-gated, but the
    domain byte is your call.
  • lance-graph mirror is a sequenced follow-up. lance-graph-contract:: ogar_codebook::CODEBOOK + lance-graph-ogar::COUNT_FUSE must go 68→78 after
    this merges (lance-graph pulls ogar_vocab via git main; bumping the mirror
    first breaks its compile-time parity assert). See .claude/harvest/.../NEXT.md.

Depends on

Honest gaps (documented, not papered over)

  • Action fn bodies are todo!("port <controller>#<action>") — the render lifts
    the signature (part_of/is_a), not the Ruby body.
  • The arg is a generic Input — ruff's Function carries reads/writes, not
    typed params yet; render(coordinates: Coordinates) needs a param harvest (next
    ruff brick).
  • Naïve singularizer artifacts (Searches → searche, Capabilities → capabilitie) — a real inflector is the other refinement.
  • AdaWorldAPI/openstreetmap-website-rs (the intended home) could not be pushed
    (session write-allowlist gap); the full tree is parked here under
    .claude/harvest/osm-website-rs/ with relocation steps in PARKED.md.

Scope

Doc/harvest-area + ogar-vocab codebook + producer crates. No consumer-repo
changes. The parked osm-domain crate is a nested workspace (excluded from
OGAR's build).

🤖 Generated with Claude Code

https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP


Generated by Claude Code

claude added 9 commits July 4, 2026 21:34
…ranscode

Add ogar-from-rails/examples/harvest_osm.rs (reusable Rails harvest driver) and
park the openstreetmap-website-rs transcode under harvest/osm-website-rs/ (50
classes, full IR + workspace scaffold). Parked here because the target repo
AdaWorldAPI/openstreetmap-website-rs is clone-able but not push-able in this
session (proxy write-allowlist gap); see harvest/osm-website-rs/PARKED.md for
provenance + relocation.

Co-Authored-By: Claude <noreply@anthropic.com>
…les green

ogar-render-askama render_class_with_methods over the 50-class OSM harvest:
associations -> typed edge fields (belongs_to Option<u64> / has_many Vec<u64>)
+ new() ctor + CANONICAL_CONCEPT. All 50 compile + test green in the parked
osm-domain crate. Adds examples/render_osm.rs driver + ogar-from-rails dev-dep.
Relocated the parked transcode to .claude/harvest/osm-website-rs/ per request.

Co-Authored-By: Claude <noreply@anthropic.com>
Allocate ConceptDomain::Geo (0x0FXX) and mint the OpenStreetMap geodata
reference ontology: osm_node/way/relation (0x0F01-03), changeset, element_tag,
relation_member, way_node, note, gpx_trace, user (0x0F04-0A). Full codebook
contract: CODEBOOK + class_ids consts + ALL + all_promoted_classes()
constructors + COUNT_FUSE pin 68->78, all gates green (96 ogar-vocab tests).

render_osm.rs gains a Rails-name->concept grounding map; the 20 grounded
geodata files now emit CLASS_ID (Node -> 0x0F01, ...). Parked osm-domain
compiles green.

NOTE: canonical cross-repo codebook mint. The lance-graph mirror
(lance-graph-contract ogar_codebook::CODEBOOK + lance-graph-ogar COUNT_FUSE)
must land in lockstep before the OGAR side merges to main (see next commit on
the lance-graph branch). Operator: confirm the 0x0F/Geo domain allocation.

Co-Authored-By: Claude <noreply@anthropic.com>
render_osm.rs now emits harvest/osm_graph.spo — the OSM association graph as
154 classid-keyed SPO edges (node/way/relation as a graph; grounded targets
carry classids). NEXT.md records the honest state of the three autoattended
steps: (1) classid mint DONE with the lance-graph mirror as a sequenced
post-merge follow-up; (2) lance-graph wiring DONE as the SPO manifest; (3)
DO-arm methods = a real capability gap (needs a Ruby controller/action
harvester; no ActionDefs exist from app/models alone).

Co-Authored-By: Claude <noreply@anthropic.com>
OGAR is Open Graph of Active *Record* — Model + Controller — so the DO arm must
exist. Add ogar_from_rails::extract_actions: walk app/controllers via the new
ruff_ruby_spo::extract_tree_with (rev 45b6e62), lift each controller's actions
to ActionDefs (ogar_from_ruff::lift_actions), key by resource model
(Api::NodesController -> Node), dedup by predicate. render_osm attaches them as
Rust methods: Node now carries CLASS_ID=0x0F01 + fields (THINK) + index/show/
create/update/destroy (DO). 318 actions across 92 controllers; parked crate
compiles green. Re-pins both ruff refs to 45b6e62 in lockstep.

Co-Authored-By: Claude <noreply@anthropic.com>
…sm-WebGL

Grounded mapping (verified against ndarray cesium/*, splat3d/helix_orient.rs,
simd_wasm.rs + OGAR 256x256-tile canon): WebMercator z/x/y -> HHTL HEEL/HIP/TWIG
via Morton interleave (canon already binds 'OSM: literal x/y' to the tier tile);
helix = the equal-area globe-position INDEX (not the display); cesium = the
WebMercator + tile I/O half (osm_pbf/esri_crs/tileset, already coded); splat3d +
ndarray simd_wasm compute + WebGL surface = the render path (q2 /helix pattern).
Fences: Mercator is display, helix is address; falsifier = tile nibble-ancestry.

Co-Authored-By: Claude <noreply@anthropic.com>
Actions were a flat REST enum (index/show/create/update/destroy) — not castable.
Reshape to the V3 (part_of:is_a) rail: part_of = controller container (incl.
view controllers map/overview/site that the resource mapping dropped), is_a =
action archetype (CRUD at stable low ids 0x01-07, custom verbs after). Each
action is a u8:u8 HHTL tile; cast = fix one axis, walk the other (is_a=show
across all containers; part_of=map across all verbs). extract_action_rail +
harvest/osm_actions.rail manifest (353 tiles, 92 part_of x 140 is_a). Model
methods now named by is_a archetype (index->list, destroy->delete).

Co-Authored-By: Claude <noreply@anthropic.com>
… ctor

The Rails 'new' action rendered as pub fn new(&self), colliding with the
struct's pub fn new(fields) constructor (E0592, 7 dups). Map the archetype
new -> new_form (it's the show-new-form action, not construction). Parked
osm-domain now compiles + tests green (verified before commit).

Co-Authored-By: Claude <noreply@anthropic.com>
… methods

Per the corrected shape: actions are namespaced free functions, not methods on
the data record. generated/actions.rs is the module tree — part_of = module,
is_a = fn: osm::map::show(input), osm::node::{create,delete,list,show,update}
(input) -> Output. Structs are now THINK-arm only (data + CLASS_ID). Input is a
typed request stub (ruff Function has reads/writes, not param types yet — the
param-name harvest is the follow-up). ident sanitiser handles '?' verbs +
keywords; dedup per module. 318 fns / 92 containers; compiles green (verified).

Co-Authored-By: Claude <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 744521d. Configure here.

let mut acts = String::from(
"//! @generated DO-arm — OSM controller actions as `osm::<part_of>::<is_a>(input)`.\n\
//! part_of = container module, is_a = action fn (standalone, not methods on the\n\
//! data struct, per OGAR's ActionDef rule). Call: `osm::map::render(Input::default())`.\n\n\

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DO-arm drops duplicate rail tiles

High Severity

The actions.rs generation collapses multiple Rails controller actions into a single Rust function when they share the same (part_of, is_a) pair. The tree map's or_insert method keeps only the first entry, leading to missing functions, incorrect source citations, and documentation examples that don't match the actual output.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 744521d. Configure here.

let mut classes = ogar_from_rails::extract_with(src_path, "osm");
if classes.is_empty() {
classes = ogar_from_rails::extract_with(&src_path.join("app/models"), "osm");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller harvest ignores models fallback

Medium Severity

THINK structs fall back to app/models when extract_with on the repo root is empty, but extract_action_rail always uses the original path joined with app/controllers. Passing only the models directory still renders structs while the DO arm and rail stay empty because controllers resolve to a nonexistent app/models/app/controllers tree.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 744521d. Configure here.

rt.push('\n');
if let Some(root) = out.ancestors().nth(4) {
fs::write(root.join("harvest/osm_actions.rail"), &rt).unwrap();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Harvest manifests skip silently

Medium Severity

osm_graph.spo and osm_actions.rail are written only when out.ancestors().nth(4) resolves. A custom --out-dir that is not five levels under the harvest tree still renders structs and actions.rs but skips updating the manifest files with no warning.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 744521d. Configure here.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 744521d72b

ℹ️ 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".

// user are the edit-provenance concepts. Versioned `Old*` Rails classes
// ground to their base concept (same concept — the temporal axis is not a
// new id).
("osm_node", 0x0F01),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the Geo terms to the canonical vocab

This mints the new 0x0FXX concepts only in Rust; the commit does not add matching terms to vocab/ogar.ttl or vocab/ogar.surql. The repo instructions in .claude/AGENTS.md say the vocabulary is the contract and new crates/ogar-vocab terms must be added there first, so TTL/SurrealQL consumers will not see osm_node, osm_way, etc. while Rust consumers do.

Useful? React with 👍 / 👎.

Comment on lines +233 to +236
tree.entry(r.part_of.clone())
.or_default()
.entry(r.is_a.clone())
.or_insert((r.controller.clone(), r.action.clone()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve API/web actions with the same rail tile

When two controllers produce the same (part_of, is_a) pair, this or_insert silently keeps the first one and drops the rest from generated/actions.rs. The harvested rail already has real collisions such as node:show for both Api::NodesController#show and NodesController#show, so the generated DO arm loses one route/action body before it can be ported.

Useful? React with 👍 / 👎.

let ir = std::env::args().any(|a| a == "--ir");
let root = Path::new(&arg);

let mut classes = ogar_from_rails::extract_with(root, "osm");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route OSM harvests through the Geo codebook

Calling the reusable Rails lift with curator "osm" does not assign the new Geo concepts: classify_domain only recognizes OpenProject/Redmine/Odoo/WOA namespaces, source_domain_concept has no Geo mapping, and canonical_concept has no OSM aliases, so this IR dump reports lexical concepts like node/way with no 0x0FXX codebook ids. Only render_osm.rs has a private ground() workaround, leaving harvest_osm and library callers with ungrounded OSM IR.

Useful? React with 👍 / 👎.

claude added 2 commits July 4, 2026 22:36
The codebook mint (0x0F Geo) landed in ogar-vocab CODEBOOK + class_ids::ALL but
not in ogar-class-view's all_canonical_classes() registry, tripping the two
drift gates (every_codebook_id_appears_in_class_ids_all +
known_class_ids_iterates_in_stable_codebook_order). Add the 10 osm_* entries
(import + registry vec) in class_ids::ALL order. ogar-class-view: 12 tests green.

Co-Authored-By: Claude <noreply@anthropic.com>
Reconcile the OCR 0x08XX domain to what the lance-graph contract mirror
already declares, and address the Bugbot/codex review on #152.

network_layer (0x0804) mint — the OGAR-authoritative counterpart of the
concept the lance-graph mirror wire-declared first (`class_ids::ALL`
68→…→79 with the 10 OSM/Geo concepts). The KIND "a Tesseract recognizer
network layer"; the concrete subclass is the classid custom-low half (a
`NetworkType` ordinal), one slot not 27 — the layer graph sinks onto
FacetCascade tenants. Added to all four codebook places (CODEBOOK,
class_ids::{NETWORK_LAYER, ALL}, all_promoted_classes, constructor) +
ogar-class-view all_canonical_classes; OCR domain-set + ALL.len() pins
3→4 / 78→79. 96 ogar-vocab + 12 ogar-class-view tests green.

render_osm review fixes:
- DO-arm no longer drops colliding rail tiles. The (part_of:is_a) tile is
  the CANONICAL castable address by design, so multiple source controllers
  (`Api::NodesController#show` + `NodesController#show` → `node:show`) are
  ONE action — but provenance was silently lost. Now every source is
  accumulated and cited in the doc comment (Bugbot High / codex P2).
- extract_action_rail resolves the repo ROOT (not the app/models fallback
  dir) so controllers resolve for a models-only `src` (Bugbot Medium).
- manifest writes warn instead of silently skipping when the out-dir isn't
  ≥5 levels under a harvest tree (Bugbot Medium).

Deferred (noted on the PR): vocab/ogar.ttl + ogar.surql are stale across
OCR/HR/Genetics/Geo (regenerate wholesale, not osm-only patch, codex P1);
moving render_osm's private ground() into a shared lift so harvest_osm +
library callers get grounded Geo IR (codex P2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP

Copy link
Copy Markdown
Owner Author

Review response (Bugbot + codex), pushed in 58a64d4:

Fixed

  • DO-arm dropped colliding rail tiles (Bugbot High / codex P2) — the (part_of : is_a) tile is the canonical castable address by design, so Api::NodesController#show + NodesController#shownode:show is correctly one action (the api-vs-web surface is the classid custom-low half, not a new tile). The real defect was silently losing the extra source from the docs; the tree value is now Vec<(controller, action)> and every source is deduped, sorted, and cited (Sources (canonical tile): …). No route is lost.
  • Controller harvest ignored the models fallback (Bugbot Medium) — extract_action_rail now resolves the repo root (steps up from app/models, or uses the dir that actually contains app/controllers) instead of blindly joining app/controllers onto whatever src was.
  • Manifests skipped silently (Bugbot Medium) — both osm_graph.spo and osm_actions.rail now eprintln! a warning when the out-dir isn't ≥5 levels under a harvest tree.

Also landed (fuse reconciliation) — minted network_layer (0x0804) as the OGAR-authoritative counterpart of the concept the lance-graph contract mirror had wire-declared first; class_ids::ALL is now 79 and the lance-graph mirror + COUNT_FUSE follow in lance-graph #648 (both sides 79).

Deferred (with reasons)

  • P1 — Geo terms into vocab/ogar.ttl + ogar.surql. Correct per policy, but those files are stale across many domains (OCR / HR / Genetics / Geo are all absent; last touched Jun 21). Patching osm-only would be a half-measure — the right fix is to regenerate the vocab exports wholesale from ogar-vocab, which is its own task, not this PR.
  • P2 — route harvests through the Geo codebook in the reusable lift. Today only render_osm.rs's private ground() maps OSM→0x0FXX; harvest_osm and library callers get lexical concepts. The clean fix is to move ground() into a shared ogar-from-rails helper (and/or teach source_domain_concept/canonical_concept the Geo aliases) — a follow-up brick, filed here so it isn't lost.

Generated by Claude Code

@AdaWorldAPI AdaWorldAPI merged commit 5d29a22 into main Jul 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants