feat(ogar-vocab): PortSpec trait + OpenProjectPort/RedminePort#87
Conversation
Adds `pub mod ports` to ogar-vocab carrying the per-port
specifications consumed by `lance_graph_ontology::UnifiedBridge`.
Each port spec is three pieces of data attached to the canonical
class schema:
const NAMESPACE: &str // "OpenProject" / "Redmine"
const BRIDGE_ID: &str // "openproject" / "redmine"
fn aliases() -> &[(&str, u16)]
// [("WorkPackage", PROJECT_WORK_ITEM), ...]
with a default `class_id(public_name)` linear-scan that ports
inherit unchanged. Both `OpenProjectPort::class_id("WorkPackage")`
and `RedminePort::class_id("Issue")` resolve to
`class_ids::PROJECT_WORK_ITEM` (0x0102) — the cross-fork
convergence pin lives here, sourced from the canonical class_ids
constants rather than re-declared per bridge.
After lance-graph migrates to `UnifiedBridge<P: PortSpec>` (sister
PR), the bridges become trivial type aliases:
pub type OpenProjectBridge = UnifiedBridge<OpenProjectPort>;
pub type RedmineBridge = UnifiedBridge<RedminePort>;
and the duplicated `OPENPROJECT_CODEBOOK` / `REDMINE_CODEBOOK`
tables in lance-graph-ontology can be deleted in favour of these
OGAR-sourced specs.
Tests (8) pin the convergence: namespace/bridge_id strings, the
headline WorkPackage/Issue → 0x0102 mapping, full alias-table
convergence across 24 concept pairs (Project ↔ Project,
Status ↔ IssueStatus, Type ↔ Tracker, Forum ↔ Board, etc.),
unknown-name None fall-through, alias drift guard against
class_ids::ALL.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bf1010da8e
ℹ️ 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".
| ("Project", class_ids::PROJECT), | ||
| ("WorkPackage", class_ids::PROJECT_WORK_ITEM), | ||
| ("TimeEntry", class_ids::BILLABLE_WORK_ENTRY), | ||
| ("User", class_ids::PROJECT_ACTOR), |
There was a problem hiding this comment.
Include Principal and Group actor aliases
When the follow-up UnifiedBridge asks this table for actual Rails actor classes, class_id("Principal") and class_id("Group") return None because User is the only alias for PROJECT_ACTOR. The existing real-source coverage in crates/ogar-from-rails/src/lib.rs (redmine_and_openproject_actors_converge_through_canonical) documents that both Redmine and OpenProject ship User and Principal, and later coverage also folds Group into project_actor; falling back for those names will give them non-codebook entity ids instead of the shared 0x0104.
Useful? React with 👍 / 👎.
| ("User", class_ids::PROJECT_ACTOR), | ||
| ("Status", class_ids::PROJECT_STATUS), | ||
| ("Type", class_ids::PROJECT_TYPE), | ||
| ("Priority", class_ids::PRIORITY), |
There was a problem hiding this comment.
Map IssuePriority instead of Priority
The port table is matched with exact string equality, but both Rails curators expose the priority class as IssuePriority, not Priority (see redmine_and_openproject_issuepriority_converge_through_canonical in crates/ogar-from-rails/src/lib.rs). As written, OpenProject returns None for class_id("IssuePriority"), and the Redmine table has no priority alias at all, so the unified bridge will miss the promoted priority codebook id for the actual class name.
Useful? React with 👍 / 👎.
Two codex P2 items on PR #87: 1. Include Principal and Group actor aliases — both Rails curators ship `User`, `Principal`, `Group` as three classes backed by one table; the codebook collapses them via `PROJECT_ACTOR` ("Principal + User + Group STI chain collapsed"). All three now route to the same id in both ports. 2. Map IssuePriority instead of Priority — both curators expose the priority class as `IssuePriority`, not `Priority`. The original OpenProject entry was the canonical-concept name, not the Rails class name; Redmine had no priority entry. Fixed both. Also: expose `OPENPROJECT_ALIASES` and `REDMINE_ALIASES` as `pub const` slices so downstream `pub const` re-exports (e.g. `lance_graph_ontology::bridges::OPENPROJECT_CODEBOOK` compat shim) can alias them. Test deltas: - `each_port_has_expected_alias_count` — OP=27, RM=28 (Redmine carries an extra standalone Comment model). - New `sti_actor_fold_routes_user_principal_group_to_project_actor` walks all three STI-fold names against PROJECT_ACTOR. - New `issue_priority_resolves_via_actual_rails_class_name`. - `openproject_and_redmine_converge_on_shared_concepts` extended with Principal/Principal, Group/Group, IssuePriority/IssuePriority pairs. All 10 inline ports tests pass under `cargo test -p ogar-vocab --lib ports::`.
PortSpectrait + the first two implsPer the user's direction: enforce the migration of
WoaBridge/MedcareBridgestyle per-port bridges to a genericUnifiedBridge<P: PortSpec>harness where the differences betweenbridges come from the OGAR class schema, not from per-bridge hardcoded
tables.
This PR delivers the OGAR side of that migration. The
lance-graph-ontologyside (defineUnifiedBridge<P>, replaceOpenProjectBridge/RedmineBridgewith type aliases) lands as afollow-up PR depending on this one.
What ships
Both ports reference the same
class_ids::*constants for convergingconcepts, so:
Convergence test pins (24 concept pairs)
What this unlocks (the lance-graph follow-up)
The duplicated
OPENPROJECT_CODEBOOK/REDMINE_CODEBOOKtables incrates/lance-graph-ontology/src/bridges/{openproject,redmine}_bridge.rs(currently 25 entries each, duplicated across the two files) get
deleted — the canonical class schema is the only source of truth.
Files
crates/ogar-vocab/src/ports.rs(new) — trait + two impls + 8 testscrates/ogar-vocab/src/lib.rs—pub mod ports;🤖 Generated with Claude Code