Skip to content

Commit 7e62a2b

Browse files
committed
docs(.claude/SMB): SMB tenant schema reference for cross-session visibility
lance-graph is the spine; sessions working here don't load smb-office-rs source. Without a local reference, "is this lance-graph change safe for SMB" requires cross-repo zipball + grep every time. These two docs make the SMB shape readable in two pages. Contents: - README.md — when to read, why per-tenant folder, how to extend - SCHEMA.md — the actual reference: - §1: two ontology surfaces (BSON × 14 German; Foundry × 3 English) - §2: Foundry-shape contract that OntologyDto::project(&registry, "SMB", ...) must return (mirrors body PR #355 deleted from upstream) - §3: BSON-shape per-entity required predicates + marking dominance - §4: projection rules Foundry ← BSON, with two flagged gaps (smb.steuer needs kunde_kdnr + eingereicht_am) - §5: lance-graph contracts that affect SMB (which signatures, how) - §6: consumer-side stopgaps that retire when registry hydration lands Pairs with smb-office-rs 611c147 (originating-side OGIT_TTL_INVENTORY) and c204819 (consumer-side stopgap for lance-graph PR #355 break). Per-tenant folder convention; sibling .claude/<tenant>/ folders can follow the same shape (MedCare next).
1 parent da156eb commit 7e62a2b

2 files changed

Lines changed: 186 additions & 0 deletions

File tree

.claude/SMB/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# .claude/SMB/ — SMB tenant schema reference
2+
3+
> **Read this when:** your task in lance-graph touches
4+
> `lance-graph-ontology`, `lance-graph-callcenter::ontology_dto`,
5+
> `lance-graph-callcenter::transcode`, `lance-graph-rbac::policy`,
6+
> the `OntologyRegistry` hydration path, or the registry → DTO
7+
> projection contract — and you need to know what a real tenant
8+
> consumer is shaped like.
9+
10+
## Files
11+
12+
| File | Read when |
13+
|---|---|
14+
| `SCHEMA.md` | You're touching a contract that affects SMB consumers. Lists the 14-entity BSON shape, the 3-entity Foundry shape, and the projection rules between them. |
15+
16+
## Why a per-tenant folder
17+
18+
The tenant consumer (`AdaWorldAPI/smb-office-rs`) is a separate repo;
19+
this lance-graph workspace doesn't load its source. Without these
20+
reference docs, "is this lance-graph change safe for SMB" requires
21+
cross-repo zipball + grep on every session. With them, the shape is
22+
two pages and immediately readable.
23+
24+
This is reference material, not policy. Authoritative source is
25+
smb-office-rs.
26+
27+
## Adding tenants
28+
29+
If a second tenant lands (MedCare, Stalwart, SAP, future Foundry
30+
consumer), add a sibling folder under `.claude/<tenant>/` with the
31+
same structure. Keep these per-tenant; do not mix shapes.

.claude/SMB/SCHEMA.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# SMB tenant — schema reference for lance-graph sessions
2+
3+
> **Why this folder exists:** lance-graph is the spine; sessions
4+
> working here usually don't have SMB context loaded. When changes to
5+
> `lance-graph-ontology` / `lance-graph-callcenter` / `lance-graph-rbac`
6+
> affect tenant consumers, this folder shows what the SMB tenant
7+
> (`AdaWorldAPI/smb-office-rs`) will hydrate into the registry — so
8+
> upstream changes can be evaluated against a real consumer shape
9+
> without cross-repo grepping.
10+
>
11+
> **Authority:** the source of truth is smb-office-rs:
12+
> - BSON-shape: `crates/smb-ontology/src/{customer,rechnung,mahnung,
13+
> schuldner,woa_artikel,remaining}.rs`
14+
> - Foundry-shape stopgap: `crates/smb-realtime/src/ontology.rs::
15+
> build_smb_ontology()`
16+
> - Originating inventory: `smb-office-rs/.claude/board/OGIT_TTL_INVENTORY.md`
17+
>
18+
> When the two diverge, smb-office-rs wins; update this doc.
19+
20+
---
21+
22+
## 1. Two ontology surfaces (by design)
23+
24+
| Surface | Naming | Entity count | Purpose | Where it lives |
25+
|---|---|---|---|---|
26+
| **BSON-shape** | German wire names, `smb.<entity>` prefix | 14 | Storage layer; matches C# `db_*.cs` verbatim per smb-office-rs CLAUDE.md iron rule 1 | `smb-ontology` crate |
27+
| **Foundry-shape** | English names, no prefix | 3 | API/membrane layer; what RBAC + RLS + Foundry Object Explorer surface externally | `smb-realtime::ontology` (consumer-side stopgap until OGIT TTL hydrates) |
28+
29+
Foundry shape is a **projection** of BSON shape. One Foundry `Customer`
30+
projects from one `smb.customer` row. Translation lives in
31+
`smb_bridge::orchestration` (entity_type → table_name).
32+
33+
---
34+
35+
## 2. Foundry-shape — what `OntologyDto::project(&registry, "SMB", …)` must return
36+
37+
Mirrors the body that lance-graph PR #355 deleted from
38+
`crates/lance-graph-callcenter/src/ontology_dto.rs::smb_ontology()`.
39+
The consumer-side stopgap at `smb-realtime/src/ontology.rs::
40+
build_smb_ontology()` reproduces it verbatim.
41+
42+
### Entities (3)
43+
44+
| Public name | OGIT URI | Required | Optional (Passthrough) | Searchable (CamPq) | Free |
45+
|---|---|---|---|---|---|
46+
| `Customer` | `ogit.SMB:Customer` | `customer_name`, `tax_id` | `address`, `iban` | `industry` | `note` |
47+
| `Invoice` | `ogit.SMB:Invoice` | `invoice_number`, `date`, `total_amount`, `currency`, `customer_ref` | `due_date` || `note` |
48+
| `TaxDeclaration` | `ogit.SMB:TaxDeclaration` | `declaration_id`, `tax_year`, `customer_ref`, `declaration_type` | `filing_date`, `status` |||
49+
50+
### Links (`one_to_many`, 2)
51+
52+
- `Customer``issued``Invoice`
53+
- `Customer``filed``TaxDeclaration`
54+
55+
### Actions (3)
56+
57+
- `approve` (manual) on `Invoice` → predicate `status`
58+
- `classify` (auto) on `Customer` → predicate `industry`
59+
- `submit` (manual) on `TaxDeclaration` → predicate `status`
60+
61+
### Label (bilingual)
62+
63+
- `de``Steuerberatungskanzlei`
64+
- `en``Tax Practice`
65+
66+
---
67+
68+
## 3. BSON-shape — 14 entities under `smb.*`
69+
70+
German wire names match C# `db_*.cs` verbatim. Per-property markings
71+
carry GDPR / financial classification.
72+
73+
| Schema name | Required predicates | Marking dominance | Source |
74+
|---|---|---|---|
75+
| `smb.customer` | `kdnr`, `firma` | Pii on personal, Financial on bank | `customer.rs` |
76+
| `smb.schuldner` | `schuldner_id`, `debtor_name`, `saldo` | Internal/Pii/Financial mix | `schuldner.rs` |
77+
| `smb.rechnung` | `rechnungsnr`, `kunde_kdnr`, `rechnungsdatum`, `faellig_am`, `betrag_netto`, `betrag_brutto`, `ust_betrag`, `ust_satz`, `zahlungsstatus` | Financial on amounts | `rechnung.rs` |
78+
| `smb.mahnung` | `mahnung_id`, `schuldner_id`, `rechnungsnr`, `stufe`, `ausgestellt_am`, `faellig_am`, `gesamtsumme`, `status` | Internal | `mahnung.rs` |
79+
| `smb.dokument` | `dokument_id`, `parent_entity`, `parent_id`, `dateiname`, `mime_type`, `hochgeladen_am` | Internal | `remaining.rs` |
80+
| `smb.bank` | `buchungssatz_id`, `kontonummer`, `buchungsdatum`, `valutadatum`, `betrag`, `waehrung` | Financial | `remaining.rs` |
81+
| `smb.fibu` | `buchung_id`, `buchungsdatum`, `soll_konto`, `haben_konto`, `betrag` | Financial | `remaining.rs` |
82+
| `smb.steuer` | `steuer_id`, `steuerart`, `zeitraum_von`, `zeitraum_bis`, `bemessungsgrundlage`, `steuerbetrag`, `status` | Financial | `remaining.rs` |
83+
| `smb.lieferant` | `lieferant_id`, `firma` | Internal | `remaining.rs` |
84+
| `smb.mitarbeiter` | `mitarbeiter_id`, `vorname`, `nachname` | Pii on personal, Financial on iban | `remaining.rs` |
85+
| `smb.auftrag` | `auftrag_id`, `kunde_kdnr`, `eingegangen_am`, `auftragssumme`, `status` | Internal | `remaining.rs` |
86+
| `smb.angebot` | `angebot_id`, `kunde_kdnr`, `erstellt_am`, `angebotssumme`, `status` | Internal | `remaining.rs` |
87+
| `smb.zahlung` | (see `remaining.rs:503+`) | Financial | `remaining.rs` |
88+
| `smb.artikel` | `beschreibung` | Internal | `woa_artikel.rs` (WoA tenant) |
89+
90+
All entities also carry `fingerprint` (`CodecRoute::CamPq`) and audit
91+
fields (`ad_create`, `ad_modify`, `ad_delete`).
92+
93+
---
94+
95+
## 4. Projection rules — Foundry shape ← BSON shape
96+
97+
| Foundry property | BSON source |
98+
|---|---|
99+
| `Customer.customer_name` | `smb.customer.firma` |
100+
| `Customer.tax_id` | `smb.customer.umsatzsteuer_id` (preferred) or `steuernummer` |
101+
| `Customer.address` | `smb.customer.anschrift` (+ `plz` + `ort` + `land` joined) |
102+
| `Customer.iban` | `smb.customer.iban` |
103+
| `Customer.industry` | `smb.customer.branche` |
104+
| `Customer.note` | `smb.customer.notiz` |
105+
| `Invoice.invoice_number` | `smb.rechnung.rechnungsnr` |
106+
| `Invoice.date` | `smb.rechnung.rechnungsdatum` |
107+
| `Invoice.total_amount` | `smb.rechnung.betrag_brutto` |
108+
| `Invoice.currency` | `smb.rechnung.waehrung` |
109+
| `Invoice.customer_ref` | `smb.rechnung.kunde_kdnr` |
110+
| `Invoice.due_date` | `smb.rechnung.faellig_am` |
111+
| `Invoice.note` | `smb.rechnung.notiz` |
112+
| `TaxDeclaration.declaration_id` | `smb.steuer.steuer_id` |
113+
| `TaxDeclaration.tax_year` | derived from `smb.steuer.zeitraum_von` (year part) |
114+
| `TaxDeclaration.customer_ref` | **gap**`smb.steuer` lacks a customer column; planned: add `kunde_kdnr` |
115+
| `TaxDeclaration.declaration_type` | `smb.steuer.steuerart` |
116+
| `TaxDeclaration.filing_date` | **gap**`smb.steuer` lacks a filing date column; planned: add `eingereicht_am` |
117+
| `TaxDeclaration.status` | `smb.steuer.status` |
118+
119+
Both gaps are smb-ontology-side fixes, independent of OGIT TTL.
120+
121+
---
122+
123+
## 5. What lance-graph changes affect SMB
124+
125+
| Lance-graph change | Affects SMB how |
126+
|---|---|
127+
| `OntologyDto::project(&registry, namespace, …)` signature | `smb-realtime::dto::smb_dto(locale)` calls it for `namespace = "SMB"`. Changes to the projection contract break the consumer. |
128+
| `MappingRow` columns (e.g. PR #355 `IdentityCodec`, `QualiaMeta`) | Computed at hydration; SMB doesn't author them but consumes the projected `OntologyDto` fields. |
129+
| `SchemaExpander` trait | smb-realtime's SPO bridge (`expand_smb_entity`) uses `Ontology::expand_entity`. If SchemaExpander moves to `MappingRow`, smb-realtime adapts. |
130+
| `CachedOntology::new(Ontology)` legacy path | Currently retained for smb-realtime's `smb_cached_ontology()`. If deprecated, smb-realtime needs `CachedOntology::from_registry(&OntologyRegistry, namespace)`. |
131+
| `Policy::evaluate(role, entity, Operation)` | smb-realtime's `SmbMembraneGate` (PR #29) wraps this. Entity keys passed are the Foundry-shape names (`Customer`, `Invoice`, `TaxDeclaration`). |
132+
| `Locale` enum additions | smb-realtime's DTO cache projects every `Locale` eagerly; growing the enum forces cache init updates. |
133+
| TTL hydrator (`hydrate_once_sync`) input format | Decides what OGIT TTL must carry for SMB (see Section 2 for the shape). |
134+
135+
---
136+
137+
## 6. Stale on the consumer side (will retire when registry hydration lands)
138+
139+
When `OntologyRegistry::hydrate_once_sync(ttl_root)` populates the
140+
`"SMB"` namespace from real OGIT/NTO/SMB TTL:
141+
142+
- `smb-realtime/src/ontology.rs::build_smb_ontology()` — body deletes,
143+
becomes a thin `OntologyDto::project(&registry, "SMB", …)` facade.
144+
- `smb-realtime/src/dto.rs::cached_smb_ontology() -> &'static Ontology`
145+
— retired when `SchemaExpander` learns to project from `MappingRow`.
146+
- `smb-realtime/src/transcode.rs::smb_cached_ontology()` — retired when
147+
`CachedOntology::from_registry` ships.
148+
- Dual cache path in `smb-realtime/src/spo.rs` (`#[cfg(feature = "postgrest")]`)
149+
— collapses to single path when `dto` becomes unconditional.
150+
151+
---
152+
153+
**Last updated:** 2026-05-12, pairs with smb-office-rs commit `611c147`.
154+
Update when SMB schema changes in smb-ontology or when lance-graph's
155+
registry projection contract shifts.

0 commit comments

Comments
 (0)