toga-ai 1.0.747 → 1.0.748

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@
11
11
  | [Primary Table templates (server/client, sizing, virtualization)](features/primary-table-templates.md) | `src/templates/PrimaryTable/` is the **production, wired-up table** built on the [Table component](table.md). | toga-blox/src/components/Table/themeConfig/toga.module.css, toga25-supply/src/themeConfig.json, toga-blox/src/templates/PrimaryTable/PrimaryTable.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableServerTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableClientTemplate.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableHeaderCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableBodyCell.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableRow.tsx, toga-blox/src/templates/PrimaryTable/PrimaryTableExpandableRow.tsx, toga-blox/src/templates/PrimaryTable/types.ts |
12
12
  | [TableRecordModal (record-detail modal shell)](features/table-record-modal.md) | `TableRecordModal` is a **generic, presentational modal shell** for showing a single table record (row) in detail — typically opened from a table row click. | toga-blox/src/components/TableRecordModal/TableRecordModal.tsx, toga-blox/src/components/TableRecordModal/index.ts, toga-blox/src/components/TableRecordModal/tableRecordModal.module.css |
13
13
  | [Table component (cells, action cells, filters & sorts, hooks, theming)](features/table.md) | The `Table` component (`src/components/Table/`) is the **TanStack Table v8** building block behind the [Primary Table templates](primary-table-templates.md). | toga-blox/src/components/Table/hooks/useFetchTablePageMeta.ts, toga-blox/src/api/types.ts, toga-blox/src/components/Table/index.ts, toga-blox/src/components/Table/types.ts, toga-blox/src/components/Table/utils/buildTanstackColumns.tsx, toga-blox/src/components/Table/utils/resolveCellType.tsx, toga-blox/src/components/Table/components/cellTypes, toga-blox/src/components/Table/components/actionCells, toga-blox/src/components/Table/components/columnFiltersAndSorts, toga-blox/src/components/Table/hooks, toga-blox/src/components/Table/themeConfig |
14
- | [Talos AI-Assistant Component (launcher + docked/drawer chat panel)](features/talos-assistant.md) | `Talos` is a **shared, pure-UI** AI-assistant component in `@agilant/toga-blox`: a header launcher button (`TalosLauncher`) plus a chat panel (`TalosPanel`) tha | toga-blox/src/components/Talos/TalosLauncher.tsx, toga-blox/src/components/Talos/TalosPanel.tsx, toga-blox/src/components/Talos/TalosMessage.tsx, toga-blox/src/components/Talos/types.ts, toga-blox/src/components/Talos/theme.ts, toga-blox/src/components/Talos/helpers.tsx, toga-blox/src/components/Talos/stub.ts, toga-blox/src/components/Talos/Talos.module.css, toga-blox/src/components/Talos/index.ts, toga-blox/src/components/Talos/TalosPanel.test.tsx, toga-blox/src/components/index.ts, toga-blox/src/components/Talos/README.md |
14
+ | [Talos AI-Assistant Component (launcher + docked/drawer chat panel)](features/talos-assistant.md) | `Talos` is a **shared, pure-UI** AI-assistant component in `@agilant/toga-blox`: a header launcher button (`TalosLauncher`) plus a chat panel (`TalosPanel`) tha | toga-blox/src/components/Talos/TalosLauncher.tsx, toga-blox/src/components/Talos/TalosPanel.tsx, toga-blox/src/components/Talos/TalosMessage.tsx, toga-blox/src/components/Talos/types.ts, toga-blox/src/components/Talos/theme.ts, toga-blox/src/components/Talos/helpers.tsx, toga-blox/src/components/Talos/stub.ts, toga-blox/src/components/Talos/Talos.module.css, toga-blox/src/components/Talos/index.ts, toga-blox/src/components/Talos/TalosPanel.test.tsx, toga-blox/src/components/index.ts, toga-blox/src/components/Talos/README.md, toga-blox/src/components/TableRecordModal/TableRecordModal.tsx |
15
15
  | [Toaster — host-token styling contract](features/toaster.md) | The blox `Toaster` is a CSS-Module component whose every visual property reads a `--toaster-*` custom property supplied by the **host app**. | toga-blox/src/components/Toaster/Toaster.module.css, toga-blox/src/components/Toaster/Toaster.tsx, toga-blox/src/components/Toaster/ToasterContext.tsx, toga2-commerce/src/styles/index.css, toga2-commerce/src/components/Toasters/ToasterList.tsx |
16
16
  | [Dynamic npm Publish Pipeline (branch → channel)](workflows/dynamic-publish-pipeline.md) | How `@agilant/toga-blox` (checkout folder `toga-blox-npm`, registry repo key `toga-blox`) publishes a per-environment npm **channel** (dist-tag) from a `_<mode> | toga-blox/.github/workflows/publish.yml, toga-blox/package.json, toga-blox/src/utils/getFontAwesomeIcon.tsx |
17
17
  | [Landing a Large Long-Lived Feature Branch (merge, never rebase)](workflows/landing-a-large-feature-branch.md) | The procedure for landing a long-lived, very large feature branch (months old, hundreds of commits) into `_production` in `toga-blox-npm`. | toga-blox/package.json, toga-blox/.github/workflows/publish.yml |
@@ -6,7 +6,7 @@ project: TOGa Blox
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-23
9
+ updated: 2026-09-02
10
10
  owners: [apeterson]
11
11
  files:
12
12
  - toga-blox/src/components/TableRecordModal/TableRecordModal.tsx
@@ -15,6 +15,8 @@ files:
15
15
  related:
16
16
  - ../architecture.md
17
17
  - table.md
18
+ - talos-assistant.md
19
+ - ../../toga25-supply/features/talos-integration.md
18
20
  ---
19
21
 
20
22
  ## Summary
@@ -58,6 +60,29 @@ it opens the modal with `isOpen={!!selectedRow}` and renders the record inside `
58
60
  (often using the same `tableViewFields` metadata to format fields). `utils/resolveModalType.tsx`
59
61
  and `types.ts` are present but **empty/reserved** for a future field-type→renderer mapping.
60
62
 
63
+ ## `data-blox-modal-open` — the root-element marker (2026-09-02)
64
+
65
+ While open, `TableRecordModal` sets **`data-blox-modal-open` on `document.documentElement`**. Other
66
+ blox CSS reacts to it — today the Talos launcher dims under `:root[data-blox-modal-open]` (see
67
+ [Talos assistant](talos-assistant.md)).
68
+
69
+ **Why an attribute on the root element and not a prop or context.** The things that must react to
70
+ "a modal is open" render **outside this component's subtree** — the Talos launcher lives in the app
71
+ **header**. No shared React state reaches it. Threading a prop down would have touched **every
72
+ modal layout in the consuming app**. This is the same boundary-crossing trick as the existing
73
+ `--talos-dock-width` custom property.
74
+
75
+ **It is a COUNTER, not a boolean.** Modals stack — a record modal can open a create modal — and the
76
+ marker must **survive the inner one closing**. So:
77
+
78
+ - Each instance holds a **`holdsModalMarker` ref**, so a re-render cannot double-count and a close
79
+ cannot decrement twice.
80
+ - The marker is cleared at **three** points: the open→false transition's **animation end**, an
81
+ **unmount** safety net, and the **count reaching 0**.
82
+
83
+ Anything else that reads the marker gets stacking correctness for free. (Note this fixes stacking
84
+ for the *marker* only — the body-overflow gotcha below is unchanged.)
85
+
61
86
  ## Gotchas
62
87
 
63
88
  - **No portal** — renders inline in the React tree; a parent `overflow: hidden` can clip it.
@@ -66,4 +91,12 @@ and `types.ts` are present but **empty/reserved** for a future field-type→rend
66
91
  - CSS-variable defaults are hardcoded fallbacks; set vars on the DOM before mount to change sizes.
67
92
 
68
93
  ## Change history
94
+ - 2026-09-02 — Added **`data-blox-modal-open` on `document.documentElement`** while a modal is open,
95
+ so chrome rendered **outside** the modal subtree can react (first consumer: the Talos launcher dims
96
+ under `:root[data-blox-modal-open]`). Chosen over a prop/context because the launcher lives in the
97
+ app **header** — no shared React state reaches it, and threading a prop would touch every modal
98
+ layout in the host. It is a **counter, not a boolean**, because modals stack and the marker must
99
+ survive the inner one closing; each instance holds a `holdsModalMarker` ref so a re-render cannot
100
+ double-count and a close cannot decrement twice, and the marker clears at three points (open→false
101
+ animation end, unmount safety net, count reaching 0). (apeterson)
69
102
  - 2026-06-23 — Documented TableRecordModal as a generic modal shell: positions, render-prop children, body-scroll lock, and consumer-owned field rendering / a11y (apeterson).
@@ -21,7 +21,9 @@ files:
21
21
  - toga-blox/src/components/Talos/TalosPanel.test.tsx
22
22
  - toga-blox/src/components/index.ts
23
23
  - toga-blox/src/components/Talos/README.md
24
+ - toga-blox/src/components/TableRecordModal/TableRecordModal.tsx
24
25
  related:
26
+ - table-record-modal.md
25
27
  - ../architecture.md
26
28
  - ../workflows/dynamic-publish-pipeline.md
27
29
  - ../workflows/local-link-into-a-consumer-app.md
@@ -254,9 +256,42 @@ threadsStatus?: "loading" | "error" | "ready";
254
256
  `DEFAULT_SUGGESTIONS` — four hardcoded **TOGa Supply** questions — was **removed**. Tenant
255
257
  business copy must not sit inside a library five apps consume: any host that mounted the panel
256
258
  silently inherited Supply's prompts. `suggestions` now defaults to an **empty module-level
257
- constant**, and the host supplies the chips (supply reads them from the surface layer — see
259
+ constant**, and the host supplies the chips (supply reads them from the surface layer as **one
260
+ authored, ordered set per tenant** — see
258
261
  [Talos integration](../../toga25-supply/features/talos-integration.md)).
259
262
 
263
+ ### The launcher is DISABLED under an open modal — and the cause was NOT z-index (2026-09-02)
264
+
265
+ The launcher stayed bright and looked clickable while a record modal was open. The obvious diagnosis
266
+ (and the developer's first guess) was z-index. **It was wrong.** Checked, not assumed:
267
+
268
+ - `.launcher` has **no `z-index`** at all.
269
+ - blox `.header` has **no `z-index` and no `position`**; supply's `.headerActions` has neither.
270
+ - The modal `.backdrop` is `position: fixed; z-index: 40`.
271
+
272
+ So the launcher was **already painted under the backdrop and its clicks were already being
273
+ swallowed by it.** The real cause is purely visual: `.backdrop-right` / `.backdrop-left` is only
274
+ `rgba(0, 0, 0, 0.2)`, and a 20% black wash over a **white** button still reads as active next to
275
+ dimmed header chrome.
276
+
277
+ **Fix — a real disabled state, not a z-index change.** `TableRecordModal` sets
278
+ **`data-blox-modal-open` on `document.documentElement`**, and `Talos.module.css` dims the launcher
279
+ under `:root[data-blox-modal-open]`: `opacity: .5`, `filter: grayscale(1)`,
280
+ `pointer-events: none`, plus **stopping the `.launcher::before` orbit animation** (a spinning ring
281
+ on a greyed-out button still reads as live). Marker mechanics and why it is a **counter**:
282
+ [TableRecordModal](table-record-modal.md).
283
+
284
+ This applies to **every** `TableRecordModal` in a host app automatically.
285
+
286
+ 🚨 **Known gap, left unfixed.** The launcher is still **keyboard-reachable** behind a modal —
287
+ `pointer-events: none` stops the **mouse only**. Supply's header takes `inert` for the side nav but
288
+ not for modals, and closing that is a **host** change affecting all header controls, so it was
289
+ flagged to the developer rather than patched here.
290
+
291
+ **Reusable lesson:** before reaching for z-index, read the actual computed stack. "Looks clickable"
292
+ and "is clickable" are different bugs, and a translucent scrim is a *contrast* problem, not a
293
+ *stacking* one.
294
+
260
295
  ### Tests (first ever for this component)
261
296
 
262
297
  `TalosPanel.test.tsx` — **31 Vitest + RTL Talos tests** as of 2026-09-02 (8 at inception), all
@@ -324,6 +359,20 @@ Rollup *"failed to resolve import"* for a component they never referenced). blox
324
359
  currently consumes it via a local symlink.
325
360
 
326
361
  ## Change history
362
+ - 2026-09-02 — **FIXED the launcher staying bright and clickable-looking under an open record
363
+ modal — and the cause was NOT z-index.** `.launcher` has no `z-index`, blox `.header` has no
364
+ `z-index`/`position`, and the modal `.backdrop` is `fixed; z-index: 40`, so the launcher was
365
+ **already** behind the backdrop with its clicks **already** swallowed. The real cause was contrast:
366
+ `.backdrop-right`/`-left` is only `rgba(0,0,0,0.2)` and a 20% wash over a white button still reads
367
+ as active. Fix is a real disabled state — `TableRecordModal` sets **`data-blox-modal-open` on
368
+ `document.documentElement`** (a **counter**, since modals stack) and `Talos.module.css` applies
369
+ `opacity: .5` / `grayscale(1)` / `pointer-events: none` and **stops the `.launcher::before` orbit
370
+ animation** under `:root[data-blox-modal-open]`. A root attribute rather than a prop because the
371
+ launcher renders in the host **header**, outside the modal subtree — the same trick as
372
+ `--talos-dock-width`. Applies to every `TableRecordModal` automatically. **Known gap flagged, not
373
+ fixed:** the launcher is still **keyboard**-reachable behind a modal; `pointer-events: none` stops
374
+ the mouse only, and adding `inert` is a host-header change. Suite at **31 Talos + TableRecordModal
375
+ tests**; blox `dist/` rebuilt. (apeterson)
327
376
  - 2026-09-02 — **FIXED the `renderMessages` slot silently killing the in-flight loading UI**: the
328
377
  bubble is synthesized from `isStreaming`/`progress` into the panel's internal `msgs` array, and the
329
378
  host-render branch replaced that whole array, so a run showed **no feedback between submit and
@@ -5,7 +5,7 @@
5
5
  | [NYCHH inventory-adjustment → item-fulfillment linking (direct FK columns, REST)](features/netsuite-inventory-adjustment-fulfillment-link.md) | 1.0 | For NYCHH stock adjustments, the NetSuite **inventory adjustment** carries a link to the **Item Fulfillment** it corrects, on the multi-select body custom field | library/app/api/toga2.php, library/app/api/netsuite/rest.php, worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/crons/toga2/netsuite/sync_togasupply_hh.php, worker/crons/toga2/netsuite/diagnose_hh_import_inventory_adjustment.php |
6
6
  | [NYCHH NetSuite → TransferOrders import (stocking-flag routing, PO bridges, origin location)](features/netsuite-transfer-order-import.md) | 1.0 | NYCHH's NetSuite **transfer orders are represented as NetSuite sales orders** (same as the GroWrk pattern), so the shared NetSuite → TOGa Supply importer (`App_ | library/app/api/toga2.php, library/app/api/netsuite/rest.php, library/app/netsuite.php, worker/crons/toga2/netsuite/sync_togasupply_hh.php, worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/crons/toga2/netsuite/diagnose_hh_stuck_transfer_order.php, dbchanges2/Core/2026-08-27a - TransferOrderUuidSearchableIdentifier.sql, dbchanges2/Client/2026-08-28a - TransferOrderItemsTimestampColumns.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestampColumnsReposition.sql, dbchanges2/Client/2026-08-29 - ItemReceiptTransferOrderIdFieldPermission.sql, dbchanges2/Client/2026-08-29b - ItemReceiptItemTransferOrderItemIdFieldPermission.sql |
7
7
  | [NYCHH PO links are UPSTREAM — the downstream SO→PO route returns empty](features/po-number-upstream-direction.md) | 2.0 | NYCHH's sales orders are created **from the customer's purchase order**, so their SO↔PO links live in the **upstream** table `PurchaseOrders_SalesOrders` (route | _underscore/Model/Client/SalesOrder.php, _underscore/Trait/Netsuite/SalesOrder.php, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/hooks/usePurchaseOrderDetails.ts, dbchanges2/Client/2026-08-11b - SalesOrderPurchaseOrdersField.sql, dbchanges2/Client/2026-08-25 - SalesOrderPurchaseOrdersFieldAllClients.sql |
8
- | [NYCHH Talos AI Assistant Enablement (the only granted tenant)](features/talos-assistant-enablement.md) | 2.0 | **NYCHH is the only tenant granted the Talos AI assistant in TOGa 2.5 Supply.** The feature is seeded **OFF in Core for every client** and turned on per tenant | dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, toga25-supply/src/hooks/useTalosSurface.ts, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/db-migrations/PLAYBOOK.md |
8
+ | [NYCHH Talos AI Assistant Enablement (the only granted tenant)](features/talos-assistant-enablement.md) | 2.0 | **NYCHH is the only tenant granted the Talos AI assistant in TOGa 2.5 Supply, and it AUTHORS ITS OWN suggestion questions.** The feature is seeded **OFF in Core | dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, toga25-supply/src/hooks/useTalosSurface.ts, toga25-supply/src/hooks/useTalosSurface.test.ts, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/db-migrations/PLAYBOOK.md |
9
9
  | [NYCHH transfer-order 2.0 model — inventory quantities + V2 field enablement](features/transfer-order-inventory-quantities.md) | 2.0 | The 2.0 (`_underscore`) side of NYCHH transfer-order support: a **two-branch inventory quantity model** on the NYCHH `Item` override, plus the **client-override | _underscore/Model/Nychh/Item.php, _underscore/Model/Nychh/PurchaseOrderItem.php, _underscore/Model/Nychh/TransferOrder.php, _underscore/Model/Nychh/TransferOrderStage.php, _underscore/Model/Client/TransferOrder.php, _underscore/Model/Client/TransferOrderItem.php, dbchanges2/Client_Nychh/2026-08-27a - TransferOrderCustomFieldWritePermission.sql, dbchanges2/Client_Nychh/2026-08-27b - TransferOrderStageStatusIdentifier.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, _underscore/Model/Client/PurchaseOrderItem.php |
10
10
  | [NYCHH TransferOrder → NetSuite push (location roles, interceptor row 312, metadata state)](features/transfer-order-netsuite-push.md) | 2.0 | NYCHH is the **first and only** client on the outbound transfer-order push. | _underscore/Model/Client/TransferOrder.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql |
11
11
  | [NYC Health & Hospitals](profile.md) | 2.0 | NYC Health & Hospitals (NYCHH) is a TOGA 2.0 client on the `_underscore` platform, prod schema `Client_Nychh`. | dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql, dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql, dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql |
@@ -12,6 +12,7 @@ files:
12
12
  - dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql
13
13
  - dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql
14
14
  - toga25-supply/src/hooks/useTalosSurface.ts
15
+ - toga25-supply/src/hooks/useTalosSurface.test.ts
15
16
  - toga25-supply/src/providers/TalosStreamProvider.tsx
16
17
  - toga25-supply/db-migrations/PLAYBOOK.md
17
18
  related:
@@ -24,21 +25,26 @@ related:
24
25
 
25
26
  ## Summary
26
27
 
27
- **NYCHH is the only tenant granted the Talos AI assistant in TOGa 2.5 Supply.** The feature is
28
- seeded **OFF in Core for every client** and turned on per tenant with `SurfaceOverrides` rows on
29
- the `talos-assistant` surface. Developer instruction, verbatim: *"in core all clients should have
30
- it toggled off. and then we will toggle it on specifically for nychh in supply."*
28
+ **NYCHH is the only tenant granted the Talos AI assistant in TOGa 2.5 Supply, and it AUTHORS ITS
29
+ OWN suggestion questions.** The feature is seeded **OFF in Core for every client** and turned on
30
+ per tenant with `SurfaceOverrides` rows on the `talos-assistant` surface. Developer instruction,
31
+ verbatim: *"in core all clients should have it toggled off. and then we will toggle it on
32
+ specifically for nychh in supply."*
31
33
 
32
- Everything shared — the surface, the elements, the blox panel, the supply provider — is
33
- tenant-neutral. Only the five grant rows are NYCHH's.
34
+ Everything shared — the surface, the two elements, the blox panel, the supply provider — is
35
+ tenant-neutral. Only NYCHH's **three** override rows are NYCHH's.
34
36
 
35
37
  ## How it works
36
38
 
37
- 1. **Core** (`Core/2026-09-02a`) seeds surface `talos-assistant` (type `DRAWER`, not
38
- record-bound) with 5 elements, all `isVisible = 0`: one `config.role='featureEnabled'` marker
39
- (the master switch) and four `config.role='suggestion'` chips.
40
- 2. **NYCHH** (`Client_Nychh/2026-09-02a`) inserts **5 `SurfaceOverrides` rows**, all
41
- `IS_VISIBLE = '1'`, one per element.
39
+ 1. **Core** (`Core/2026-09-02a`) seeds surface `talos-assistant` (type `DRAWER`, not record-bound)
40
+ with **two** elements, both `isVisible = 0`:
41
+ - `config.role='featureEnabled'`, `config.key='feature'` — the master switch.
42
+ - `config.role='suggestion'`, `config.key='suggestions'` — **the whole chip row in one element**,
43
+ its questions in `config.items` (an ordered array of `{label, prompt, icon}`).
44
+ 2. **NYCHH** (`Client_Nychh/2026-09-02a`) inserts **three rows**:
45
+ - `IS_VISIBLE = '1'` on `feature`
46
+ - `IS_VISIBLE = '1'` on `suggestions`
47
+ - a **`CONFIG` override** on `suggestions` carrying **NYCHH's own three questions**.
42
48
  3. The supply FE reads the marker via `useTalosSurface()` and **fails closed** — no bundle, failed
43
49
  fetch, missing marker, or still-loading all mean disabled. `TalosStreamProvider`'s `enabled`
44
50
  prop means an ungranted tenant never even calls Aegra's `/assistants`.
@@ -47,6 +53,53 @@ Mechanism and seeding rules:
47
53
  [surface-layer-schema](../../../2.0/apps/dbchanges2/features/surface-layer-schema.md).
48
54
  FE wiring: [Talos integration](../../../2.0/apps/toga25-supply/features/talos-integration.md).
49
55
 
56
+ ## NYCHH's three questions — it does NOT inherit Core's default
57
+
58
+ | Label | Icon |
59
+ |---|---|
60
+ | How many orders have been placed in the last year? | `lock` |
61
+ | Show me orders pending fulfillment | `truck` |
62
+ | How many orders are in each status? | `chartSimple` |
63
+
64
+ Developer instruction, verbatim: *"now for nychh, i need the array to be this instead: …"*. NYCHH's
65
+ `CONFIG` override **replaces** Core's default set outright.
66
+
67
+ `icon` is a **literal FontAwesome name**, and `label` is a **raw string, not a `Messages` key** —
68
+ so `MessageTranslations` cannot translate these. That trade is recorded in the schema doc.
69
+
70
+ ### 🚨 The override MUST restate `"role":"suggestion"`
71
+
72
+ A `CONFIG` override **replaces `config` wholesale — it does not merge** (`_underscore
73
+ Model/Core/Surface.php:839` assigns the decoded JSON over `$config`; there is no `array_merge`).
74
+ Send only `items` and `config.role` is **wiped**, the element stops matching in the frontend, and
75
+ **the chips silently disappear with no error anywhere**. A bare top-level JSON array also passes
76
+ that check, which is why it is the tempting wrong move.
77
+
78
+ The JSON goes in **`c_longValue`, never `value`** — `value` is `varchar(255)` and any real chip set
79
+ overflows it.
80
+
81
+ **That is why the verify query asserts the role survived, not just that the row exists:**
82
+
83
+ ```sql
84
+ JSON_UNQUOTE(JSON_EXTRACT(o.c_longValue, '$.role')) -- must be non-NULL
85
+ ```
86
+
87
+ ### Changing NYCHH's chips later
88
+
89
+ The file carries a `CHANGING NYCHH'S CHIPS LATER` block with two recipes:
90
+
91
+ - **Reword / add / reorder a chip** → `UPDATE` the `CONFIG` row's `c_longValue`. Order is **array
92
+ order**; there is no per-chip `sortOrder`. No Core deploy.
93
+ - **Fall back to Core's default set** → **`DELETE` the `CONFIG` row.** Do **not** write an empty
94
+ `items` array — that renders an **empty chip row**, not the default.
95
+
96
+ ### Section shape (prod cannot cross clusters)
97
+
98
+ Section **A2** is the single-cluster form (local/beta). Section **C2** is the prod literal-id form,
99
+ because Core and `Client_*` are on **separate clusters** in production. Section B reads the element
100
+ ids off the Core cluster first. Full three-section rule:
101
+ [surface-layer-schema](../../../2.0/apps/dbchanges2/features/surface-layer-schema.md).
102
+
50
103
  ## The grants are CLIENT-WIDE — no role scoping
51
104
 
52
105
  `roleId` / `personaId` / `languageId` are **NULL**, so **every NYCHH user, including internal
@@ -55,6 +108,22 @@ grants navigation (NYCHH does not scope navigation by role). **Per-role gating w
55
108
  it is ever needed, the grants have to be widened/narrowed at the *same* tier (see the shadowing rule
56
109
  in the schema doc).
57
110
 
111
+ ⚠ Note for whoever *does* scope it: chip **sets** are scopable, individual chips are not, and two
112
+ role-scoped `CONFIG` rows resolve by `ORDER BY (roleId IS NULL) DESC, (personaId IS NULL) DESC,
113
+ id ASC` — for a user holding both roles the **higher id simply wins**. The `feature` marker is a
114
+ plain boolean grant precisely so *access* keeps the clean grant-only rules.
115
+
116
+ ## Teardown — order matters
117
+
118
+ Both SQL files carry a commented `TEARDOWN`. Run it in this order:
119
+
120
+ 1. `DELETE` the `Client_Nychh.SurfaceOverrides` rows.
121
+ 2. `DELETE` the Core `SurfaceElements` rows.
122
+ 3. `DELETE` the `Surfaces` row.
123
+
124
+ Reverse it and the grants point at element ids that no longer exist. The `talos.feature.name`
125
+ `Message` is **left behind on purpose** — harmless, and re-used on a re-run.
126
+
58
127
  ## Aegra side
59
128
 
60
129
  - **`assistant_id` `c7981164-1fe9-5878-b115-85c38dadad43`** — name `TOGa Supply`,
@@ -70,24 +139,39 @@ in the schema doc).
70
139
 
71
140
  ## Run state — 🚨 beta and prod are NOT done
72
141
 
73
- Verified on **local only** (2026-09-02, developer ran the SQL):
142
+ Verified on **local only** (2026-09-02, developer ran the SQL; the full teardown + re-run worked end
143
+ to end and NYCHH's three chips render):
74
144
 
75
145
  - `navigation` surface = **id 39**, `appId` **1**.
76
- - Talos seed produced `SurfaceElements` **216–220** (feature / pendingApproval / blockedApprovals /
77
- pendingFulfillment / statusCounts), `sortOrder` 1 / 10 / 20 / 30 / 40, all `isVisible = 0`, all
78
- labels non-NULL.
79
- - `Client_Nychh` grants: **5 rows, all `IS_VISIBLE = '1'` on 216–220**.
146
+ - `Client_Nychh` overrides: **3 rows** — `IS_VISIBLE='1'` on `feature` and `suggestions`, plus the
147
+ `CONFIG` row carrying NYCHH's three items.
80
148
  - **Beta and production: NOT YET RUN.**
81
149
 
82
- **Element ids are per-environment** (`AUTO_INCREMENT`), so local / sandbox / prod will differ. Do
83
- **not** copy 216–220 into a prod run — that is precisely why the client grant file's Section B reads
84
- the ids off the Core cluster first.
150
+ **Do NOT copy any element id from this doc or from a teammate's local DB.** Element ids are
151
+ `AUTO_INCREMENT` and therefore **per-environment**. The first attempt of this feature consumed
152
+ `SurfaceElements` **216–220** under the old per-chip shape; those were torn down and re-created under
153
+ the two-element shape, so local ids advanced **again**. That is exactly why the client grant file's
154
+ Section B reads the ids off the Core cluster at run time.
85
155
 
86
156
  ## Change history
87
- - 2026-09-02 — First record. NYCHH became the **only** tenant granted Talos: Core seeds the
88
- `talos-assistant` surface OFF for all clients, `Client_Nychh/2026-09-02a` adds the 5 client-wide
89
- `IS_VISIBLE=1` grants (`roleId` NULL — every NYCHH user incl. devteam; per-role gating not built).
90
- Verified on **local only** — surface elements **216–220**, `navigation` surface 39/appId 1; **beta
91
- and prod not yet run**, and element ids are per-environment so prod must read them off Core.
92
- Recorded the NYCHH `assistant_id` and that thread auto-titles are broken **server-side** on that
93
- assistant, not in supply. (apeterson)
157
+ - 2026-09-02 — **CORRECTED to the final shape (the per-chip design recorded earlier is
158
+ superseded).** NYCHH now writes **three** rows, not five: `IS_VISIBLE='1'` on the `feature` and
159
+ `suggestions` elements plus **one `CONFIG` override authoring NYCHH's own three questions** ("How
160
+ many orders have been placed in the last year?" / lock, "Show me orders pending fulfillment" /
161
+ truck, "How many orders are in each status?" / chartSimple). Core seeds **two** elements — the
162
+ `featureEnabled` marker and **one** `suggestion` element whose `config.items` is an ordered array
163
+ of `{label, prompt, icon}` — replacing the five-element, one-chip-per-element first cut, which
164
+ meant only **Core** could author a tenant's wording. 🚨 The override **must restate
165
+ `"role":"suggestion"`**: a `CONFIG` override replaces `config` wholesale (`Surface.php:839`, no
166
+ merge), so omitting it wipes the role and the chips **silently vanish** — hence the verify query
167
+ now asserts `JSON_EXTRACT(c_longValue,'$.role')` is non-NULL, and the JSON lives in `c_longValue`
168
+ (not the varchar(255) `value`). Added a `CHANGING NYCHH'S CHIPS LATER` block (`UPDATE` to reword,
169
+ `DELETE` to fall back to Core's default — **never** an empty `items` array, which renders an empty
170
+ row) and a commented **TEARDOWN** whose order is load-bearing (client `SurfaceOverrides` → Core
171
+ `SurfaceElements` → `Surfaces`). Chip labels are raw strings, so `MessageTranslations` cannot
172
+ translate them; chip **sets** are scopable but individual chips are not, and a multi-role user
173
+ resolves arbitrarily (higher id wins) — which is why the `feature` marker stays a plain boolean
174
+ grant. Re-verified on **local only** after a full teardown + re-run; element ids advanced again and
175
+ remain per-environment, so prod must read them off Core in Section B. **Beta and prod still not
176
+ run.** Recorded the NYCHH `assistant_id` and that thread auto-titles are broken **server-side** on
177
+ that assistant, not in supply. (apeterson)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.747",
3
+ "version": "1.0.748",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",