Skip to content

Commit 302c284

Browse files
authored
Merge pull request #110 from AdaWorldAPI/claude/medcare-bridge-lance-graph-wmx76z
Mint the 0x0B AuthStore class family (keystone §7, confirmed by OGIT shape)
2 parents 0bbb979 + 9034170 commit 302c284

6 files changed

Lines changed: 948 additions & 269 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,58 @@
1515
1616
## Entries (newest first)
1717

18+
## 2026-06-23 — OGIT's Configuration entity ⊨ the keystone's auth_store; the 0x0B AuthStore family is minted (autoattended resolution)
19+
**Status:** FINDING
20+
**Scope:** OGAR keystone §7 ↔ canonical OGIT shape convergence × the 0x0B mint × autoattended decision-making
21+
22+
The operator's insight — *"having our vision and already the canonical
23+
OGIT shape it's easy"* — is correct and now has a receipt in code. The
24+
OGAR keystone (`CLASSID-RBAC-KEYSTONE-SPEC.md` §7) and the canonical
25+
OGIT Auth shape (the 2026-06-23 entry below) converge **1:1**, which
26+
collapses the "which auth harness" question from a fraught decision into
27+
plain sequencing.
28+
29+
The convergence, term-for-term:
30+
31+
| OGIT Auth (canonical shape, upstream) | keystone §7 | Zitadel |
32+
|---|---|---|
33+
| `Account` (the `sub`) | actor `0x0104` | User |
34+
| `Application` | class scope | Project/App |
35+
| `Role` | role `0x0117` | Project-Role |
36+
| `RoleAssignment` | membership tuple `0x0108/0x0118` | Grant |
37+
| `Organization`/`OrgDomain` | row-scope (axis 3) | Org |
38+
| `DataScope`/`scopeId` | row-scope predicate | scope |
39+
| **`Configuration`** (keyed org/app/account/scope IDs + `configurationData`) | **`auth_store 0x0B01`** | the IdP config record |
40+
41+
The punchline: **arago's January-2026 `Configuration`-bridge entity IS
42+
the keystone's `auth_store`** — same four external-ID keys, same config
43+
blob, built upstream independently. Keystone §7 had already written
44+
"Zitadel maps 1:1"; the OGIT shape is the receipt that it isn't
45+
speculative.
46+
47+
**Autoattended resolution (this session):** because the vision and the
48+
canonical shape agree, the tractable part shipped without a steer
49+
round-trip — the `0x0B` Auth domain is **minted** in `ogar-vocab`:
50+
`auth_store 0x0B01` + `auth_zitadel 0x0B02` / `auth_zanzibar 0x0B03` /
51+
`auth_ory_keto 0x0B04` (CODEBOOK + `class_ids` consts + `ALL` +
52+
`ConceptDomain::Auth` + `all_promoted_classes()` builders +
53+
`ogar-class-view` registration + tests). 298/0 workspace tests.
54+
55+
What stayed gated (the keystone's OWN gates, not caution): the
56+
`authorize()` **enforcement** waits on `PROBE-OGAR-RBAC-AUTHORIZE`
57+
(§10); the woa `WoaMembraneGate` mirror and the `project_role.permissions`
58+
→ typed-grant Core change land per keystone §11 build order. Minting the
59+
profiles is "reserving costs nothing"; enforcing them is the gated,
60+
security-review-class step. Full decision record:
61+
`.claude/board/ISSUES.md` ISS-RBAC-AUTHORIZE-BY-CLASSID.
62+
63+
Method note (autoattended decision-making): autonomy means honoring the
64+
PROJECT'S ratified gates (the probe, the 5+3-hardened keystone), not
65+
bulldozing them. The mint is spec-ratified (keystone §7 is hardened,
66+
zero BLOCK) and confirmed by the OGIT shape, so it ships; the
67+
enforcement has an explicit probe gate, so it waits. That distinction
68+
is what makes "auto-resolve" responsible rather than reckless.
69+
1870
## 2026-06-23 — Live 2026 receipt for the semantic-compiler thesis: bardioc is actively extending OGIT's Auth symbol table with a linker-phase external-IAM bridge (probably Zitadel)
1971
**Status:** FINDING (shape-grounded; external system not named in-file → [H], not [G])
2072
**Scope:** addendum to the 2026-06-22 "OGIT was already a semantic compiler's symbol table" entry below × Auth-domain dating × the AuthStore-mapping pattern × the queued 0x0BXX cross-walk

.claude/board/ISSUES.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# ISSUES.md — open issues / blockers / tracked decisions for OGAR
2+
3+
> **APPEND-ONLY.** Newest at top. Each entry: an id, a `**Status:**`
4+
> line (OPEN / RESOLVED / SUPERSEDED — the only mutable line), the
5+
> question, the evidence, the decision/resolution, and what (if
6+
> anything) remains gated. Corrections append as new dated lines citing
7+
> the original.
8+
9+
## Entries (newest first)
10+
11+
## ISS-RBAC-AUTHORIZE-BY-CLASSID — reconcile the shipped MembraneGate path with the keystone; mint the AuthStore family
12+
**Status:** RESOLVED (decision made autoattended; the `0x0B` mint is shipped this session; the `authorize()` *enforcement* remains gated on `PROBE-OGAR-RBAC-AUTHORIZE`, the keystone's own §10 gate)
13+
**Filed/Resolved:** 2026-06-23
14+
15+
### What this corrects
16+
An earlier framing called classid-keyed authorize a "hard blocker." It
17+
is not. The auth surface is largely SHIPPED, just not unified, and
18+
OGAR's own design for it is already hardened:
19+
20+
- `lance-graph-rbac` exists: `Policy` / `Role` / `Operation::{Read{depth},
21+
Write{predicate}}` / `AccessDecision::{Allow,Deny,Escalate}` /
22+
`smb_policy()`.
23+
- smb-office-rs PR #29 shipped `SmbMembraneGate` (~30 LOC, newtype over
24+
`Arc<lance_graph_rbac::Policy>`, keyed `(role × entity_type)`, impl
25+
`MembraneGate`). The spec's enforcement, instantiated.
26+
- woa-rs is on the OLD path (`UnifiedBridge<WoaBridge>`); migration =
27+
mirror smb #29. NOT blocked on a missing primitive.
28+
- medcare is the bigger lift (lacks `medcare-rbac` + `medcare-realtime`,
29+
~800 LOC, deferred behind DM-7/DM-8 per `MEDCARE_POLICY_GAP.md`).
30+
- **OGAR's own `docs/CLASSID-RBAC-KEYSTONE-SPEC.md` v2 is hardened
31+
(zero remaining BLOCK)** and already decided classid-keyed authorize
32+
is canonical (I-K1), with the AuthStore class family preminted (§7).
33+
34+
### It's not two harnesses — it's interim (shipped) vs canonical (gated), one sequence
35+
1. SHIPPED INTERIM: `Policy` (keyed `entity_type: &str`) + `MembraneGate`
36+
/ `SmbMembraneGate` (smb #29). Proven.
37+
2. lance-graph `super-domain-rbac-tenancy-v1.md` §3.9: 4-stage
38+
`authorize`; §13.1 composes onto `callcenter::policy`.
39+
3. OGAR CANONICAL: `CLASSID-RBAC-KEYSTONE-SPEC` v2 — `authorize(actor,
40+
classid, op)` via the `ClassRbac` trait, ReBAC-compiled-at-the-key,
41+
typed grants replacing `project_role.permissions: text`, consumer
42+
bridges evaporate after collapse (§11.5). Gated on
43+
`PROBE-OGAR-RBAC-AUTHORIZE` (§10).
44+
45+
The keystone §1 names the string-`entity_type` `Policy` as the *defect*
46+
it removes — so "MembraneGate canonical" and "keystone canonical" differ
47+
on END-STATE but NOT on sequencing: the keystone is gated on an un-green
48+
probe, so MembraneGate is the right thing to ship NOW.
49+
50+
### The OGIT convergence that makes this easy (operator insight 2026-06-23)
51+
OGAR's keystone vision and the canonical OGIT shape CONVERGE 1:1.
52+
arago's January-2026 `NTO/Auth/Configuration` entity (keyed by
53+
`organizationId`/`accountId`/`applicationId`/`scopeId` +
54+
`configurationData`, "registered in hiro knowledge core") IS the
55+
keystone's `auth_store` — the IdP→classid mapping class — built
56+
upstream independently. Zitadel maps 1:1
57+
(Project→class-scope, Project-Role→role, Grant→membership tuple,
58+
Org→scope, User→`sub`). The decision dissolves: we are matching an
59+
existing shape, not inventing one. (Receipt: `EPIPHANIES.md` 2026-06-23
60+
entries; `OGAR-AS-IR.md` linker phase.)
61+
62+
### Decision (autoattended, RESOLVED)
63+
- **Canonical end-state:** the keystone v2 — classid-keyed `authorize`
64+
via `ClassRbac`, AuthStore profiles. Already hardened; confirmed by
65+
the OGIT shape.
66+
- **Interim (ship now, unblocks woa):** woa-rs mirrors smb #29
67+
`WoaMembraneGate` over `Arc<lance_graph_rbac::Policy>`, classid from
68+
`WoaPort::class_id`. This is the SHIPPED `MembraneGate` pattern, NOT a
69+
`*Bridge` stopgap — so it does not violate the README guidance.
70+
- **Sequencing:** the probe orders them; they do not compete.
71+
72+
### Shipped this session (the tractable, ungated part)
73+
The `0x0B` Auth domain is **minted** in `ogar-vocab` per keystone §7 —
74+
`auth_store 0x0B01` + `auth_zitadel 0x0B02` / `auth_zanzibar 0x0B03` /
75+
`auth_ory_keto 0x0B04`: CODEBOOK entries, `class_ids` consts, `ALL`,
76+
`ConceptDomain::Auth` (`0x0B` → Auth), `all_promoted_classes()`
77+
builders, `ogar-class-view` `all_canonical_classes()` registration,
78+
tests (`auth_domain_concepts_resolve_and_route`). Reservations only —
79+
"reserving costs nothing." 298/0 workspace tests; fmt-clean; no new
80+
clippy.
81+
82+
### Held (gated, NOT this session — the keystone's own gates)
83+
- The `authorize()` **enforcement** (ClassRbac trait impl + the
84+
bit-for-bit decision) — gated on `PROBE-OGAR-RBAC-AUTHORIZE` (§10)
85+
running green against a reference (Odoo `ir.model.access ∧ ir.rule`,
86+
Redmine `User#allowed_to?`, or an OpenFGA model). Security-review-class.
87+
- The woa-rs `WoaMembraneGate` mirror — a different repo; mirrors smb #29
88+
when woa work is picked up. Unblocked, not gated.
89+
- `project_role.permissions: text` → typed-grant Core change (§6) —
90+
lands with the keystone build order §11, after the probe.
91+
92+
### Refs
93+
`docs/CLASSID-RBAC-KEYSTONE-SPEC.md` (§7 AuthStore, §10 probe, §11
94+
order); lance-graph `super-domain-rbac-tenancy-v1.md` §3.9/§13.1;
95+
smb-office-rs PR #29; `MEDCARE_POLICY_GAP.md`; `EPIPHANIES.md`
96+
2026-06-23 (the OGIT convergence + the mint).

crates/ogar-class-view/src/lib.rs

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,15 @@ use lance_graph_contract::{
6464
ontology::{DisplayTemplate, FieldRef, ObjectView},
6565
};
6666
use ogar_vocab::{
67-
billable_work_entry, billing_party, canonical_concept_id, commercial_document,
68-
commercial_line_item, currency_policy, diagnosis, lab_value, medication, patient,
69-
payment_record, priority, project, project_actor, project_attachment, project_changeset,
70-
project_comment, project_custom_field, project_custom_value, project_enabled_module,
71-
project_forum, project_journal, project_member_role, project_membership, project_message,
72-
project_news, project_query, project_relation, project_repository, project_role,
73-
project_status, project_type, project_version, project_watcher, project_wiki_page,
74-
project_work_item, tax_policy, treatment, visit, vital_sign, Class,
67+
Class, auth_ory_keto, auth_store, auth_zanzibar, auth_zitadel, billable_work_entry,
68+
billing_party, canonical_concept_id, commercial_document, commercial_line_item,
69+
currency_policy, diagnosis, lab_value, medication, patient, payment_record, priority, project,
70+
project_actor, project_attachment, project_changeset, project_comment, project_custom_field,
71+
project_custom_value, project_enabled_module, project_forum, project_journal,
72+
project_member_role, project_membership, project_message, project_news, project_query,
73+
project_relation, project_repository, project_role, project_status, project_type,
74+
project_version, project_watcher, project_wiki_page, project_work_item, tax_policy, treatment,
75+
visit, vital_sign,
7576
};
7677

7778
/// All 32 promoted canonical concepts: `(canonical_concept_name, Class)`.
@@ -124,6 +125,11 @@ fn all_canonical_classes() -> Vec<(&'static str, Class)> {
124125
("treatment", treatment()),
125126
("visit", visit()),
126127
("vital_sign", vital_sign()),
128+
// ── 0x0BXX — auth (the AuthStore class family, keystone §7) ──
129+
("auth_store", auth_store()),
130+
("auth_zitadel", auth_zitadel()),
131+
("auth_zanzibar", auth_zanzibar()),
132+
("auth_ory_keto", auth_ory_keto()),
127133
]
128134
}
129135

@@ -140,7 +146,8 @@ fn all_canonical_classes() -> Vec<(&'static str, Class)> {
140146
/// [`DisplayTemplate::Detail`] — per-class template selection is the
141147
/// renderer's job, not this adapter's.
142148
fn lift_object_view(class: &Class) -> ObjectView {
143-
let mut fields: Vec<FieldRef> = Vec::with_capacity(class.attributes.len() + class.associations.len());
149+
let mut fields: Vec<FieldRef> =
150+
Vec::with_capacity(class.attributes.len() + class.associations.len());
144151
for attr in &class.attributes {
145152
fields.push(FieldRef::new(attr.name.clone(), attr.name.clone()));
146153
}
@@ -402,13 +409,13 @@ mod tests {
402409
let v = OgarClassView::new();
403410
let id = canonical_concept_id("billable_work_entry").unwrap();
404411
let class = billable_work_entry();
405-
let assoc_names: std::collections::HashSet<&str> = class
406-
.associations
412+
let assoc_names: std::collections::HashSet<&str> =
413+
class.associations.iter().map(|a| a.name.as_str()).collect();
414+
let field_names: std::collections::HashSet<&str> = v
415+
.fields(id)
407416
.iter()
408-
.map(|a| a.name.as_str())
417+
.map(|f| f.predicate_iri.as_str())
409418
.collect();
410-
let field_names: std::collections::HashSet<&str> =
411-
v.fields(id).iter().map(|f| f.predicate_iri.as_str()).collect();
412419
for assoc in &assoc_names {
413420
assert!(
414421
field_names.contains(assoc),
@@ -437,10 +444,18 @@ mod tests {
437444
// green for the 0x09XX block.
438445
let v = OgarClassView::new();
439446
for concept in [
440-
"patient", "diagnosis", "lab_value", "medication", "treatment", "visit", "vital_sign",
447+
"patient",
448+
"diagnosis",
449+
"lab_value",
450+
"medication",
451+
"treatment",
452+
"visit",
453+
"vital_sign",
441454
] {
442455
let id = canonical_concept_id(concept).unwrap();
443-
let view = v.object_view(id).unwrap_or_else(|| panic!("{concept} not registered"));
456+
let view = v
457+
.object_view(id)
458+
.unwrap_or_else(|| panic!("{concept} not registered"));
444459
assert!(!view.fields.is_empty(), "{concept} has no field basis");
445460
}
446461
}
@@ -457,5 +472,4 @@ mod tests {
457472
assert_eq!(fields[10].predicate_iri, "patient");
458473
assert_eq!(fields[11].predicate_iri, "encounter");
459474
}
460-
461475
}

0 commit comments

Comments
 (0)