Skip to content

feat(ogar-vocab): agnostic vocab — Inheritance + domain tag + synergies#56

Merged
AdaWorldAPI merged 3 commits into
mainfrom
claude/ogar-vocab-inheritance
Jun 19, 2026
Merged

feat(ogar-vocab): agnostic vocab — Inheritance + domain tag + synergies#56
AdaWorldAPI merged 3 commits into
mainfrom
claude/ogar-vocab-inheritance

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jun 19, 2026

Copy link
Copy Markdown
Owner

Agnostic vocab — three concrete actions (code, not framing)

1. Inheritance metabolization (item 8)

Rails conflates STI parent / abstract base / STI root across parent + abstract_model + inheritance_column_disabled. Collapsed into one typed slot:

  • ogar-vocab: enum Inheritance { Root, Concrete{parent}, Abstract, RootedAt{root} } (#[non_exhaustive], serde, names-as-String) + Class.inheritance (#[serde(default)]). Additive — old fields kept one cycle; all consumers build unchanged.
  • ogar-from-ruff: lift_inheritance maps StiInfo -> the slot. Mixins not consulted (separate axis).

2. Name the curator domain (one tiny classifier)

  • ogar-vocab: Class.source_domain: Option<String> — coarse curator-agnostic tag + ClassFingerprint component.
  • ogar-from-ruff: classify_domain(namespace) -> OpenProject/Redmine = project, Odoo = erp, else None; lift_model_graph tags every class.

3. Wire synergies (cross-domain concept bridges)

  • ogar-vocab: Synergy { concept, members:[SynergyMember{domain, class_name}] } + wire_synergies(&[Class]) — groups by canonical_concept, keeps concepts spanning 2+ distinct domains. OpenProject User <-> Odoo res.users -> concept user. The unified vocab made worth more than its curators.

Green

ogar-vocab 11 · ogar-from-ruff 19 · all 5 other vocab consumers build (additive) · clippy clean.

🤖 Generated with Claude Code

claude added 3 commits June 19, 2026 09:28
…tion

Item 8 of the agnostic flip, as code. Rails conflates three distinct
things across parent + abstract_model + inheritance_column_disabled; this
collapses them into one typed, curator-agnostic slot.

- ogar-vocab: add Inheritance { Root, Concrete{parent}, Abstract,
  RootedAt{root} } (non_exhaustive, Default=Root, serde) + a
  Class.inheritance field (serde(default)). Additive: parent /
  abstract_model / inheritance_column_disabled retained one cycle; all
  existing consumers keep building unchanged.
- ogar-from-ruff: lift_inheritance(model) metabolizes StiInfo —
  inherits_from -> Concrete; abstract_class -> Abstract;
  inheritance_column (no parent) -> RootedAt(self); else Root. Mixins are
  NOT consulted (concerns are a separate axis).

Mixins/concerns stay separate (the doctrine: STI parent and 'include
Journalized' are not the same organ). ogar-vocab 9, ogar-from-ruff 18
green; all 5 other vocab consumers build; clippy clean.
…erp)

One tiny classifier, not a metabolization: tag each lifted Class with its
curator domain so the agnostic shape carries 'what kind of system' without
leaking the namespace.

- ogar-vocab: add Class.source_domain: Option<String> (serde default) — a
  coarse curator-agnostic tag and a ClassFingerprint component.
- ogar-from-ruff: classify_domain(namespace) maps openproject/redmine ->
  'project', odoo -> 'erp', else None (no guessing); lift_model_graph tags
  every class from graph.namespace.

ogar-vocab 9, ogar-from-ruff 19 green; clippy clean; additive.
Once domains are named, wire the synergies: the same canonical concept
surfacing in 2+ domains (OpenProject User <-> Odoo res.users => concept
'user') is what makes the unified vocab worth more than its curators.

- Synergy { concept, members: [SynergyMember{domain, class_name}] }.
- wire_synergies(&[Class]) groups by canonical_concept, keeps only
  concepts spanning 2+ distinct source_domains; deterministic order;
  undomained classes skipped.
- canonical_concept: lowercase, last dotted segment (res.users->users),
  drop a trailing plural s except after ss. Coarse v1 bridge, not a
  thesaurus.

ogar-vocab 11 green; clippy clean.
@AdaWorldAPI AdaWorldAPI changed the title feat(ogar-vocab): Inheritance — agnostic STI/abstract/root metabolization feat(ogar-vocab): agnostic vocab — Inheritance + domain tag + synergies Jun 19, 2026
@AdaWorldAPI AdaWorldAPI merged commit 6d23aab into main Jun 19, 2026
1 check passed

@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: 9378578522

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

let mut class = Class::new(&model.name);
class.language = Language::Ruby;
class.parent = model.sti.as_ref().and_then(sti_parent);
class.inheritance = lift_inheritance(model);

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 lifted inheritance in graph output

When StiInfo represents abstract_class or an STI root, this assignment is the only place that records the fact: parent stays None and the legacy booleans are not updated. I checked TripleEmitter::emit_class in crates/ogar-emitter/src/lib.rs, and it still emits only parent, abstract_model, and inheritance_column_disabled while never reading Class::inheritance, so those newly lifted abstract/root inheritance facts disappear as soon as these classes go through the existing graph/Turtle path. Please either keep the retained legacy fields in sync for this migration cycle or add emission/parsing for the new enum before relying on it downstream.

Useful? React with 👍 / 👎.

AdaWorldAPI pushed a commit that referenced this pull request Jun 22, 2026
…sition

Companion to the typed PortSpec::APP_PREFIX in this branch: adds
`ogar_vocab::app`, the central high-u16 composition machinery from
APP-CLASS-CODEBOOK-LAYOUT.md §0/§4, so consumers re-export ONE source of the
bit math instead of each re-implementing `(prefix << 16) | concept` locally.

  - render_classid(prefix, concept) -> u32      compose the full classid
  - render_classid_for::<P: PortSpec>(concept)  compose from P::APP_PREFIX
  - app_of(classid) -> u16                       high half (render lens, §4 key)
  - concept_of(classid) -> u16                   low half (shared RBAC+ontology)

This resolves the duplication where each consumer (op-canon #56, the
redmine-canon twin) defines its own `render_classid` over a local 0x000N
literal: they now compose via `render_classid_for::<OpenProjectPort>(concept)`
reading the typed PortSpec::APP_PREFIX — one source of truth, same discipline
as class_ids.

Composing the high half is reserving/stamping, not minting a class_id (§2);
concept (low-u16) mints stay gated on the 5+3 pass. Core (hi=0x0000) is
bit-identical to the bare concept (I-APP1/I-APP5), pinned by test.

cargo +1.95 test -p ogar-vocab --lib app:: -> 6 passed; ports:: -> 27 passed;
check --workspace --all-targets clean.
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