Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions .claude/board/CONSUMER_SCAN_TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Consumer Scan TODO — cross-repo latent-issue sweep (2026-06-23)

> Two latent issue-classes surfaced this session. Both are *anticipatable*
> across every consumer, not one-offs. This is the scan checklist: run each
> check against each client, tick the box, file an issue on any hit.
>
> Status keys: `[ ]` = not scanned · `[x]` = scanned + clean · `[!]` = hit (file issue) · `[fixed]` = scanned + repaired this arc.

---

## Issue Class A — OGAR codebook-mirror drift

**Symptom:** `error[E0080]` at `lance-graph-ogar/src/lib.rs` COUNT_FUSE, or a
runtime `assert_codebook_parity` panic — fires in **every** build that vendors
the OGAR git dep when `ogar_vocab::class_ids::ALL` and a local mirror disagree
on concept count or domain.

**Root cause:** a hand-maintained copy of the codebook that is NOT bound to
`ogar_vocab` by `pub use`. The zero-dep contract mirror cannot re-export (it has
no dep on ogar-vocab by design), so it is the ONE surface that drifts.

**The rule (E-CODEBOOK-MINT-IS-A-CROSS-REPO-ARC):** an OGAR concept mint is a
cross-repo arc. DoD = OGAR `ogar-vocab` entry **+** `lance-graph-contract::ogar_codebook`
mirror rows + `ConceptDomain` variant + `canonical_concept_domain` arm **+**
`lance-graph-ogar::parity::domains_agree` arm — all in the same arc.

### Scan check
For each client: does it carry a codebook/class-id table that is a **copy**
rather than a `pub use ogar_vocab::class_ids::*` re-export? If yes → drift risk.

```
# per repo:
rg -n "ConceptDomain|canonical_concept_domain|class_ids::ALL|0x0[0-9A-F]{3}" --type rust
# a re-export ("pub use ogar_vocab::class_ids::*") is SAFE; a literal id table is the risk.
```

### Per-client status
- [fixed] **lance-graph-contract** `ogar_codebook::CODEBOOK` — the one hand-maintained mirror. Synced to 43 (added 0x0B auth family). This is the only client that can structurally drift; guard it hardest.
- [x] **openproject-nexgen-rs** `op-canon/src/class_ids.rs` — `pub use ogar_vocab::class_ids::*` re-export → cannot drift. CLEAN.
- [x] **MedCare-rs** — SWEPT 2026-06-23 (workspace-wide id-table grep): no literal `0xDDCC`/`ConceptDomain` table in any `medcare-*` crate. Vendors `lance-graph-ogar` + `ogar-vocab` (git main); the Health set is inherited via the upstream gate, not a hand-list. CLEAN.
- [x] **smb-office-rs** — SWEPT 2026-06-23: no local codebook copy; consumes via `lance-graph-contract`. CLEAN.
- [x] **woa-rs** — SWEPT 2026-06-23: no literal id table; `WoaPort` pulls classid via PortSpec. CLEAN.
- [x] **q2 / ladybug-rs / crewai-rust / n8n-rs / rs-graph-llm** — SWEPT 2026-06-23: none carries a literal OGAR id table (most don't consume the codebook at all). CLEAN.

> **Sweep result (2026-06-23):** a workspace-wide grep for literal codebook id-tables
> (`const … CODEBOOK`, `=> ConceptDomain::…`, `pub const … : u16 = 0xDD…`) returns
> exactly TWO authoritative tables: `OGAR/ogar-vocab` (the source) and
> `lance-graph-contract::ogar_codebook` (the mirror, now fused at 43). Every other
> consumer re-exports or pulls via PortSpec. **Class-A drift surface = the one mirror.**

### Guard to add (prevents recurrence)
- [ ] Any client that maintains its own concept list MUST either (a) `pub use ogar_vocab::class_ids::*`, or (b) add a `const _` COUNT_FUSE against `ogar_vocab::class_ids::ALL.len()`. Hand-lists with neither are the bug.

---

## Issue Class B — PaaS deploy crash (Railway / Heroku / Cloud Run / Fly)

**Symptom (B1 — unreachable):** container starts, binds a fixed port (e.g.
`0.0.0.0:3000`), platform routes its public edge to `$PORT` (often 8080) → the
app is up but the proxy can't reach it (the `shuttle.proxy…:45472 > :3000`
non-resolve medcare hit).

**Symptom (B2 — crash-loop):** the app writes to a CWD-relative dir
(`./audit`, `./data`, `./cache`) that is read-only on the container image →
`PermissionDenied` at boot, fail-closed crash-loop (the medcare
`MEDCARE_AUDIT_DIR` crash).

### Scan checks
```
# B1 — fixed-port bind without $PORT fallback:
rg -n "TcpListener::bind|SocketAddr|\.listen|bind\(" --type rust src
# HIT if the bind addr is a config/literal with NO `std::env::var("PORT")` branch.

# B2 — CWD-relative writable path in a sink/store/cache initializer:
rg -n '"\./|from\("\.|PathBuf::from\("[^/]' --type rust src
# HIT if a write target defaults to a relative path instead of a writable data root.
```

### Per-client status (web-app `main`/`server`/`serve` found)
- [fixed] **MedCare-rs** `medcare-server/src/main.rs` — `$PORT` bind branch added; audit dir now derives `<lance-data-root>/audit` (writable), not `./audit`. Both classes repaired.
- [ ] **woa-rs** `src/main.rs` — axum; verify `$PORT` bind + Tresor/PDF/sled write dirs use a writable data root (Stefan's Railway deploy is production).
- [ ] **openproject-nexgen-rs** `op-server/src/main.rs` — verify `$PORT` + any RLS/audit write dir.
- [ ] **q2** `crates/quarto-hub/src/server.rs` — verify `$PORT` + hub doc-store path.
- [ ] **rs-graph-llm** `insurance-claims-service` / `medical-document-service` / `recommendation-service` / `notebook` mains — verify `$PORT` + any session/Lance store path.
- [ ] **n8n-rs** `n8n-server/src/main.rs` — verify `$PORT` + sled/background-work dir.
- [ ] **crewai-rust** `src/bin/server.rs` — verify `$PORT` + any cache dir.
- [ ] **ladybug-rs** `src/bin/server.rs` — verify `$PORT` (note: uses CogRedis, separate concern).
- [ ] **spider** `spider_worker/src/main.rs` — verify `$PORT` if exposed.
- [ ] **lance-graph** `lance-graph-planner/src/serve.rs`, `cognitive-shader-driver/src/{serve.rs,bin/serve.rs}` — these are lab/serve surfaces; verify `$PORT` before any are deployed.

### Canonical pattern (copy from medcare)
```rust
// B1: bind $PORT when set (all interfaces), else fall back to config.
let addr: SocketAddr = match std::env::var("PORT") {
Ok(p) if !p.trim().is_empty() => format!("0.0.0.0:{}", p.trim()).parse()?,
_ => settings.listen.parse()?,
};
// B2: derive writable paths from the data root the platform mounts,
// never a CWD-relative "./audit" / "./data".
```

---

## How to run this sweep
1. One pass per repo with the rg checks above (5 min each).
2. Tick `[x]` clean / `[!]` hit. For each `[!]`, file an issue in that repo's
board (`ISSUES.md` / `Altlasten.md` / `braid`) pointing at this doc.
3. Class-A hits also append to OGAR `EPIPHANIES` (mint-arc rule) if a new
un-guarded mirror is discovered.
4. Re-run after the next OGAR codebook mint — Class A is recurring by nature.
89 changes: 89 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,92 @@
## 2026-06-23 — E-AUTH-CLASS-WIRED-TO-RBAC — the OGIT-imported 0x0B AuthStore family is now the membrane front-door of authorize()

**Status:** FINDING (2026-06-23). The OGIT `NTO/Auth/Configuration` entity (arago's
`auth_store`, 1:1 with OGAR `0x0B01`) is wired into `lance-graph-rbac` as the
authorization membrane (keystone §7 / I-K7: the inner `authorize()` kernel never
touches a token). `lance-graph-rbac/src/auth.rs`:

- `AuthProvider` enum = the preminted `0x0B` family (`Store`/`Zitadel`/`Zanzibar`/
`OryKeto`); each variant's classid is resolved through the **zero-dep contract
mirror** (`contract::ogar_codebook::canonical_concept_id`) — one source, no
hardcoded `0x0B0N`, no `ogar-vocab` dep (BBB-safe).
- `ClaimGrammar` per provider (subject/roles/tenant claim keys) — Zitadel's
project-roles URN, Zanzibar's user/relation/namespace tuple grammar, the
plain-OIDC base — the §7 "each profile carries its claim grammar as data".
- `AuthProvider::resolve(sub, roles, tenant) -> ResolvedIdentity` — the §7 mapping
(`sub → actor`, `role-key → roles`, `org → tenant`/scope). `ResolvedIdentity` is
the ONLY thing that crosses inward; it feeds `authorize(rbac, &id.actor, class, op)`.
- 4 tests incl. `resolved_identity_feeds_authorize` (membrane → kernel end-to-end)
and `provider_class_ids_resolve_through_the_contract_mirror` (pins the 0x0B family
to the codebook).

Token *extraction* (JWT/JSON parse via `grammar()`) stays at the consumer membrane —
no JWT/serde dep leaks into the rbac tier. The consumer maps IdP role strings → its
own role set. Cross-ref: OGAR `CLASSID-RBAC-KEYSTONE-SPEC.md` §7, E-RBAC-AUTHORIZE-PROBE-GREEN.

## 2026-06-23 — E-RBAC-AUTHORIZE-PROBE-GREEN — classid-keyed `authorize()` reproduces the shipped membrane gate bit-for-bit; keystone §5 promoted CONJECTURE→FINDING (for the in-repo reference)

**Status:** FINDING (probe green, 2026-06-23). The OGAR `CLASSID-RBAC-KEYSTONE-SPEC.md`
§10 names `PROBE-OGAR-RBAC-AUTHORIZE` as the gate that must pass before any consumer
collapses onto classid-keyed authorization: the classid-keyed kernel must reproduce a
reference system's decision **bit-for-bit**. Built against the in-repo reference (the
shipped `lance_graph_rbac::policy::Policy::evaluate` — the "reconcile the shipped
MembraneGate path with the keystone" framing of `ISS-RBAC-AUTHORIZE-BY-CLASSID`):

- `lance-graph-rbac/src/authorize.rs` — `ClassRbac` trait (§4), `authorize(rbac, actor,
class: ClassId, op)` (§5 positive ∧ op-gate kernel), `ClassGrants` (`PermissionSpec`
**re-keyed by `ClassId`**, §11). The kernel mirrors the shipped deny reasons exactly
("unknown role" / "insufficient read depth" / "predicate not writable" / "action not
allowed") so the comparison is bit-for-bit, not just allow/deny.
- `probe_ogar_rbac_authorize` — 15-tuple corpus across all three SMB roles, all three op
kinds, the allow path, every distinct deny reason, the depth boundary, and the
unknown-actor path → all equal to `Policy::evaluate`. GREEN.
- `probe_is_falsifiable_under_wrong_keying` — proves the gate is not vacuous: a wrong
classid flips an Allow, so the corpus genuinely tests the keying + kernel, not a
delegation.

**Honest fence (what is NOT yet certified):** the shipped reference is positive
role→permission only — no row-scope predicate, no field projection in the decision. So
this gate certifies the §5 *positive ∧ op-gate* half and the §11 classid re-keying.
The §5 stage-2 row-scope predicate and the projecting `Allow { scope, mask }` return
remain keystone work; the stronger references (Odoo `ir.model.access ∧ ir.rule`,
OpenFGA) exercise scope and are the follow-on probes. **Necessary, not yet sufficient**
for the full keystone — but it is the step-4 reconciliation, and it unblocks the
medcare #169 consumer-collapse (`authorize(actor, HealthcarePort::class_id("Patient"))`)
against the positive-grant half. Trait promotion to `lance-graph-contract` (§11) and the
scope-bearing references are the next deliverables.

## 2026-06-23 — E-CODEBOOK-MINT-IS-A-CROSS-REPO-ARC — an OGAR concept mint is NOT done until the lance-graph-contract mirror lands in the SAME arc

**Status:** FINDING (cross-repo cascade, 2026-06-23). Minting the `0x0B`
AuthStore family in OGAR (`ogar-vocab` PR #110, merged to OGAR `main`) added 4
concepts to `ogar_vocab::class_ids::ALL` (39 → 43). The `lance-graph-contract`
zero-dep mirror (`ogar_codebook::CODEBOOK`) was NOT updated in the same arc, so
the **compile-time `COUNT_FUSE`** in `lance-graph-ogar` (`assert!(mirror::CODEBOOK.len()
== ogar_vocab::class_ids::ALL.len())`) fired `error[E0080]` in **every** lance-graph
consumer that vendors the OGAR git dep — medcare CI went red on a `cargo build`,
not just a test. The fuse did exactly its job; the gap was process.

The lesson, promoted to a rule:

1. **The codebook has TWO authoritative homes that move in lockstep.** OGAR
`ogar-vocab` (the source) and `lance-graph-contract::ogar_codebook` (the
zero-dep wire mirror). The `COUNT_FUSE` (compile-time) + `assert_codebook_parity`
(runtime, in `lance-graph-ogar`) bind them. A mint touches BOTH or it breaks
the build of everything downstream.
2. **A mint is a cross-repo arc, not a single-repo PR.** The Definition-of-Done
for "mint concept X in OGAR" includes: (a) the OGAR `ogar-vocab` entry +
`ConceptDomain` variant + `canonical_concept_domain` arm; (b) the paired
`lance-graph-contract` mirror CODEBOOK rows + `ConceptDomain::X` variant +
`0xDD => X` arm; (c) the `lance-graph-ogar` `domains_agree` match arm (else the
runtime parity test panics); (d) the consumer coverage gates that inherit the
concept set (medcare's RLS fail-closed gate inherits the Health set this way —
a new Health concept becomes a fail-closed boot error, by design).
3. **Fix landed here:** mirror CODEBOOK +4 auth rows (auth_store 0x0B01,
auth_zitadel 0x0B02, auth_zanzibar 0x0B03, auth_ory_keto 0x0B04),
`ConceptDomain::Auth`, `0x0B => Auth`, and the `domains_agree` `(O::Auth, C::Auth)`
arm — restoring 43 == 43. Confirmed by the OGIT Configuration ⊨ auth_store
convergence (arago's Jan-2026 bridge entity). See OGAR `docs/CLASSID-RBAC-KEYSTONE-SPEC.md`
§7 + OGAR `EPIPHANIES` for the mint provenance.
## 2026-06-23 — E-DOLCE-ODOO-SILENT-SUFFIX-DRIFT — two hydrator-side suffix rules silently failed their own comments; cross-validation against `od_ontology::alignment` caught it (odoo-rs PR #15)

**Status:** FINDING (cross-validation result from a sibling repo). Two
Expand Down
4 changes: 4 additions & 0 deletions .claude/board/ISSUES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Issues Log — Open + Resolved (double-entry, append-only)

## 2026-06-23 — ISS-OGAR-AUTH-MIRROR-DRIFT — `0x0B` AuthStore mint broke the contract mirror's COUNT_FUSE in every consumer

**Status:** RESOLVED 2026-06-23 (this commit). OGAR `ogar-vocab` PR #110 minted the `0x0B` AuthStore family (4 concepts: auth_store 0x0B01, auth_zitadel 0x0B02, auth_zanzibar 0x0B03, auth_ory_keto 0x0B04) and merged to OGAR `main`, taking `ogar_vocab::class_ids::ALL` from 39 → 43. The paired `lance-graph-contract::ogar_codebook::CODEBOOK` mirror was NOT updated in the same arc, so the compile-time `COUNT_FUSE` in `lance-graph-ogar` (`assert!(mirror::CODEBOOK.len() == ogar_vocab::class_ids::ALL.len())`) fired `error[E0080]` (`vendor/lance-graph/crates/lance-graph-ogar/src/lib.rs:113`) in **every** consumer vendoring the OGAR git dep — medcare CI went red on `cargo build`. **Resolution:** added the 4 auth rows + `ConceptDomain::Auth` + `0x0B => Auth` to the mirror, and the `(O::Auth, C::Auth)` arm to `lance-graph-ogar::parity::domains_agree` (else the runtime `assert_codebook_parity` test panics). 43 == 43 restored; `cargo test -p lance-graph-contract` green. **Process fix (see EPIPHANIES E-CODEBOOK-MINT-IS-A-CROSS-REPO-ARC):** an OGAR concept mint is a cross-repo arc — the OGAR entry + the contract mirror + the `domains_agree` arm land together, never split across sessions. **Merge note (2026-06-23):** main landed #595 (auth sync) + #597 (PRODUCT + ACCOUNTING_ACCOUNT, OGAR #111) first; on merge this branch took main's superset `ogar_codebook.rs` (45 concepts incl. the `AppPrefix` render layer), so the auth mirror rows here are subsumed — the `domains_agree` Auth arm + this finding stand.

## 2026-06-22 — ISS-CONTRACT-APP-PREFIX-MIRROR — `contract::ogar_codebook` lacks the OGAR#97 `APP_PREFIX` / `render_classid_for` mirror, so membrane consumers must hand-stamp the hi-u16 render prefix

**Status:** RESOLVED 2026-06-22 (`claude/contract-app-prefix-mirror`) · Owner: lance-graph-contract · Surfaced by: `.claude/knowledge/ogar-consumer-preflight.md` (the consumer spellbook).
Expand Down
Loading
Loading