Skip to content

Commit 6075d00

Browse files
authored
Merge pull request #562 from AdaWorldAPI/claude/bridge-codebook-convergence
fix(bridges): codebook convergence + seed OpenProject/Redmine + co-located tests
2 parents df21a20 + 230e5f4 commit 6075d00

8 files changed

Lines changed: 1075 additions & 48 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
//! `bridges::codebook` — shared canonical class_id constants for the
2+
//! ports that emit through the OGAR codebook.
3+
//!
4+
//! Northstar plan §3 C4/C5 follow-up. Codex P1 on PR #559 flagged that
5+
//! `OpenProjectBridge::entity("WorkPackage")` and
6+
//! `RedmineBridge::entity("Issue")` minted **distinct** entity_type_ids
7+
//! despite the PR body promising both routed to the same
8+
//! `project_work_item` arm. Root cause: the registry's
9+
//! [`crate::registry::RegistryState::append`] reuses `entity_type_id`
10+
//! only when the exact `ogit_uri` matches, so per-port public names
11+
//! never converge on a shared id by accident.
12+
//!
13+
//! This module is the single source of truth for the canonical class_ids;
14+
//! both [`crate::bridges::OpenProjectBridge`] and
15+
//! [`crate::bridges::RedmineBridge`] reference these constants in their
16+
//! per-port public-name → class_id alias tables. Consumers that dispatch
17+
//! on `EntityRef::schema_ptr.entity_type_id()` get the same id for
18+
//! `WorkPackage`/`Issue`/etc. across both bridges — the cross-fork
19+
//! convergence the codebook was calcified for.
20+
//!
21+
//! # Source of authority
22+
//!
23+
//! Mirrors `ogar-vocab::class_ids::*` (the calcified canonical codebook,
24+
//! 32 promoted concepts). This crate intentionally **does not** depend
25+
//! on `ogar-vocab` to avoid a cycle (OGAR depends on
26+
//! lance-graph-contract, and lance-graph-ontology sits between them);
27+
//! the constants below are the same values verbatim. Adding a
28+
//! cross-crate `class_ids_match_ogar_vocab` smoke test in OGAR is a
29+
//! roadmap item.
30+
//!
31+
//! # Coverage
32+
//!
33+
//! Project-management arm (0x01XX) — all 26 promoted concepts.
34+
//! Commerce arm (0x02XX) and other arms — added as ports adopt them.
35+
36+
// ── Project-management arm (0x01XX) — 26 promoted concepts ────────────
37+
38+
/// `project` (0x0101) — the project itself. OpenProject `Project`,
39+
/// Redmine `Project`.
40+
pub const PROJECT: u16 = 0x0101;
41+
42+
/// `project_work_item` (0x0102) — the canonical work-item concept.
43+
/// **OpenProject `WorkPackage`, Redmine `Issue` — the headline
44+
/// convergence pin.**
45+
pub const PROJECT_WORK_ITEM: u16 = 0x0102;
46+
47+
/// `billable_work_entry` (0x0103) — hours logged against a work item.
48+
/// OpenProject `TimeEntry`, Redmine `TimeEntry`.
49+
pub const BILLABLE_WORK_ENTRY: u16 = 0x0103;
50+
51+
/// `project_actor` (0x0104) — User / Principal in the port's terms.
52+
pub const PROJECT_ACTOR: u16 = 0x0104;
53+
54+
/// `project_status` (0x0105) — OpenProject `Status`, Redmine
55+
/// `IssueStatus`.
56+
pub const PROJECT_STATUS: u16 = 0x0105;
57+
58+
/// `project_type` (0x0106) — OpenProject `Type`, Redmine `Tracker`.
59+
pub const PROJECT_TYPE: u16 = 0x0106;
60+
61+
/// `priority` (0x0107).
62+
pub const PRIORITY: u16 = 0x0107;
63+
64+
/// `project_membership` (0x0108) — OpenProject `Membership`, Redmine
65+
/// `Member`.
66+
pub const PROJECT_MEMBERSHIP: u16 = 0x0108;
67+
68+
/// `project_journal` (0x0109) — change history.
69+
pub const PROJECT_JOURNAL: u16 = 0x0109;
70+
71+
/// `project_repository` (0x010A) — SCM repository attached to a project.
72+
pub const PROJECT_REPOSITORY: u16 = 0x010A;
73+
74+
/// `project_version` (0x010B) — milestone / target version.
75+
pub const PROJECT_VERSION: u16 = 0x010B;
76+
77+
/// `project_wiki_page` (0x010C).
78+
pub const PROJECT_WIKI_PAGE: u16 = 0x010C;
79+
80+
/// `project_query` (0x010D) — saved filter / list-view spec.
81+
pub const PROJECT_QUERY: u16 = 0x010D;
82+
83+
/// `project_attachment` (0x010E).
84+
pub const PROJECT_ATTACHMENT: u16 = 0x010E;
85+
86+
/// `project_comment` (0x010F).
87+
pub const PROJECT_COMMENT: u16 = 0x010F;
88+
89+
/// `project_custom_field` (0x0110).
90+
pub const PROJECT_CUSTOM_FIELD: u16 = 0x0110;
91+
92+
/// `project_relation` (0x0111) — OpenProject `Relation`, Redmine
93+
/// `IssueRelation`.
94+
pub const PROJECT_RELATION: u16 = 0x0111;
95+
96+
/// `project_changeset` (0x0112).
97+
pub const PROJECT_CHANGESET: u16 = 0x0112;
98+
99+
/// `project_watcher` (0x0113).
100+
pub const PROJECT_WATCHER: u16 = 0x0113;
101+
102+
/// `project_news` (0x0114).
103+
pub const PROJECT_NEWS: u16 = 0x0114;
104+
105+
/// `project_message` (0x0115) — forum message.
106+
pub const PROJECT_MESSAGE: u16 = 0x0115;
107+
108+
/// `project_forum` (0x0116) — OpenProject `Forum`, Redmine `Board`.
109+
pub const PROJECT_FORUM: u16 = 0x0116;
110+
111+
/// `project_role` (0x0117).
112+
pub const PROJECT_ROLE: u16 = 0x0117;
113+
114+
/// `project_member_role` (0x0118) — join row between Membership and Role.
115+
pub const PROJECT_MEMBER_ROLE: u16 = 0x0118;
116+
117+
/// `project_custom_value` (0x0119) — instance of a CustomField for one
118+
/// work item.
119+
pub const PROJECT_CUSTOM_VALUE: u16 = 0x0119;
120+
121+
/// `project_enabled_module` (0x011A) — per-project module toggle.
122+
pub const PROJECT_ENABLED_MODULE: u16 = 0x011A;
123+
124+
#[cfg(test)]
125+
mod tests {
126+
use super::*;
127+
128+
/// Headline convergence pin: the canonical concept both
129+
/// OpenProject's `WorkPackage` and Redmine's `Issue` route through
130+
/// has the codebook id `0x0102`. The whole point of C4 + C5.
131+
#[test]
132+
fn project_work_item_is_0x0102() {
133+
assert_eq!(PROJECT_WORK_ITEM, 0x0102);
134+
}
135+
136+
/// All 26 project-management arm ids are in the `0x01XX` block and
137+
/// strictly monotonic in the order they appear here — matches the
138+
/// codebook layout. Drift here means drift from the OGAR codebook.
139+
#[test]
140+
fn project_arm_ids_are_dense_monotone_0x0101_through_0x011a() {
141+
let ids = [
142+
PROJECT,
143+
PROJECT_WORK_ITEM,
144+
BILLABLE_WORK_ENTRY,
145+
PROJECT_ACTOR,
146+
PROJECT_STATUS,
147+
PROJECT_TYPE,
148+
PRIORITY,
149+
PROJECT_MEMBERSHIP,
150+
PROJECT_JOURNAL,
151+
PROJECT_REPOSITORY,
152+
PROJECT_VERSION,
153+
PROJECT_WIKI_PAGE,
154+
PROJECT_QUERY,
155+
PROJECT_ATTACHMENT,
156+
PROJECT_COMMENT,
157+
PROJECT_CUSTOM_FIELD,
158+
PROJECT_RELATION,
159+
PROJECT_CHANGESET,
160+
PROJECT_WATCHER,
161+
PROJECT_NEWS,
162+
PROJECT_MESSAGE,
163+
PROJECT_FORUM,
164+
PROJECT_ROLE,
165+
PROJECT_MEMBER_ROLE,
166+
PROJECT_CUSTOM_VALUE,
167+
PROJECT_ENABLED_MODULE,
168+
];
169+
assert_eq!(ids.len(), 26);
170+
// All in the project-management arm.
171+
for id in ids {
172+
assert!(
173+
(0x0101..=0x011A).contains(&id),
174+
"id 0x{id:04X} outside project-arm block 0x0101..=0x011A"
175+
);
176+
}
177+
// Strictly monotone (codebook order pin).
178+
for w in ids.windows(2) {
179+
assert!(w[0] < w[1], "0x{:04X} should precede 0x{:04X}", w[0], w[1]);
180+
}
181+
}
182+
}

crates/lance-graph-ontology/src/bridges/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Default tenant bridge implementations.
22
//!
3-
//! Six bridges ship today:
3+
//! Seven bridges ship today:
44
//!
55
//! - [`OgitBridge`]: pass-through bridge for tools that already speak raw
66
//! OGIT URIs. `bridge_id = "ogit"`. Locks to whatever namespace its
@@ -23,21 +23,37 @@
2323
//! port (`openproject-nexgen-rs` + `op-canon`) with the scoped
2424
//! registry view every consumer that touches OpenProject data on the
2525
//! unified bridge goes through.
26+
//! - [`RedmineBridge`]: locks to the `Redmine` namespace. Public names
27+
//! like `Issue` / `TimeEntry` / `Project` resolve to
28+
//! `ogit.Redmine:*` URIs. Northstar plan §3 C5 — sibling of
29+
//! `OpenProjectBridge` over the same 32 promoted concepts. Both
30+
//! bridges synthesize EntityRefs whose `entity_type_id()` is the
31+
//! shared OGAR codebook id for the canonical concept (so
32+
//! `WorkPackage` and `Issue` both → `0x0102 project_work_item`),
33+
//! delivering the cross-fork convergence pin.
34+
//!
35+
//! The shared codebook constants both project-management bridges
36+
//! reference live in [`codebook`] — single source of truth so the
37+
//! OpenProject port and the Redmine port can't drift on the same
38+
//! canonical concept's class_id.
2639
//!
2740
//! The `smb-bridge` and `callcenter-bridge` are NOT created in this
2841
//! session: smb stays on its native ontology fallback, callcenter has its
2942
//! own auth + per-customer scoping concerns that need a separate design pass.
3043
44+
pub mod codebook;
3145
mod medcare_bridge;
3246
mod ogit_bridge;
3347
mod openproject_bridge;
48+
mod redmine_bridge;
3449
mod sharepoint_bridge;
3550
mod spear_bridge;
3651
mod woa_bridge;
3752

3853
pub use medcare_bridge::MedcareBridge;
3954
pub use ogit_bridge::OgitBridge;
40-
pub use openproject_bridge::OpenProjectBridge;
55+
pub use openproject_bridge::{OpenProjectBridge, OPENPROJECT_CODEBOOK};
56+
pub use redmine_bridge::{RedmineBridge, REDMINE_CODEBOOK};
4157
pub use sharepoint_bridge::SharePointBridge;
4258
pub use spear_bridge::SpearBridge;
4359
pub use woa_bridge::WoaBridge;

0 commit comments

Comments
 (0)