diff --git a/.claude/board/INTEGRATION_PLANS.md b/.claude/board/INTEGRATION_PLANS.md index 8129d0df0..dc309a4a8 100644 --- a/.claude/board/INTEGRATION_PLANS.md +++ b/.claude/board/INTEGRATION_PLANS.md @@ -1,3 +1,10 @@ +## 2026-10-11 — concept-labels-hotplug-v1 — labels of a concept, pulled through the plug → `.claude/plans/concept-labels-hotplug-v1.md` + +**Status:** ACTIVE (D-LBL-0..5). Operator DECISION 2026-10-11: labels are ontology data in `ogar-vocab`, pulled through `hotplug.rs` only for plugged concepts; every language now, a non-breaking per-consumer spec later; customer binaries activate at build time; `0x0103` German preferred "Zeiterfassung". + +- Rows `(concept u16, BCP-47 tag, role, text)`, a concept × language `LazyLock` index, `Activation::with_labels` (additive, like `with_register_laws`). +- Carries two corrections: the source-id mapping lives in the consumer's plug, not in OGAR port tables; capabilities carry no consumer list. + ## 2026-10-10 (2) — v3-mandatory-hotplug-reading-v1 — the reading comes from the plug, never from the classid value → `.claude/plans/v3-mandatory-hotplug-reading-v1.md` **Status:** ACTIVE (D-HPR-0..6). Operator DECISION 2026-10-10: V3 mandatory, hotplug.rs + OGAR plug-and-play is the only reading path, lockstep deprecated, concepts are immutable addresses and language names are labels. diff --git a/.claude/board/STATUS_BOARD.md b/.claude/board/STATUS_BOARD.md index 399ffb1cd..2abaa464f 100644 --- a/.claude/board/STATUS_BOARD.md +++ b/.claude/board/STATUS_BOARD.md @@ -1,3 +1,16 @@ +## D-LBL — labels of a concept, pulled through the plug (2026-10-11) + +Plan: `.claude/plans/concept-labels-hotplug-v1.md`. + +| D-id | Deliverable | Status | Gate | +|---|---|---|---| +| **D-LBL-0** | Decision record, board rows, corrections (mapping in the consumer plug; no executor lists) | In PR | — | +| **D-LBL-1** | `ogar-vocab` label rows + concept × language `LazyLock` index; seed `0x0103` (de pref Zeiterfassung, de alt Stundenzettel) | Queued (OGAR PR) | one preferred per (concept, tag); roles disjoint; concept in `CODEBOOK` | +| **D-LBL-2** | `Activation::with_labels` + `label()` / `alt_labels()`; `OgarAuthority::activate` fills plugged concepts only | Queued | unplugged concept's labels absent; missing language → `None` | +| **D-LBL-3** | Build-time activation for customer binaries (generated constants, labels included) | Queued | the binary links only `lance-graph-contract` | +| **D-LBL-4** | Per-consumer `LabelSpec` + default `activate_with_labels` (non-breaking) | Queued (when a volume problem is measured) | `activate()` output unchanged; spec `["de"]` yields no `en` rows | +| **D-LBL-5** | TTL reader keeps language tags (`ogar-from-schema`) | Queued | a tagged `rdfs:label` reaches the label rows with its tag | + ## D-HPR — V3 mandatory; reading from the plug only (2026-10-10) Plan: `.claude/plans/v3-mandatory-hotplug-reading-v1.md`. @@ -10,7 +23,9 @@ Plan: `.claude/plans/v3-mandatory-hotplug-reading-v1.md`. | **D-HPR-3** | Consumers move (lance-graph, q2, MedCare); `classid_read_mode` deprecated | Queued (2026-10-11 census: MedCare has 0 callers; q2's are offline bake mints; deepnsm-v2 is blocked on a concept mint, not code) | no NEW reading selected through the table (rule 5 keeps stored keys readable) | | **D-HPR-4** | `0x1000` / render prefixes documented as deprecated, kept (no eviction) | Shipped for `0x1000` (#1464; doc notes on `CLASSID_*_V3` + `classid_read_mode`); render prefixes, `NodeGuid::read_mode` and OGAR/q2 sites still carry no note | — | | **D-HPR-5** | Stored legacy keys keep resolving; a `SlabDeclaration` may override for its own data; no forced removal | Holds today (status quo). Caveat: a `SlabDeclaration` overrides `value_schema` only, never the tail, so it cannot protect a V1/V2-tail slab (`hotplug.rs:231-257, 392`) | — | -| **D-HPR-6** | No V1 default for an unknown classid; `guid-v3-tail` not a gate | Queued — split: **6a** un-gate `guid-v3-tail` (default-on, and no fleet consumer disables it, so only `--no-default-features` builds change; there the 5 V3 fossils read `DEFAULT` today); **6b** unknown classid → error (behaviour change, operator) | unknown classid → `NoReadingFor` | +| **D-HPR-6** | No V1 default for an unknown classid; `guid-v3-tail` not a gate | Queued — split: **6a** un-gate `guid-v3-tail` (default-on, and no fleet consumer disables it, so only `--no-default-features` builds change; there the 5 V3 fossils read `DEFAULT` today); **6b** unknown classid → error. Decided 2026-10-11: go, staged after 6a, D-HPR-3 and a stored-classid check | unknown classid → `NoReadingFor` | +| **D-HPR-7** | Storage readings leave the concept key: out of `concept_override`, into the slab's `SlabDeclaration` or the owning consumer's plug (O3, decided (a) 2026-10-11) | Queued | a non-Odoo plug of `0x0202` reads `PLUG_AND_PLAY_V3`; Odoo's slab still reads `ERP_V3` | +| **D-HPR-8** | A plug that asks only for a reading succeeds (reading is not a capability; decided 2026-10-11) | Queued | `[0x0103]` with `covered: &[]` → `Ok`, `PLUG_AND_PLAY_V3`; with a covered capability → refused as today | ## D-XGP — Cross-glove business parity: Odoo × SAP over one Quack algebra (2026-10-10) diff --git a/.claude/board/entries/2026-10-11-coresearch-labels-address-lifetime.md b/.claude/board/entries/2026-10-11-coresearch-labels-address-lifetime.md index eb7809fcd..fbd106f61 100644 --- a/.claude/board/entries/2026-10-11-coresearch-labels-address-lifetime.md +++ b/.claude/board/entries/2026-10-11-coresearch-labels-address-lifetime.md @@ -3,6 +3,17 @@ **Status:** OPEN — exploration map. It ratifies nothing; the operator chooses. Plan context: `v3-mandatory-hotplug-reading-v1.md` rules 3–5, O1, O2. +> **⊘ Corrected 2026-10-11 (operator decisions, plan `concept-labels-hotplug-v1.md`).** +> - Shape piece 2 ("`PortSpec` is the mapping set") and the X14 / X17 / X18 verdicts +> "ADOPT in `PortSpec` / OGAR" are superseded. Port alias tables are lockstep tables, +> deprecated and kept. The source-id → concept mapping lives in the consumer's own `HotPlug`. +> - Shape piece 3 and the "Label home" open point are decided. Labels live in `ogar-vocab`. +> A consumer pulls them through `hotplug.rs` only for the concepts it plugs (concept × +> language `LazyLock` index). Every language comes now; a non-breaking per-consumer spec +> comes later. +> - O2's label half is decided: `0x0103` has German preferred "Zeiterfassung" and German +> alternate "Stundenzettel". + ## The question, after the premise audit The draft asked where "Stundenzettel / TimeSheet / Zeiterfassung" live as diff --git a/.claude/board/entries/2026-10-11-synergy-ledger-classid-hotplug.md b/.claude/board/entries/2026-10-11-synergy-ledger-classid-hotplug.md index 27cdf1f51..955c39502 100644 --- a/.claude/board/entries/2026-10-11-synergy-ledger-classid-hotplug.md +++ b/.claude/board/entries/2026-10-11-synergy-ledger-classid-hotplug.md @@ -8,6 +8,12 @@ adversarial re-reader per scout checked every item against its cited lines: completeness critic was not run (stopped to save tokens), so the ranking below is the main thread's. +> **⊘ Corrected 2026-10-11 (operator, plan `concept-labels-hotplug-v1.md`):** capabilities on a +> concept carry no list of consumers. Each consumer's plug declares what it covers, and the +> expected-executors check in OGAR's `resolve_hotplug` is a lockstep residue to deprecate. +> The executor-list framing of `ogar-p18-capability-table-0x0103` and +> `odoo-capability-0x0103-consumer-tension` is superseded. + ## Leverage ranking (open items) **Low fruit (hours, no operator needed):** diff --git a/.claude/plans/concept-labels-hotplug-v1.md b/.claude/plans/concept-labels-hotplug-v1.md new file mode 100644 index 000000000..3582fc43e --- /dev/null +++ b/.claude/plans/concept-labels-hotplug-v1.md @@ -0,0 +1,105 @@ +# concept-labels-hotplug-v1 — labels of a concept, pulled through the plug + +**Status:** ACTIVE (D-LBL-0..5). DECISION by the operator, 2026-10-11. W0 (this record) lands first; W1 onward are code. + +## The decisions (operator, 2026-10-11, verbatim) + +> "Labels live in ogar-vocab pulled via hotplug.rs from the consumers. No need to keep labels for hundreds of consumers hot when it's separation of concerns that the plug and play pattern enables the consumers to activate the label distribution via ogar-vocab" + +> "lean (a) now, with (b) when a real volume problem shows up. if you can make multilingual no breaking plan option per consumer in the board. Consumers mint the specs and Concept x language lazylock would make it cheap" + +> Customer binaries activate at "build time". The German preferred label of `0x0103` is "Zeiterfassung". + +What it means, in rules: + +1. **Labels are ontology data in `ogar-vocab`.** OGAR is the only writer. No consumer is ever listed in the label data, so it is not a lockstep table. +2. **A consumer gets labels only for the concepts it plugs**, through `hotplug.rs`, the same way it gets readings. Nothing keeps every label hot for every consumer. +3. **Now:** an activation carries every language of each plugged concept. `HotPlug` does not change. +4. **Later (when a volume problem is measured):** a consumer selects languages through a spec it mints itself. This must not break any existing consumer (D-LBL-4). +5. **Customer binaries activate at build time.** The build emits a static activation, labels included. No OGAR link at runtime (BBB barrier). +6. **`0x0103`:** German preferred "Zeiterfassung", German alternate "Stundenzettel". + +## Shape + +**Rows (`ogar-vocab`).** One row per label: `(concept u16, BCP-47 tag, role, text)`, role ∈ {preferred, alternate, hidden}. Invariants, each a test: + +- at most one preferred row per (concept, tag); +- the three roles are disjoint per (concept, tag); +- every concept exists in `CODEBOOK`; +- an absent language has no row. A lookup never copies another language into it. + +**Index.** `static LABELS: LazyLock>` is built once from the rows: concept × language. Filling an activation costs one lookup per plugged concept and language, independent of how many labels exist in total. + +**Activation (contract, additive).** `Activation` is built only through `Activation::new(..)`. No sibling repo constructs it, and `with_register_laws(..)` already shows the additive builder pattern. Labels follow that pattern: + +- `Activation::with_labels(..)` builder; `OgarAuthority::activate` fills it for `plug.classids`; +- `Activation::label(concept, &["de-AT", "de", "en"]) -> Option<&str>` returns the preferred label; +- `Activation::alt_labels(concept, lang)` returns the alternates; +- the caller passes its own language priority. The lookup walks it and returns `None` when no requested language exists, never another language. + +**App-specific and private labels** stay in the consumer's own ClassView or overlay, never in `ogar-vocab`. A private repository's wording never reaches a public crate. + +## D-LBL-4 — per-consumer languages, non-breaking + +The consumer mints its spec beside its `HOT_PLUG`, in its own crate: + +```rust +pub const LABEL_SPEC: LabelSpec = LabelSpec { + consumer: "", // must equal HOT_PLUG.consumer + languages: &["de", "en"], // BCP-47 tags + roles: LabelRoles::PREFERRED_AND_ALTERNATE, +}; +``` + +The authority gains a **default** trait method, so every existing authority keeps compiling: + +```rust +fn activate_with_labels(&self, plug: &HotPlug, spec: &LabelSpec) + -> Result +{ + // default: activate(plug), then keep only spec.languages × spec.roles +} +``` + +- `activate(&HOT_PLUG)` keeps returning every language, so no existing caller changes behaviour. +- Filtering reads the concept × language index directly: one lookup per plugged concept and requested language. +- A tag with no rows is not an error; it yields no labels for that tag. +- `spec.consumer` must equal `plug.consumer`. +- Nothing in `ogar-vocab` lists consumers or specs. The spec lives in the consumer. + +## Build-time activation (D-LBL-3) + +- A customer binary's build step calls the authority and emits the activation as generated Rust constants: concepts, capabilities, readings and labels. +- The binary links only `lance-graph-contract`. Spine and test binaries may still activate at runtime. +- The generated constants also record which label set shipped. + +## Waves + +- [ ] **W0 — D-LBL-0: record.** This plan, `INTEGRATION_PLANS.md`, `STATUS_BOARD.md`, and the corrections below. +- [ ] **W1 — D-LBL-1: label rows + index in `ogar-vocab`** (OGAR PR). Seed `0x0103`: de preferred "Zeiterfassung", de alternate "Stundenzettel"; en preferred from the canonical name. Then the other German words the 2026-10-11 census found (`0x0202`, `0x0203`, `0x0204`, `0x0205`). Invariant tests above. +- [ ] **W2 — D-LBL-2: `Activation::with_labels` + `label()`;** `OgarAuthority::activate` fills labels for plugged concepts. +- [ ] **W3 — D-LBL-3: build-time activation** for customer binaries. +- [ ] **W4 — D-LBL-4: `LabelSpec` + default `activate_with_labels`,** when a volume problem is measured. +- [ ] **W5 — D-LBL-5: the TTL reader keeps language tags** (`ogar-from-schema` keeps only the first `rdfs:label` and drops the tag today). This feeds W1 from OGIT once OGIT carries tagged labels. + +## Falsifiers + +- **It fires:** a plug of `[0x0103]` gives `label(0x0103, &["de"]) == Some("Zeiterfassung")`, and the German alternates contain "Stundenzettel". +- **It stays silent:** + - an unplugged concept's labels (`0x0202`) are absent from that activation; + - `label(c, &["fr"])` is `None` when no French row exists, never the English one. +- **Invariants:** two preferred rows for one (concept, tag) turn the test red; a row naming a concept absent from `CODEBOOK` turns it red. +- **D-LBL-4:** a spec of `["de"]` yields no English rows; `activate()` without a spec returns exactly what it returned before the change. + +## Corrections carried by this record (operator, 2026-10-11) + +> "'WoA's port row' suggests that deprecated lockstep instead of hotplug.rs is used" + +- **The mapping lives in the consumer's plug, not in OGAR's port tables.** WoA's `HotPlug` declares `0x0103` for its `TimeSheet` (a close match, owned by WoA). `WOA_ALIASES` and the other `PortSpec` alias tables are lockstep tables: deprecated, kept. This supersedes, in `entries/2026-10-11-coresearch-labels-address-lifetime.md`, shape piece 2 ("`PortSpec` is the mapping set") and the X14 / X17 / X18 verdicts "ADOPT in `PortSpec` / OGAR". +- **Capabilities.** The concept declares its capabilities; each consumer's plug declares which it covers. No list of consumers exists. The expected-executors check in OGAR's `resolve_hotplug` (`UnexpectedConsumer`) is a lockstep residue to deprecate. This supersedes the executor-list framing of `ogar-p18-capability-table-0x0103` in `entries/2026-10-11-synergy-ledger-classid-hotplug.md`. + +## Decided with this record (operator, 2026-10-11: "Recommended accepted") + +- The three colliding u16s: storage readings leave the concept key (`v3-mandatory-hotplug-reading-v1` W7, D-HPR-7). +- A plug that asks only for a reading succeeds; reading is not a capability (W8, D-HPR-8). +- D-HPR-6b goes ahead, staged after W6a, W3 and a stored-classid check. diff --git a/.claude/plans/v3-mandatory-hotplug-reading-v1.md b/.claude/plans/v3-mandatory-hotplug-reading-v1.md index b5e40c3f8..50898b1de 100644 --- a/.claude/plans/v3-mandatory-hotplug-reading-v1.md +++ b/.claude/plans/v3-mandatory-hotplug-reading-v1.md @@ -62,18 +62,21 @@ What it means, in rules: - Holds today. **Caveat (2026-10-11):** a `SlabDeclaration` overrides `value_schema` and the register reading only, never the key tail (`hotplug.rs:231-257`, `:392`). It cannot protect a V1/V2-tail slab. q2's `osint_scene.soa` is one: V2 tails under a classid whose builtin reading is V1. It is read by its own projection and must not be re-read through the table. - [ ] **W6 — D-HPR-6: V1 cannot be reached by default.** `ReadMode::DEFAULT` stops being the fallback for an unknown classid (it becomes `NoReadingFor`, as on the plug path), and `guid-v3-tail` stops being a feature gate. - Split (2026-10-11). **W6a**: un-gate `guid-v3-tail`. It is default-on and no fleet consumer disables it, so only `--no-default-features` builds change; there the 5 V3 fossils read `DEFAULT` today (coresearch probe P-X3). **W6b**: an unknown classid errors. This is a behaviour change, and both q2 and MedCare have already hit the V1 default once, so it needs operator approval. + - **Decided (operator, 2026-10-11: "Recommended accepted"): go, staged.** 1. W6a. 2. W3 moves the remaining mint paths onto plug readings. 3. A check that every stored artifact's classid resolves. 4. Only then W6b. +- [ ] **W7 — D-HPR-7: storage readings leave the concept key** (O3, decided (a)). The five domain storage readings move out of `concept_override` (keyed by concept) into the slab's `SlabDeclaration` or the owning consumer's plug. A consumer plugging a shared concept then reads `PLUG_AND_PLAY_V3`. Falsifier: a plug of `0x0202` by a non-Odoo consumer reads `PLUG_AND_PLAY_V3`, and Odoo's own slab still reads `ERP_V3`. +- [ ] **W8 — D-HPR-8: a plug that asks only for a reading succeeds** (decided). Reading is not a capability. A plug with `covered` empty gets readings only, also for a concept that has a capability table. A plug that names a capability still needs the table. Falsifier: `[0x0103]` with `covered: &[]` → `Ok` and `read_mode_for(0x0103) == PLUG_AND_PLAY_V3`; with `covered: &["anything"]` → refused as today. ## Open questions (operator) - **O1.** What classview should a NEW mint of a canon domain use instead of `0x1000`: `0x0000` (shared canonical core), or an app-owned value? Not blocking: existing mints keep `0x1000`. - **O2.** Does WoA's `ogit.WorkOrder:TimeSheet` resolve to `0x0103`, with "Stundenzettel" as its label, instead of today's `TimesheetActivity` (plan `cross-glove-business-parity-v1.md` §C.9.5)? Rule 4 settles that the German name is a label; it does not settle which WoA row the label names. -- **O3 (2026-10-11).** Since W2, `concept_override` keys storage readings by **concept** (`0x0101` → `PROJECT_V3`, `0x0202` → `ERP_V3`, `0x0A01` → `FMA_V3`). Those three u16s are also code-minted consumer appid heads (OpenProject, Odoo, q2). So any consumer that plugs `commercial_document` (aliased by the WoA, SMB, Odoo and HubSpo ports) gets the ERP storage reading, not `PLUG_AND_PLAY_V3`. This is latent: no plug names these concepts today. Options: +- **O3 (2026-10-11).** Since W2, `concept_override` keys storage readings by **concept** (`0x0101` → `PROJECT_V3`, `0x0202` → `ERP_V3`, `0x0A01` → `FMA_V3`). Those three u16s are also code-minted consumer appid heads (OpenProject, Odoo, q2). So any consumer that plugs `commercial_document` (aliased by the WoA, SMB, Odoo and HubSpo ports) gets the ERP storage reading, not `PLUG_AND_PLAY_V3`. This is latent: no plug names these concepts today. **Decided 2026-10-11: (a)** → W7 (D-HPR-7). Options were: - (a) key storage readings by a consumer-owned value; - (b) move the heads off occupied concept slots; - (c) document that plugging these concepts means that domain's storage model. - Related: should a plug that asks only for a reading (`covered` empty) succeed for a concept that has a capability table? It would unblock the SAP/Odoo `0x0103` plugs, q2 geo and deepnsm-v2. Record: `entries/2026-10-11-coresearch-labels-address-lifetime.md` (X6), `entries/2026-10-11-synergy-ledger-classid-hotplug.md`. + Related, **decided 2026-10-11: yes** → W8 (D-HPR-8). The question was: should a plug that asks only for a reading (`covered` empty) succeed for a concept that has a capability table? It would unblock the SAP/Odoo `0x0103` plugs, q2 geo and deepnsm-v2. Record: `entries/2026-10-11-coresearch-labels-address-lifetime.md` (X6), `entries/2026-10-11-synergy-ledger-classid-hotplug.md`. ## What this plan does not do