toga-ai 1.0.742 → 1.0.744
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.
- package/knowledge/1.0/apps/library/features/toga2-api-client-and-bridge.md +12 -1
- package/knowledge/1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md +63 -1
- package/knowledge/2.0/apps/dbchanges2/INDEX.md +1 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +137 -1
- package/knowledge/2.0/apps/talos/features/aegra-api.md +47 -1
- package/knowledge/2.0/apps/toga-blox/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga-blox/features/talos-assistant.md +81 -3
- package/knowledge/2.0/apps/toga-blox/workflows/local-link-into-a-consumer-app.md +28 -1
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +23 -0
- package/knowledge/2.0/apps/toga25-supply/features/talos-integration.md +128 -3
- package/knowledge/INDEX.md +1 -1
- package/knowledge/clients/elite/features/netsuite-togasupply-sync.md +16 -1
- package/knowledge/clients/nychh/INDEX.md +2 -1
- package/knowledge/clients/nychh/features/talos-assistant-enablement.md +93 -0
- package/knowledge/clients/nychh/profile.md +12 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [jcardinal, mhammontree, bala]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
@@ -413,6 +413,17 @@ enable flags** and an optional `$monitorTogadeskDepartmentIds[]`:
|
|
|
413
413
|
|
|
414
414
|
## Change history
|
|
415
415
|
|
|
416
|
+
- 2026-09-02 — `syncPurchaseOrderFromNetsuite`'s cleanup DELETE ("remove dropped items") was a **bare
|
|
417
|
+
throwing** DELETE that froze the whole PURCHASE_ORDERS section whenever a NetSuite-dropped PO line
|
|
418
|
+
still had a RESTRICT child (item receipt `ItemReceiptItems.purchaseOrderItemId`, or an SO/TO link).
|
|
419
|
+
Fixed to **unlink first, then delete**: delete the four PO bridge routes, unhook item receipts with
|
|
420
|
+
`PUT /item-receipt-items/{uuid}` `['purchaseOrderItem' => null]`, then DELETE the PO line kept
|
|
421
|
+
**throwing** as the fail-loud backstop — mirroring the SALES_ORDERS cleanup. Written + reviewed,
|
|
422
|
+
**not yet committed/deployed**; carries a schema deploy dependency (nullable
|
|
423
|
+
`ItemReceiptItems.purchaseOrderItemId`). Full detail, the mirror-NetSuite decision, and the api2
|
|
424
|
+
bridge-route-key mechanic (kebab→camel, no pluralization) live on
|
|
425
|
+
[NetSuite → TOGa Supply per-client sync](../../worker/features/netsuite-togasupply-per-client-sync.md)
|
|
426
|
+
(2026-09-02). (jcardinal)
|
|
416
427
|
- 2026-09-01 — Recorded an options-DSL gotcha: a `where` with a **single** condition still
|
|
417
428
|
needs the `'and' =>` (or `'or' =>`) wrapper — a bare list of conditions makes api2 return
|
|
418
429
|
**HTTP 500 `EO-1` with an empty `transactionId`**, no usable message. Most often hit by
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: ["dfranks", "bala", "jcardinal", "mhammontree", "snaredla"]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/toga2/netsuite/common_sync_togasupply.php
|
|
@@ -596,6 +596,53 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
|
|
|
596
596
|
`NETSUITE_LAST_SYNC_DATETIME_*` frozen watermark), then read `Logs.Issue` — not `Logs_<Client>.Api`
|
|
597
597
|
— for the throwing order.
|
|
598
598
|
|
|
599
|
+
- **⚠ The PURCHASE_ORDERS cleanup DELETE froze the whole section when NetSuite drops a PO line that
|
|
600
|
+
still has an item receipt or SO/TO link (fixed 2026-09-02, `library` only — WRITTEN + REVIEWED,
|
|
601
|
+
NOT yet committed/deployed).** Same freeze class as the SALES_ORDERS renumber-DELETE above, on a
|
|
602
|
+
different path. `syncPurchaseOrderFromNetsuite`'s cleanup loop ("remove remaining items if
|
|
603
|
+
updating", `library/app/api/toga2.php`) deleted a leftover Toga PO line with a **bare, throwing**
|
|
604
|
+
`send()` DELETE `/purchase-order-items/{uuid}`. When the dropped line still had an item receipt
|
|
605
|
+
(`ItemReceiptItems.purchaseOrderItemId`, `ON DELETE RESTRICT`) or a sales-order link, the DELETE
|
|
606
|
+
returned MySQL **1451** → api2 **EV-11** 400 → `send()` threw → the whole PURCHASE_ORDERS section
|
|
607
|
+
aborted **before** advancing its watermark. Mode stuck at
|
|
608
|
+
`NETSUITE_EXECUTION_MODE_PURCHASE_ORDERS='1-RUNNING'`, `NETSUITE_LAST_SYNC_DATETIME_PURCHASE_ORDERS`
|
|
609
|
+
frozen (Elite: 2025-10-13), window shrank to 1s, the same poison PO re-tried every 5 min. Confirmed
|
|
610
|
+
in prod for Elite.
|
|
611
|
+
- **Fix (mirrors the accepted SALES_ORDERS cleanup pattern in the same file):** before deleting a
|
|
612
|
+
NetSuite-dropped PO line — (a) delete its link/bridge rows via api2 for all **four** bridge routes
|
|
613
|
+
(`/purchase-order-item-sales-order-items`, `/sales-order-item-purchase-order-items`,
|
|
614
|
+
`/purchase-order-items-transfer-order-items`, `/transfer-order-items-purchase-order-items`) —
|
|
615
|
+
removes the **relationship only**, keeps the SO/TO line; (b) unhook item-receipt lines with
|
|
616
|
+
`PUT /item-receipt-items/{uuid}` `['purchaseOrderItem' => null]` — keeps the receipt record, nulls
|
|
617
|
+
its link; (c) DELETE the PO line, **kept THROWING** as the fail-loud backstop so any *uncleared*
|
|
618
|
+
RESTRICT child (`BillItems`, `AdvanceShippingNoticeItems`) fails loud instead of corrupting or
|
|
619
|
+
silently skipping.
|
|
620
|
+
- **Decision — the sync MIRRORS NetSuite (NetSuite is source of truth).** When NetSuite drops a PO
|
|
621
|
+
line, delete our line **and** its link/bridge records but **keep the real records on the other
|
|
622
|
+
side** (units, tracking numbers, the item receipt itself — only the link is removed). **Never
|
|
623
|
+
log-and-skip past a stuck record** — that is data damage and violates the fail-loud rule. This
|
|
624
|
+
session first tried a log-and-skip guard; the developer rejected it and it was reverted. Same
|
|
625
|
+
policy as the 2026-08-27 per-order `try/catch` removal (an application of the universal
|
|
626
|
+
*never catch-and-silently-ignore* coding-style rule).
|
|
627
|
+
- **Schema dependency (must ship first).** The receipt "unhook" only works because
|
|
628
|
+
`ItemReceiptItems.purchaseOrderItemId` is **nullable**, made so by
|
|
629
|
+
`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` (section 7, guarded/re-runnable).
|
|
630
|
+
**Confirm that migration has run on every client DB before deploying the code fix**, or the PUT
|
|
631
|
+
`purchaseOrderItem = null` fails.
|
|
632
|
+
- **api2 route mechanics (verified against `api2/Component/Api/V2/V2.php`).** For these list/bridge
|
|
633
|
+
routes the response collection key is a **plain kebab→camel conversion of the route, no
|
|
634
|
+
pluralization** (`/purchase-order-item-sales-order-items` → `purchaseOrderItemSalesOrderItems`).
|
|
635
|
+
`PUT` accepts `['purchaseOrderItem' => null]` and writes SQL `NULL` (`_underscore` Model
|
|
636
|
+
`FIELD_FOREIGNKEY` preserves null). Bridge Records ids: **289/290** (SO↔PO), **329/330** (PO↔TO).
|
|
637
|
+
- **⚠ Tangled duplicate PO lines must fail loud — do NOT auto-move the receipt.** Elite prod has
|
|
638
|
+
**78** duplicate `(purchaseOrder, vendorItem)` PO line-groups; only **4** carry a receipt (the ones
|
|
639
|
+
that froze the cleanup). Worked example: NetSuite PO **138287** (internal 5734159) once had a
|
|
640
|
+
duplicate `SVC-FS-DEPLOY` line; Toga kept both (PO items 138 @line2, 139 @line3); a real NetSuite
|
|
641
|
+
item receipt (6239473, 38 units) landed on the removed duplicate → `Client_Elite.ItemReceiptItems`
|
|
642
|
+
51 pinned line 139. In **all 4** cases **both** duplicate lines carried receipts, so a naive
|
|
643
|
+
"move the receipt to the surviving line" is **unsafe** — double-count / `UNIQUE(itemReceiptId,
|
|
644
|
+
purchaseOrderItemId)` collision. Only clean cases self-heal; tangled ones must throw for a human.
|
|
645
|
+
|
|
599
646
|
- **⚠ Inventory-adjustment sync: two shared-engine bugs + it is still SOAP (2026-08-28).** Both fixed
|
|
600
647
|
in `syncInventoryAdjustmentFromNetsuite` (`library/app/api/toga2.php`) and reachable by any client
|
|
601
648
|
running inventory adjustments: (a) the stale-item prune deleted an `InventoryAdjustmentItem` before
|
|
@@ -922,6 +969,21 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
|
|
|
922
969
|
|
|
923
970
|
## Change history
|
|
924
971
|
|
|
972
|
+
- 2026-09-02 — **PURCHASE_ORDERS cleanup DELETE freeze fixed (shared, `library` only — WRITTEN +
|
|
973
|
+
REVIEWED, NOT yet committed/deployed).** `syncPurchaseOrderFromNetsuite`'s "remove dropped items"
|
|
974
|
+
cleanup deleted a NetSuite-dropped PO line with a bare throwing DELETE; a leftover item receipt
|
|
975
|
+
(`ItemReceiptItems.purchaseOrderItemId`, RESTRICT) or SO/TO link → 1451 → EV-11 → threw → froze the
|
|
976
|
+
whole PURCHASE_ORDERS section at `1-RUNNING` (Elite watermark stuck 2025-10-13, window ÷3 to 1s,
|
|
977
|
+
poison PO re-tried every 5 min). Fix mirrors the SALES_ORDERS cleanup: unlink all four PO bridge
|
|
978
|
+
routes, unhook item receipts via `PUT /item-receipt-items/{uuid}` `purchaseOrderItem = null`, then
|
|
979
|
+
DELETE the PO line kept **throwing** as the fail-loud backstop. **Decision:** the sync mirrors
|
|
980
|
+
NetSuite — remove our line + its links but keep the real records on the other side; never
|
|
981
|
+
log-and-skip (a log-and-skip guard was tried this session and rejected as data damage). **Deploy
|
|
982
|
+
dependency:** `ItemReceiptItems.purchaseOrderItemId` must be nullable
|
|
983
|
+
(`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7) on every client DB first.
|
|
984
|
+
Recorded the api2 bridge-route key mechanic (plain kebab→camel, no pluralization; PUT writes SQL
|
|
985
|
+
NULL) and Elite's 78 duplicate PO line-groups (4 with receipts, all tangled → must fail loud).
|
|
986
|
+
No prod data was modified (read-only SELECTs + NetSuite reads). (jcardinal)
|
|
925
987
|
- 2026-09-01 — **Customer-PO `dateOrder`/`cost` fixes + NYCHH inventory-adjustment serial-case unfreeze
|
|
926
988
|
(shared, `library` only, working-tree — not yet committed/deployed).** `syncSalesOrderFromNetsuite`
|
|
927
989
|
now sets the customer PO's `dateOrder` from the SO `tranDate` (was always NULL) and its PO lines'
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Database Changes (dbchanges2) Repository Architecture](architecture.md) | `dbchanges2` is the **schema-migration / SQL change-set repository** for the entire 2.0 platform. | Core/, Client/, Client_<Tenant>/, Logs/, Logs_Client/, _modules/ |
|
|
6
6
|
| [Re-runnable additive migrations (uuid4 in SQL, INSERT guards, the DISTINCT trap, conditional ALTERs)](features/rerunnable-additive-inserts.md) | Most `dbchanges2` files are **additive data grants** run by hand against production, often more than once (once per environment, or twice because someone was no | dbchanges2/Client_Compass/2026-08-26a - CompassCreativeStudioPersona.sql, dbchanges2/Core/2026-08-27a - Insert - Netsuite Location SyncAll CronJob.sql, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql |
|
|
7
|
-
| [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Core/2026-08-31a - SalesOrderDenialDetailsSurfaceSeed.sql, dbchanges2/Client_Compass/2026-08-31a - SalesOrderDenialDetailsReadOnlyRules.sql, toga25-supply/db-migrations/PLAYBOOK.md, toga25-supply/db-migrations/SURFACE-FEATURE-RUNBOOK.md, dbchanges2/Client_Compass/, dbchanges2/Client_CompassCanada/, dbchanges2/Client_Quad/, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Core/2026-08-24b - SalesOrderStatusFilterSurfaceSeed.sql, dbchanges2/Client_Compass/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_CompassCanada/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_Quad/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_Nychh/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Core/2026-08-21 - SalesOrderDecisionSurfacesReseed.sql, dbchanges2/Core/2026-08-24 - RestoreApproveDenyRowActionsVisibility.sql, dbchanges2/Client_Quad/2026-08-24 - ProdPortApprovePoNumberEnabledRule.sql, dbchanges2/Client_Quad/2026-07-21b - ApproveDisabledTooltipPoNumber.sql, dbchanges2/Client_Quad/2026-08-21 - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_CompassCanada/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-20e - RestoreApproveDenyRowActions.sql, dbchanges2/Client_Compass/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md, dbchanges2/Client_Compass/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Compass/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_CompassCanada/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.sql, dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql, dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql, dbchanges2/Client_Quad/2026-07-21c - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_CompassCanada/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Core/2026-07-23a - PoNumberDetailFieldValueKey.sql, dbchanges2/Core/2026-07-29a - SalesOrderApprovalDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-05b - ApprovalDetailsShippingMethodConcatCharge.sql, dbchanges2/Core/2026-08-14a - NoteBadgesVocabularySurface.sql, dbchanges2/Client/2026-08-14a - NoteBadgeThemeTokens.sql, dbchanges2/Client/2026-08-14b - NoteBadgeUserColorFix.sql, dbchanges2/Client_Compass/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Client_CompassCanada/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Core/2026-08-26a - ServiceRequestRecordHeaderSurfaceSeed.sql, dbchanges2/Core/2026-08-26b - ServiceRequestDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql |
|
|
7
|
+
| [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, dbchanges2/Core/2026-08-31a - SalesOrderDenialDetailsSurfaceSeed.sql, dbchanges2/Client_Compass/2026-08-31a - SalesOrderDenialDetailsReadOnlyRules.sql, toga25-supply/db-migrations/PLAYBOOK.md, toga25-supply/db-migrations/SURFACE-FEATURE-RUNBOOK.md, dbchanges2/Client_Compass/, dbchanges2/Client_CompassCanada/, dbchanges2/Client_Quad/, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Core/2026-08-24b - SalesOrderStatusFilterSurfaceSeed.sql, dbchanges2/Client_Compass/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_CompassCanada/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_Quad/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_Nychh/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Core/2026-08-21 - SalesOrderDecisionSurfacesReseed.sql, dbchanges2/Core/2026-08-24 - RestoreApproveDenyRowActionsVisibility.sql, dbchanges2/Client_Quad/2026-08-24 - ProdPortApprovePoNumberEnabledRule.sql, dbchanges2/Client_Quad/2026-07-21b - ApproveDisabledTooltipPoNumber.sql, dbchanges2/Client_Quad/2026-08-21 - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_CompassCanada/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-20e - RestoreApproveDenyRowActions.sql, dbchanges2/Client_Compass/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md, dbchanges2/Client_Compass/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Compass/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_CompassCanada/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.sql, dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql, dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql, dbchanges2/Client_Quad/2026-07-21c - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_CompassCanada/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Core/2026-07-23a - PoNumberDetailFieldValueKey.sql, dbchanges2/Core/2026-07-29a - SalesOrderApprovalDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-05b - ApprovalDetailsShippingMethodConcatCharge.sql, dbchanges2/Core/2026-08-14a - NoteBadgesVocabularySurface.sql, dbchanges2/Client/2026-08-14a - NoteBadgeThemeTokens.sql, dbchanges2/Client/2026-08-14b - NoteBadgeUserColorFix.sql, dbchanges2/Client_Compass/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Client_CompassCanada/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Core/2026-08-26a - ServiceRequestRecordHeaderSurfaceSeed.sql, dbchanges2/Core/2026-08-26b - ServiceRequestDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql |
|
|
8
8
|
| [2.0 New-Client Onboarding (manual process)](workflows/client-onboarding.md) | > **A local browser wizard now automates this.** Steps 2–9 below (create DBs, generate Core/API > inserts, append to `Clients_Db.txt`) — plus the dbchanges2 bla | Client/, Client_<Tenant>/, Core/, Logs_Client/ |
|
|
9
9
|
| [Auditing a client DB that drifted from its models (partially applied module migration)](workflows/client-schema-drift-audit.md) | A recurring 2.0 failure mode: **one client's database drifts from what the PHP models declare**, usually because a `_modules/<module>/` migration was applied to | dbchanges2/Client_Growrk/2026-05-28.sql, dbchanges2/Client_Growrk/2026-08-10c - GrowrkServiceRequestCustomFieldsCatchUp.sql, dbchanges2/Client_Growrk/2026-08-10d - GrowrkServiceRequestTypeAndDispositionSeeds.sql, dbchanges2/_modules/netsuite/2026-07-10a - UnitInventoryFields.sql, dbchanges2/Client_Growrk/2026-08-10 - GrowrkUnitInventoryFieldsCatchUp.sql, dbchanges2/Client_Growrk/2026-08-10b - GrowrkUnitItemDescriptionAcl.sql, dbchanges2/Client_Growrk/_modules.txt |
|
|
10
10
|
| [Framework branch running AHEAD of schema — environment-wide 1054/EO-1 after a deploy](workflows/framework-branch-ahead-of-schema.md) | A **third** kind of 2.0 schema drift, distinct from the two already documented: nobody's database went backwards — **the code went forwards**. | _underscore/Model/Core/RecordField.php, _underscore/Model/Client/TableViewField.php, _underscore/Model/Client/TableView.php, _underscore/Model/Core/Surface.php, _underscore/Model/Client/Urgency.php, _underscore/Model/Client/RecordFieldSetting.php, _underscore/Model/Client/ClientRecordFieldSetting.php, api2/Component/Api/V2/V2.php, dbchanges2/Core/2026-08-14c - RecordFieldsValidationMetadata.sql, dbchanges2/Client/2026-08-14a - RecordFieldSettingsClientTable.sql, dbchanges2/Client/2026-08-13a - UserNavPreferencesClientTable.sql, dbchanges2/Client/2026-08-13c - PersonaNavVisibilityClientTable.sql |
|
|
@@ -6,9 +6,11 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [jcardinal, apeterson, tcox]
|
|
11
11
|
files:
|
|
12
|
+
- dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql
|
|
13
|
+
- dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql
|
|
12
14
|
- dbchanges2/Core/2026-08-31a - SalesOrderDenialDetailsSurfaceSeed.sql
|
|
13
15
|
- dbchanges2/Client_Compass/2026-08-31a - SalesOrderDenialDetailsReadOnlyRules.sql
|
|
14
16
|
- toga25-supply/db-migrations/PLAYBOOK.md
|
|
@@ -1089,7 +1091,141 @@ hits `Duplicate entry '251' for key 'messages.PRIMARY'`. The resolution that kee
|
|
|
1089
1091
|
Create want. Seed an `Action` when the *behaviour* lives in the registry; seed a bare
|
|
1090
1092
|
`config.valueKey` when it lives in the page.
|
|
1091
1093
|
|
|
1094
|
+
## Gating a WHOLE FEATURE from the surface layer — the Talos seed (2026-09-02)
|
|
1095
|
+
|
|
1096
|
+
`Core/2026-09-02a - TalosAssistantSurfaceSeed.sql` +
|
|
1097
|
+
`Client_Nychh/2026-09-02a - TalosAssistantEnable.sql` put the Talos AI assistant behind the
|
|
1098
|
+
surface layer. The surface is `talos-assistant`, **type `DRAWER`**, **not record-bound**, with
|
|
1099
|
+
`appId` **inherited from the `navigation` sibling by slug** (never a literal). Five elements,
|
|
1100
|
+
**all seeded `isVisible = 0`** — grant-only:
|
|
1101
|
+
|
|
1102
|
+
| Role | Count | Purpose |
|
|
1103
|
+
|---|---|---|
|
|
1104
|
+
| `config.role = 'featureEnabled'`, `config.key = 'feature'` | 1 | **MASTER SWITCH.** Its `isVisible` turns the launcher, the panel and the whole AI stream on or off. |
|
|
1105
|
+
| `config.role = 'suggestion'` | 4 | one chip each (`pendingApproval`, `blockedApprovals`, `pendingFulfillment`, `statusCounts`): label → `labelMessageId`, the question actually sent → **`config.prompt`**, icon → `iconToken` + `iconWeightToken='solid'`, order → `sortOrder`. |
|
|
1106
|
+
|
|
1107
|
+
It replaced `DEFAULT_SUGGESTIONS` — four hardcoded **TOGa Supply** questions that lived inside
|
|
1108
|
+
`toga-blox`, a library five apps consume, so any host mounting the panel inherited Supply's
|
|
1109
|
+
prompts. FE side:
|
|
1110
|
+
[Talos integration](../../toga25-supply/features/talos-integration.md).
|
|
1111
|
+
|
|
1112
|
+
### 🚨 A feature switch is a MARKER ELEMENT — never `Surfaces.isVisible`
|
|
1113
|
+
|
|
1114
|
+
The override cascade is keyed by **`surfaceElementId`** and is **never applied to a `Surfaces`
|
|
1115
|
+
row's own `isVisible`** (`_loadOverrides`, `Model/Core/Surface.php` ~L733–748). **Overriding the
|
|
1116
|
+
surface row silently does nothing.** So a whole-feature switch has to be a hidden **marker
|
|
1117
|
+
element** on the surface, exactly like the `sectionVisibility` and `approvalsEnabled` markers.
|
|
1118
|
+
|
|
1119
|
+
### The prompt lives in `config.prompt`, NOT an `Actions` row
|
|
1120
|
+
|
|
1121
|
+
`RUN_TALOS` **is** a member of the `Actions.type` ENUM, but **nothing in `dbchanges2` has ever
|
|
1122
|
+
seeded an `Actions` row** and there is **no FE action-registry entry** for it. `config` is emitted
|
|
1123
|
+
**verbatim** into the resolved bundle, so a self-contained `config` needs **zero new resolution
|
|
1124
|
+
wiring**. Same trade `Core/2026-08-12a` made choosing `config.path` over `Actions`/`NAVIGATE`, and
|
|
1125
|
+
the same discriminator as the BUTTON_BAR rule above: **seed an `Action` only when the behaviour
|
|
1126
|
+
lives in the registry.**
|
|
1127
|
+
|
|
1128
|
+
### ⚠ Second authoring pattern: AUTO_INCREMENT + `NOT EXISTS`, for a PURELY ADDITIVE seed
|
|
1129
|
+
|
|
1130
|
+
The reserved-literal-id house style above (2026-08-24) remains the default. **These two files
|
|
1131
|
+
deliberately use `AUTO_INCREMENT` ids with `NOT EXISTS` guards on natural keys instead**, and the
|
|
1132
|
+
discriminator is *what the file does*, not *which table it writes*:
|
|
1133
|
+
|
|
1134
|
+
| Use reserved literal ids when… | Use `AUTO_INCREMENT` + `NOT EXISTS` when… |
|
|
1135
|
+
|---|---|
|
|
1136
|
+
| the file **re-parents** elements onto known surfaces, or any `Client_*` file must inline a Core id | the file is **purely additive** and **id-agnostic** — nothing references an id it did not just create |
|
|
1137
|
+
| reserving is only safe after verifying `MAX(id)` against **every** environment first (the TransferOrder seeds) | there is nothing to verify, because no literal is written anywhere |
|
|
1138
|
+
|
|
1139
|
+
The closest structural analogue is the original **`navigation`** seed, which is also additive and
|
|
1140
|
+
also took auto-increment ids.
|
|
1141
|
+
|
|
1142
|
+
**The cost is that element ids differ per environment**, which is exactly why the client grant
|
|
1143
|
+
file reads them off Core at run time (Section B below) instead of inlining them.
|
|
1144
|
+
|
|
1145
|
+
### The 3-section shape for a client grant that cannot cross clusters in prod
|
|
1146
|
+
|
|
1147
|
+
**In production Core and `Client_*` are on SEPARATE CLUSTERS**, so a client override file's
|
|
1148
|
+
`JOIN Core.SurfaceElements` **cannot run there**. `Client_Nychh/2026-09-02a` is therefore split
|
|
1149
|
+
into three labelled sections, matching the *Prod ACL procedure* in
|
|
1150
|
+
`toga25-supply/db-migrations/SURFACE-FEATURE-RUNBOOK.md`:
|
|
1151
|
+
|
|
1152
|
+
1. **Section A** — the single-cluster cross-schema `JOIN` version. **Local / beta only**, marked
|
|
1153
|
+
in the file "do NOT run in production".
|
|
1154
|
+
2. **Section B** — a **read-only `SELECT` on the CORE cluster** that prints the `surfaceId` + the
|
|
1155
|
+
element ids **and a paste-able `VALUES` body**.
|
|
1156
|
+
3. **Section C** — the literal-id `INSERT` on the **client** cluster, still `NOT EXISTS`-guarded.
|
|
1157
|
+
|
|
1158
|
+
Rollback and verify queries are given in **both** single-cluster and prod forms.
|
|
1159
|
+
|
|
1160
|
+
### Qualify or de-qualify a schema name on PURPOSE — the two rules pull opposite ways
|
|
1161
|
+
|
|
1162
|
+
- **A Core-only file must be DE-QUALIFIED** (`Core.Messages` → `Messages`). The Core schema is
|
|
1163
|
+
**not guaranteed to be named `Core`** — api2 can rename it via `[database] core_dbname` — so
|
|
1164
|
+
qualifying breaks exactly the environment that renamed it. A Core-only file crosses nothing and
|
|
1165
|
+
needs no qualification.
|
|
1166
|
+
- **A per-client file KEEPS its `Client_<Tenant>.` qualification** on purpose. Run against the
|
|
1167
|
+
wrong cluster or tenant it then fails **loudly** on a missing schema, instead of **silently
|
|
1168
|
+
granting the feature to another tenant**.
|
|
1169
|
+
|
|
1170
|
+
### 🚨 `SurfaceOverrides`' unique key gives a client-wide grant NO protection
|
|
1171
|
+
|
|
1172
|
+
The index is `UNIQUE (surfaceElementId, surfaceId, personaId, roleId, languageId, attribute)`.
|
|
1173
|
+
**MySQL treats NULLs in a unique key as DISTINCT**, and a client-wide grant has `personaId`,
|
|
1174
|
+
`roleId` and `languageId` **all NULL** — so **duplicate grant rows are accepted by the index.**
|
|
1175
|
+
|
|
1176
|
+
**The `NOT EXISTS` guard is the only thing making a re-run a no-op. It must never be "simplified"
|
|
1177
|
+
away.**
|
|
1178
|
+
|
|
1179
|
+
### Confirmed against the real DDL, not assumed
|
|
1180
|
+
|
|
1181
|
+
`SurfaceElements.iconToken` is **nullable**; `NEUTRAL` / `BUTTON` / `TEXT` / `LEFT` are valid enum
|
|
1182
|
+
members; `Surfaces.type` **`DRAWER`** exists; and the `Surfaces` UNIQUE KEY is **`(slug, appId)`**,
|
|
1183
|
+
not `slug` alone.
|
|
1184
|
+
|
|
1185
|
+
### OFF is the CORRECT default here, not merely the cautious one
|
|
1186
|
+
|
|
1187
|
+
Per the developer: *"in core all clients should have it toggled off. and then we will toggle it on
|
|
1188
|
+
specifically for nychh in supply."* This passes test (b) of the neutral-default rule decisively —
|
|
1189
|
+
Talos answers questions from a tenant's **live supply-chain data through an external AI service**,
|
|
1190
|
+
so a tenant must **not** inherit it from a migration. The FE mirrors it and **fails closed**.
|
|
1191
|
+
|
|
1192
|
+
**NOT built: per-role gating.** The grants are **client-wide** (`roleId` NULL), so every NYCHH
|
|
1193
|
+
user — including devteam — gets Talos. That matches how NYCHH already grants its navigation.
|
|
1194
|
+
|
|
1195
|
+
### Team convention — publish the next free id after any id-consuming seed
|
|
1196
|
+
|
|
1197
|
+
After a seed that consumes ids, post **"next `<Table>.id` = N"** for teammates. The number to
|
|
1198
|
+
publish is the **highest `MAX(id)+1` across EVERY environment**, not just local.
|
|
1199
|
+
|
|
1092
1200
|
## Change history
|
|
1201
|
+
- 2026-09-02 — Added the **Talos feature-gate seed pair** (`Core/2026-09-02a -
|
|
1202
|
+
TalosAssistantSurfaceSeed.sql`: surface `talos-assistant`, type **DRAWER**, not record-bound,
|
|
1203
|
+
`appId` inherited from the `navigation` sibling by slug, 5 elements all `isVisible = 0`;
|
|
1204
|
+
`Client_Nychh/2026-09-02a - TalosAssistantEnable.sql`: 5 client-wide grants). **DECIDED** four
|
|
1205
|
+
things worth reusing: (1) a whole-feature switch must be a hidden **marker element**, because the
|
|
1206
|
+
override cascade is keyed by `surfaceElementId` and is **never** applied to a `Surfaces` row's own
|
|
1207
|
+
`isVisible` (`_loadOverrides`, `Surface.php` ~L733–748) — overriding the surface row silently does
|
|
1208
|
+
nothing; (2) the chip prompt lives in **`config.prompt`**, not an `Actions` row — `RUN_TALOS` is in
|
|
1209
|
+
the ENUM but no `Actions` row has ever been seeded and there is no FE registry entry, while
|
|
1210
|
+
`config` is emitted verbatim, so it needs no new wiring; (3) a **purely additive, id-agnostic**
|
|
1211
|
+
seed may use **`AUTO_INCREMENT` + `NOT EXISTS`** instead of reserved literal ids — the
|
|
1212
|
+
discriminator is whether the file re-parents rows or a `Client_*` file must inline a Core id, and
|
|
1213
|
+
the analogue is the original `navigation` seed; (4) **OFF is correct, not just cautious**, for a
|
|
1214
|
+
feature that sends a tenant's live data to an external AI. **FIXED prod-readiness** (developer
|
|
1215
|
+
caught it): Core and `Client_*` are on **separate clusters** in prod, so the client file's
|
|
1216
|
+
`JOIN Core.SurfaceElements` cannot run there — restructured into **Section A** (single-cluster
|
|
1217
|
+
join, local/beta only), **Section B** (read-only `SELECT` on the Core cluster printing ids + a
|
|
1218
|
+
paste-able `VALUES` body), **Section C** (literal-id guarded `INSERT` on the client cluster), with
|
|
1219
|
+
rollback/verify in both forms. **De-qualified the Core file** (`Core.Messages` → `Messages`) because
|
|
1220
|
+
api2 can rename the Core schema via `[database] core_dbname`, while the per-client file **keeps**
|
|
1221
|
+
its `Client_Nychh.` qualification so a wrong-cluster run fails loudly rather than granting another
|
|
1222
|
+
tenant the feature. DISCOVERED that **`SurfaceOverrides`' unique key protects a client-wide grant
|
|
1223
|
+
not at all** — MySQL treats NULLs in a unique key as distinct, and a client-wide grant is
|
|
1224
|
+
`personaId`/`roleId`/`languageId` all NULL, so the `NOT EXISTS` guard is the *only* thing making a
|
|
1225
|
+
re-run idempotent. Verified against the real DDL: `iconToken` nullable, `NEUTRAL`/`BUTTON`/`TEXT`/
|
|
1226
|
+
`LEFT` valid, `Surfaces.type DRAWER` exists, `Surfaces` UNIQUE KEY is `(slug, appId)`. Local run
|
|
1227
|
+
produced `SurfaceElements` **216–220** (`navigation` = surface 39, appId 1); **beta and prod NOT
|
|
1228
|
+
yet run**. (apeterson)
|
|
1093
1229
|
- 2026-08-31 — Added the **denial-details** seed pair (`Core/2026-08-31a -
|
|
1094
1230
|
SalesOrderDenialDetailsSurfaceSeed.sql` — Surface **47**, elements **170–176**, Messages
|
|
1095
1231
|
**227–232**, plus a `readOnlyRule` marker on surface 41; and `Client_Compass/2026-08-31a -
|
|
@@ -6,7 +6,7 @@ project: TOGa IQ
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [akhokhani, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- talos/libs/aegra-api/src/aegra_api/main.py
|
|
@@ -224,11 +224,57 @@ None — uniform across clients. Per-tenant data isolation is enforced by
|
|
|
224
224
|
`Content-Type`** — `fetch` forbids a body on GET and a JSON content-type only adds a preflight.
|
|
225
225
|
- **An unregistered tenant gets a clean 403 on every call even with a valid token** (control-plane
|
|
226
226
|
DB / `tenant_router` LRU). Provisioning is explicit; there is no auto-create.
|
|
227
|
+
### The LIVE OpenAPI spec is PUBLIC — use it as the contract source (2026-09-02)
|
|
228
|
+
|
|
229
|
+
`https://api.togaiq.com/openapi.json` (and `/docs`) returns the **full live spec,
|
|
230
|
+
unauthenticated** — title `Talos 0.1.0`. It is a better contract source than any handoff
|
|
231
|
+
document: it is generated from the running deployment, so it cannot drift.
|
|
232
|
+
|
|
233
|
+
**Probing technique for "does this route exist?" without a token:** auth runs **before**
|
|
234
|
+
routing, so a real route answers **401** unauthenticated while a nonexistent route answers
|
|
235
|
+
**404**. Comparing the two codes proves a route exists with no credentials at all.
|
|
236
|
+
|
|
237
|
+
Confirmed this way (both are real server-side persistence in Aegra's own DB, not client state):
|
|
238
|
+
|
|
239
|
+
| Route | Notes |
|
|
240
|
+
|---|---|
|
|
241
|
+
| `PATCH /threads/{thread_id}` | *Update Thread*; body schema `ThreadUpdate` carries **only `metadata`**. `If-Match` is an **optional** header. This is how a client renames a thread. |
|
|
242
|
+
| `DELETE /threads/{thread_id}` | Deletes the thread. |
|
|
243
|
+
|
|
244
|
+
### ⚠ Thread auto-titles are a PER-ASSISTANT setting, and they are off/broken today (2026-09-02)
|
|
245
|
+
|
|
246
|
+
Two facts a browser client cannot work around:
|
|
247
|
+
|
|
248
|
+
1. **Aegra names a thread asynchronously after the first run and emits NO SSE event for it.**
|
|
249
|
+
The title lands in `metadata.thread_name`. A client's only way to see it is to **re-request
|
|
250
|
+
the thread list** — and because the name arrives *after* the run finishes, polling only
|
|
251
|
+
*during* the run usually misses it.
|
|
252
|
+
2. **Naming is configured per assistant, not globally.** `AssistantConfigResponse` exposes
|
|
253
|
+
**`thread_name_generation_enabled`** (boolean, **default `true`**) and
|
|
254
|
+
**`thread_name_model`** (default a Bedrock Claude Haiku model) on
|
|
255
|
+
`GET` / `PATCH /api/assistants/{assistant_id}/config`.
|
|
256
|
+
|
|
257
|
+
**Observed:** every thread in NYCHH's list reads `"New conversation"`, including threads days
|
|
258
|
+
old and far outside any client poll window. So either `thread_name_generation_enabled` is
|
|
259
|
+
`false` on the `TOGa Supply` assistant, or the naming model is unreachable in that deployment.
|
|
260
|
+
**A consumer app cannot fix this** — toga25-supply reads `metadata.thread_name` correctly.
|
|
261
|
+
Open with `akhokhani`; NYCHH `assistant_id` `c7981164-1fe9-5878-b115-85c38dadad43`.
|
|
262
|
+
|
|
227
263
|
- *Point-in-time note, may be stale:* on **2026-09-01** `https://api.beta.togaiq.com` returned
|
|
228
264
|
**502** on `/health`, `/info` and `/assistants` (nginx up, app behind it down) after a clean 401
|
|
229
265
|
~10 minutes earlier. Re-probe `/info` before assuming a client bug.
|
|
230
266
|
|
|
231
267
|
## Change history
|
|
268
|
+
- 2026-09-02 — DISCOVERED that Aegra publishes a **public** OpenAPI spec at
|
|
269
|
+
`https://api.togaiq.com/openapi.json` (`/docs`), which is a better contract source than a handoff
|
|
270
|
+
doc, plus the token-free route-probe technique (auth runs before routing, so a real route 401s and
|
|
271
|
+
a missing one 404s). Confirmed `PATCH /threads/{thread_id}` (`ThreadUpdate`, `metadata` only,
|
|
272
|
+
optional `If-Match`) and `DELETE /threads/{thread_id}` exist, so rename/delete are real
|
|
273
|
+
server-side persistence. Recorded that **thread auto-naming is a per-assistant config**
|
|
274
|
+
(`thread_name_generation_enabled`, default true, + `thread_name_model` on
|
|
275
|
+
`GET`/`PATCH /api/assistants/{id}/config`) and is **off or broken** for NYCHH's `TOGa Supply`
|
|
276
|
+
assistant — every thread reads "New conversation" regardless of age — and that the name lands
|
|
277
|
+
**after** the run with **no SSE event**, so a client must re-poll the thread list. (apeterson)
|
|
232
278
|
- 2026-09-01 — Added a **browser-client** gotcha set found while wiring toga25-supply: Aegra returns
|
|
233
279
|
CORS headers **on the OPTIONS preflight only**, so a browser blocks every real response even on a
|
|
234
280
|
healthy service (server-side callers never see it); `AUTH_TYPE=togahub` **accepts an api2 JWT
|
|
@@ -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 |
|
|
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 |
|
|
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-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [apeterson, tcox]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/src/components/Talos/TalosLauncher.tsx
|
|
@@ -20,6 +20,7 @@ files:
|
|
|
20
20
|
- toga-blox/src/components/Talos/index.ts
|
|
21
21
|
- toga-blox/src/components/Talos/TalosPanel.test.tsx
|
|
22
22
|
- toga-blox/src/components/index.ts
|
|
23
|
+
- toga-blox/src/components/Talos/README.md
|
|
23
24
|
related:
|
|
24
25
|
- ../architecture.md
|
|
25
26
|
- ../workflows/dynamic-publish-pipeline.md
|
|
@@ -211,10 +212,56 @@ so moving `messages` to the host is the same pattern, not a new one. And `connec
|
|
|
211
212
|
panel a real "we never got a token to begin with" state — previously a thrown `onSend` was its only
|
|
212
213
|
failure path.
|
|
213
214
|
|
|
215
|
+
### The loading bubble is CHROME, not conversation (fixed 2026-09-02)
|
|
216
|
+
|
|
217
|
+
The in-flight loading bubble was never a message. The panel **synthesizes** it from its own
|
|
218
|
+
`isStreaming` / `progress` props and appends it to its internal `msgs` array. When the
|
|
219
|
+
`renderMessages` host-render slot landed, its branch **replaced that whole array** — so a
|
|
220
|
+
controlled host saw **zero feedback between submit and answer**.
|
|
221
|
+
|
|
222
|
+
**Fix: the panel renders the loading bubble in BOTH modes.** A host taking over the message
|
|
223
|
+
list must not be able to lose it. Two regression tests pin this.
|
|
224
|
+
|
|
225
|
+
> **General rule for any future host-render slot:** anything the panel *synthesizes* rather than
|
|
226
|
+
> receives (loading state, connection notices, empty/error notes) has to live **outside** the
|
|
227
|
+
> host-replaceable branch. A slot that replaces a list also replaces everything the panel quietly
|
|
228
|
+
> mixed into that list.
|
|
229
|
+
|
|
230
|
+
### History drawer + new chat — DATA-DRIVEN, works in controlled mode (2026-09-02)
|
|
231
|
+
|
|
232
|
+
The drawer UI already existed, but it was bound to the panel's **internal** conversation list and
|
|
233
|
+
therefore force-disabled whenever the panel was controlled. It is now host-fed:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
threads?: TalosThreadSummary[]; // { id, title, updated }
|
|
237
|
+
activeThreadId?: string | null;
|
|
238
|
+
onSelectThread?: (id: string) => void;
|
|
239
|
+
onNewChat?: () => void;
|
|
240
|
+
threadsStatus?: "loading" | "error" | "ready";
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
- **`features.history` re-enables only when `threads` AND `onSelectThread` are BOTH supplied.**
|
|
244
|
+
`features.newChat` re-enables only when `onNewChat` is. A half-wired host gets **no button**
|
|
245
|
+
rather than a dead one — this replaces the blanket "controlled ⇒ history/newChat off" rule.
|
|
246
|
+
- **The panel sorts rows newest-first**, so a host may pass any order.
|
|
247
|
+
- **All three drawer notes (loading / error / empty) are gated on `histRows.length === 0`.**
|
|
248
|
+
Before, the error copy had no empty-list guard and rendered **alongside a full, usable list**
|
|
249
|
+
— the user saw "Couldn't load your chats." on top of their chats. Notes that describe the
|
|
250
|
+
*absence* of rows must be mutually exclusive with rows.
|
|
251
|
+
|
|
252
|
+
### Suggestion chips carry NO default content any more (2026-09-02)
|
|
253
|
+
|
|
254
|
+
`DEFAULT_SUGGESTIONS` — four hardcoded **TOGa Supply** questions — was **removed**. Tenant
|
|
255
|
+
business copy must not sit inside a library five apps consume: any host that mounted the panel
|
|
256
|
+
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
|
|
258
|
+
[Talos integration](../../toga25-supply/features/talos-integration.md)).
|
|
259
|
+
|
|
214
260
|
### Tests (first ever for this component)
|
|
215
261
|
|
|
216
|
-
`TalosPanel.test.tsx` — **
|
|
217
|
-
uncontrolled behaviour as unchanged
|
|
262
|
+
`TalosPanel.test.tsx` — **31 Vitest + RTL Talos tests** as of 2026-09-02 (8 at inception), all
|
|
263
|
+
passing, including **two that pin uncontrolled behaviour as unchanged** and two that pin the
|
|
264
|
+
loading bubble surviving `renderMessages`. (Separate from the ~40 pre-existing unrelated suite failures.)
|
|
218
265
|
|
|
219
266
|
**The stream itself does NOT belong in blox.** Per
|
|
220
267
|
[frontend.md §21](../../../standards/frontend.md) (promote only after a 2nd consumer validates the
|
|
@@ -234,6 +281,12 @@ Rollup *"failed to resolve import"* for a component they never referenced). blox
|
|
|
234
281
|
{ role: "user", text } // ✅ user turn
|
|
235
282
|
{ role: "talos", message } // ✅ assistant turn
|
|
236
283
|
```
|
|
284
|
+
- **⚠ The old rule "controlled mode forces `history`/`newChat` OFF" is SUPERSEDED (2026-09-02).**
|
|
285
|
+
They are off only until the host supplies `threads` + `onSelectThread` / `onNewChat`. Do not
|
|
286
|
+
code around the old behaviour.
|
|
287
|
+
- **A `renderMessages`-style slot can silently delete panel-synthesized UI.** The loading bubble
|
|
288
|
+
is not in `messages`; it was lost exactly this way. Check what the panel mixes into a list
|
|
289
|
+
before replacing that list.
|
|
237
290
|
- **`onSend` cannot stream** — use **controlled mode** (above). A live streaming backend needs the
|
|
238
291
|
controlled props, not a cleverer adapter.
|
|
239
292
|
- **Tool calls and HITL interrupts still have no representation** in `TalosMessageModel`. A host
|
|
@@ -257,12 +310,37 @@ Rollup *"failed to resolve import"* for a component they never referenced). blox
|
|
|
257
310
|
- **Hosts can import the Talos subpath to avoid the barrel's CSS.** `@agilant/toga-blox/dist/components/Talos/index.js`
|
|
258
311
|
resolves (package `exports` allows `./dist/*`); importing the package barrel pulls **every** blox
|
|
259
312
|
component's CSS into the consumer bundle.
|
|
313
|
+
- **⚠ `dist/` must be REBUILT for a blox change to reach any browser — and a green consumer
|
|
314
|
+
typecheck does NOT prove it did.** The consumer's `tsc` resolves blox **types from source**
|
|
315
|
+
through the `node_modules` symlink, but Vite loads the package at **runtime from `dist/`**. So
|
|
316
|
+
`tsc --noEmit` can be perfectly clean while the browser runs stale blox code. Every blox change
|
|
317
|
+
needs `npm run build` in the blox repo **plus** `rm -rf node_modules/.vite` in the consumer. See
|
|
318
|
+
[Local-linking blox into a consumer app §4](../workflows/local-link-into-a-consumer-app.md).
|
|
319
|
+
**Correction:** an earlier note claimed blox `npm run build` fails on a missing
|
|
320
|
+
`tsconfig.app.json` — it does not; it builds fine.
|
|
260
321
|
- blox `dist` is gitignored (shipped via publish, not the branch). Other consumers only get
|
|
261
322
|
Talos after blox is **published** per the `_sandbox-dev` dist-tag workflow — see
|
|
262
323
|
[Dynamic npm Publish Pipeline](../workflows/dynamic-publish-pipeline.md). toga25-supply
|
|
263
324
|
currently consumes it via a local symlink.
|
|
264
325
|
|
|
265
326
|
## Change history
|
|
327
|
+
- 2026-09-02 — **FIXED the `renderMessages` slot silently killing the in-flight loading UI**: the
|
|
328
|
+
bubble is synthesized from `isStreaming`/`progress` into the panel's internal `msgs` array, and the
|
|
329
|
+
host-render branch replaced that whole array, so a run showed **no feedback between submit and
|
|
330
|
+
answer**. The bubble now renders in **both** modes — it is chrome, not conversation. **BUILT a
|
|
331
|
+
data-driven history drawer + new chat that work in controlled mode**: new `threads`
|
|
332
|
+
(`TalosThreadSummary[]`), `activeThreadId`, `onSelectThread`, `onNewChat`, `threadsStatus` props;
|
|
333
|
+
`features.history` re-enables only when `threads` **and** `onSelectThread` are both passed and
|
|
334
|
+
`features.newChat` only when `onNewChat` is, so a half-wired host gets no button instead of a dead
|
|
335
|
+
one (this **supersedes** the blanket "controlled forces history/newChat off" rule); rows are sorted
|
|
336
|
+
newest-first inside the panel. **FIXED the drawer error note rendering alongside a full list** —
|
|
337
|
+
loading/error/empty are all now gated on `histRows.length === 0`. **REMOVED `DEFAULT_SUGGESTIONS`**
|
|
338
|
+
(four hardcoded TOGa Supply questions inside a shared library — every host inherited them);
|
|
339
|
+
`suggestions` now defaults to an empty constant and the host supplies chips. Suite is at **31 Talos
|
|
340
|
+
tests**. DISCOVERED that `dist/` must be rebuilt for any blox change to reach a browser and that a
|
|
341
|
+
clean consumer `tsc` does not prove it (types resolve from source, Vite loads `dist/`); this also
|
|
342
|
+
CORRECTS the earlier note that blox `npm run build` fails on a missing `tsconfig.app.json`.
|
|
343
|
+
(apeterson)
|
|
266
344
|
- 2026-09-01 — **BUILT controlled mode** (the TRUE-80692 contract change, previously only decided):
|
|
267
345
|
optional `messages` / `isStreaming` / `onSubmit` / `onStop` / `connection` on `TalosPanelProps`
|
|
268
346
|
plus a new exported `TalosConnection` type. Controlled mode keys off **`messages` being passed at
|
|
@@ -6,7 +6,7 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [tcox, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/package.json
|
|
@@ -207,6 +207,28 @@ config never reaches a real consumer. Restore the name when you unlink.
|
|
|
207
207
|
|
|
208
208
|
The consumer reads blox's `dist`, so rebuild blox after every change.
|
|
209
209
|
|
|
210
|
+
```
|
|
211
|
+
# in the blox checkout
|
|
212
|
+
npm run build
|
|
213
|
+
# in the consumer
|
|
214
|
+
rm -rf node_modules/.vite
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**⚠ A green consumer typecheck does NOT prove the consumer has your change (2026-09-02).** The
|
|
218
|
+
two toolchains read **different copies of blox**:
|
|
219
|
+
|
|
220
|
+
| Tool | Where it reads blox from |
|
|
221
|
+
|---|---|
|
|
222
|
+
| the consumer's `tsc --noEmit -p tsconfig.app.json` | blox **source**, through the `node_modules` symlink |
|
|
223
|
+
| Vite (dev server and build) | the blox package's **`dist/`** |
|
|
224
|
+
|
|
225
|
+
So `tsc` can be completely clean while the browser runs **stale** blox code — the symptom is "my
|
|
226
|
+
change does nothing" with no error anywhere. Both commands above are required after **every** blox
|
|
227
|
+
edit; clearing `node_modules/.vite` matters because Vite's pre-bundle cache holds the old `dist`.
|
|
228
|
+
|
|
229
|
+
**Correction:** an earlier note said blox `npm run build` fails on a missing `tsconfig.app.json`.
|
|
230
|
+
**It does not** — the build works.
|
|
231
|
+
|
|
210
232
|
## Gotchas
|
|
211
233
|
|
|
212
234
|
- **Confirm the link actually exists before you trust it — docs and memory go stale.**
|
|
@@ -236,6 +258,11 @@ The consumer reads blox's `dist`, so rebuild blox after every change.
|
|
|
236
258
|
drags every component's CSS into the consumer bundle.
|
|
237
259
|
|
|
238
260
|
## Change history
|
|
261
|
+
- 2026-09-02 — Expanded step 4: the consumer's `tsc` resolves blox **types from source** through the
|
|
262
|
+
symlink while Vite loads the package from **`dist/`**, so a clean `tsc --noEmit` does **not** prove
|
|
263
|
+
the browser has your change. Every blox edit needs `npm run build` in blox **plus**
|
|
264
|
+
`rm -rf node_modules/.vite` in the consumer. CORRECTED the earlier claim that blox `npm run build`
|
|
265
|
+
fails on a missing `tsconfig.app.json` — it builds fine. (apeterson)
|
|
239
266
|
- 2026-09-01 — Recorded that **any `npm install` in the consumer silently replaces the blox symlink
|
|
240
267
|
with the registry version** (toga25-supply → `1.1.1-production.139`, which has no Talos), and that
|
|
241
268
|
the symptom is a misleading `does not provide an export named 'TalosLauncher'`. Added the re-link
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
| [Side navigation & default route — an empty nav renders a BLANK PAGE and gets reported as "cannot log in"](features/side-navigation-and-default-route.md) | The 2.5 side nav is **100% backend-driven** by the `navigation` surface bundle, and the same list also decides **which routes exist** and **where `/` lands**. | toga25-supply/src/routes.tsx, toga25-supply/src/layout/AppLayout/viewModel/useAppLayoutViewModel.ts, api2/Component/Api/V2/V2.php, _underscore/Model/Core/Surface.php |
|
|
14
14
|
| [SSO redirect & public-vs-user session gating (useAuthenticationFlow)](features/sso-redirect-and-session-gating.md) | How 2.5 Supply decides, on every navigation, whether an anonymous visitor should be bounced to their client's SSO IdP instead of the local `/login` form. | toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/src/routes.tsx, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/api/api.ts |
|
|
15
15
|
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/App.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/SalesOrderApprovalModalsLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/RecordApprovalModalLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/view/ApprovalTimelineView.tsx, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css, toga25-supply/src/utils/formatDateTime.ts, toga25-supply/src/utils/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/layout/RecordApprovalModal/, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/ServiceRequestRecordModalLayout.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/view/ServiceRequestsView.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/useServiceRequestRecordModalLayoutModel.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/AdminNotesSection.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SurfaceRowActions.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderVip.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
16
|
-
| [Talos Integration (AppLayout host, live Aegra streaming, LangGraph→blox mapper)](features/talos-integration.md) | toga25-supply is the first host of the shared blox [Talos assistant](../../toga-blox/features/talos-assistant.md). | toga25-supply/src/api/talos.ts, toga25-supply/src/hooks/useTalosSession.ts, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/src/utils/talosMessages.ts, toga25-supply/src/utils/talosMessages.test.ts, toga25-supply/src/layout/AppLayout/AppLayout.tsx, toga25-supply/src/components/Header/Header.tsx, toga25-supply/src/components/Header/Header.module.css, toga25-supply/src/index.css, toga25-supply/src/assets/talos-owl.png |
|
|
16
|
+
| [Talos Integration (AppLayout host, live Aegra streaming, LangGraph→blox mapper)](features/talos-integration.md) | toga25-supply is the first host of the shared blox [Talos assistant](../../toga-blox/features/talos-assistant.md). | toga25-supply/src/api/talos.ts, toga25-supply/src/hooks/useTalosSession.ts, toga25-supply/src/hooks/useTalosThreads.ts, toga25-supply/src/hooks/useTalosThreads.test.ts, toga25-supply/src/hooks/useTalosSurface.ts, toga25-supply/src/hooks/useTalosSurface.test.ts, toga25-supply/src/App.tsx, toga25-supply/db-migrations/PLAYBOOK.md, dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/src/utils/talosMessages.ts, toga25-supply/src/utils/talosMessages.test.ts, toga25-supply/src/layout/AppLayout/AppLayout.tsx, toga25-supply/src/components/Header/Header.tsx, toga25-supply/src/components/Header/Header.module.css, toga25-supply/src/index.css, toga25-supply/src/assets/talos-owl.png |
|
|
17
17
|
| [Transfer Orders page (TableView → Core surfaces → React page + record modal)](features/transfer-orders-page.md) | The Transfer Orders screen — list + read-only record modal — built end to end on 2026-08-28 from a Claude Design prototype. | toga25-supply/src/pages/TransferOrders/TransferOrders.tsx, toga25-supply/src/pages/TransferOrders/hooks/useTransferOrdersTableState.ts, toga25-supply/src/pages/TransferOrders/viewModel/useTransferOrdersPageViewModel.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrdersTableLayout/TransferOrdersTableLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/hooks/useTransferOrderRecord.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/viewModel/useTransferOrderRecordModalLayoutModel.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/view/TransferOrderRecordView.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/CreateTransferOrderModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/index.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/usePurchaseOrderItemRows.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTargetLocationOptions.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTransferSourceLocation.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useLocationContacts.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateLocationContact.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/AddContactModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferNotesField.tsx, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, toga25-supply/src/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/routes.tsx, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.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 |
|
|
18
18
|
| [AWS Amplify Multi-Environment Deployment](workflows/amplify-deployment.md) | How `toga25-supply` deploys to **all** of its environments on AWS Amplify from a **single shared `amplify.yml`**. | toga25-supply/amplify.yml, toga25-supply/src/api/api.ts, toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/vite.config.ts, toga25-supply/package.json, toga25-supply/.env.development |
|
|
19
19
|
| [Cypress Testing Harness (component + e2e)](workflows/cypress-testing.md) | The Cypress test harness for the `toga25-supply` frontend, bootstrapped from scratch (`cypress` was already a dependency but there was no config, no `cypress/` | toga25-supply/cypress.config.ts, toga25-supply/cypress/tsconfig.json, toga25-supply/cypress/support/component.tsx, toga25-supply/cypress/support/component-index.html, toga25-supply/cypress/support/e2e.ts, toga25-supply/cypress/support/commands.ts, toga25-supply/cypress/support/fixtures.ts, toga25-supply/cypress/support/mocks/useApprovalModalViewModel.ts, toga25-supply/cypress/component/SalesOrderApprovalModalsLayout.cy.tsx, toga25-supply/cypress/component/RecordApprovalModalLayout.cy.tsx, toga25-supply/cypress/component/EnterPoNumberModal.cy.tsx, toga25-supply/cypress/e2e/salesOrderApproval.cy.ts |
|
|
@@ -725,6 +725,20 @@ here is a cross-screen change. Re-check Sales Orders, Items and Vendor Items aft
|
|
|
725
725
|
builds the bundle fresh every request (see
|
|
726
726
|
[surface-resolver](../../_underscore/features/surface-resolver.md); the old advice to clear
|
|
727
727
|
`~/toga/cache/surface_meta/*.json` is retracted). The FE persisted cache is the only one.
|
|
728
|
+
|
|
729
|
+
**✅ FIXED 2026-09-02 — the surface bundle is no longer persisted to disk.** `App.tsx` had **no
|
|
730
|
+
`dehydrateOptions`** at all, so the persister wrote *every* query — bundle included — to
|
|
731
|
+
`localStorage` and it expired only at the persister's **24h default `maxAge`**. Combined with
|
|
732
|
+
`staleTime: Infinity` that meant a grant, **or worse a REVOKE**, could take up to a day to reach
|
|
733
|
+
an existing browser. Unacceptable once a surface element gates an AI with access to tenant data.
|
|
734
|
+
A `shouldDehydrateQuery` filter now **excludes the `surface-meta` and `surface-meta-group`
|
|
735
|
+
namespaces** (and the `talos` namespace, which holds session data). Cost: **one request per page
|
|
736
|
+
load** — the in-memory cache still covers route changes within a page. The `staleTime: Infinity`
|
|
737
|
+
on the hooks is unchanged and still correct; disk persistence was the wrong half.
|
|
738
|
+
|
|
739
|
+
**Rule that generalizes:** a `PersistQueryClientProvider` with no `shouldDehydrateQuery` filter
|
|
740
|
+
persists **everything**. Any query that carries permissions, feature gates or session identity
|
|
741
|
+
must be excluded explicitly — never assume a reload re-reads it.
|
|
728
742
|
- **The single-meta envelope can hide the real bundle behind a DECOY `elements: []`.** The V2 envelope
|
|
729
743
|
can arrive as `{ surfaces: { meta: <real-bundle> }, elements: [] }` — a decoy empty `elements: []`
|
|
730
744
|
sits at the top level with **no `surface` descriptor**. A `looksLikeBundle` check matching on
|
|
@@ -900,6 +914,15 @@ button except **View Log** renders and does nothing on click. Whether the SR mod
|
|
|
900
914
|
actions surface is **still an open decision**, not an oversight.
|
|
901
915
|
|
|
902
916
|
## Change history
|
|
917
|
+
- 2026-09-02 — **FIXED the surface bundle being cached in `localStorage` for up to 24h, so a
|
|
918
|
+
permission or feature change could not take effect.** `App.tsx`'s `PersistQueryClientProvider` had
|
|
919
|
+
**no `dehydrateOptions`**, so every query — including the `staleTime: Infinity` surface bundle —
|
|
920
|
+
was written to disk and expired only at the persister's 24h default `maxAge`; a grant or a
|
|
921
|
+
**revoke** could take a day to reach a running browser. Added a `shouldDehydrateQuery` filter
|
|
922
|
+
excluding `surface-meta` / `surface-meta-group` (and `talos`, which is session data). Costs one
|
|
923
|
+
request per page load; the in-memory cache still covers route changes. Prompted by Talos becoming
|
|
924
|
+
a surface-gated feature — see
|
|
925
|
+
[Talos integration](./talos-integration.md). (apeterson)
|
|
903
926
|
- 2026-09-02 — Corrected the `cleanOrder.ts` note-slug gotcha: it flattens by the **real** slugs
|
|
904
927
|
`request` / `shipping` (fixed in `ce47dbd`), so the "latent blank-notes bug" recorded earlier no
|
|
905
928
|
longer exists. Added where "Reason for Request" actually lives (`SalesOrderNotes` +
|
|
@@ -6,11 +6,19 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/api/talos.ts
|
|
13
13
|
- toga25-supply/src/hooks/useTalosSession.ts
|
|
14
|
+
- toga25-supply/src/hooks/useTalosThreads.ts
|
|
15
|
+
- toga25-supply/src/hooks/useTalosThreads.test.ts
|
|
16
|
+
- toga25-supply/src/hooks/useTalosSurface.ts
|
|
17
|
+
- toga25-supply/src/hooks/useTalosSurface.test.ts
|
|
18
|
+
- toga25-supply/src/App.tsx
|
|
19
|
+
- toga25-supply/db-migrations/PLAYBOOK.md
|
|
20
|
+
- dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql
|
|
21
|
+
- dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql
|
|
14
22
|
- toga25-supply/src/providers/TalosStreamProvider.tsx
|
|
15
23
|
- toga25-supply/src/utils/talosMessages.ts
|
|
16
24
|
- toga25-supply/src/utils/talosMessages.test.ts
|
|
@@ -21,6 +29,9 @@ files:
|
|
|
21
29
|
- toga25-supply/src/assets/talos-owl.png
|
|
22
30
|
related:
|
|
23
31
|
- ../../toga-blox/features/talos-assistant.md
|
|
32
|
+
- ./surface-frontend.md
|
|
33
|
+
- ../../dbchanges2/features/surface-layer-schema.md
|
|
34
|
+
- ../../../clients/nychh/features/talos-assistant-enablement.md
|
|
24
35
|
- ../../toga-blox/workflows/local-link-into-a-consumer-app.md
|
|
25
36
|
- ../../talos/features/chat-frontend.md
|
|
26
37
|
- ../../talos/features/aegra-api.md
|
|
@@ -30,8 +41,11 @@ related:
|
|
|
30
41
|
## Summary
|
|
31
42
|
|
|
32
43
|
toga25-supply is the first host of the shared blox [Talos assistant](../../toga-blox/features/talos-assistant.md).
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
`AppLayout` is the host: the launcher renders in the header, the panel at app root.
|
|
45
|
+
|
|
46
|
+
**⚠ Corrected 2026-09-02 — Talos is NO LONGER enabled for all clients.** It is now gated by the
|
|
47
|
+
**surface layer**, seeded **OFF for every client**, and opted in per tenant. **NYCHH is the only
|
|
48
|
+
tenant granted it today.** The suggestion chips come from the same surface, not from blox.
|
|
35
49
|
|
|
36
50
|
As of **2026-09-01 the live Aegra/LangGraph streaming backend is BUILT** (TRUE-80693). `onSend` is
|
|
37
51
|
no longer a stub — the panel runs in blox's new **controlled mode** and the stream lives in a
|
|
@@ -41,7 +55,9 @@ supply-owned provider. All LangChain code is **app-local**; blox stays pure UI.
|
|
|
41
55
|
|
|
42
56
|
```
|
|
43
57
|
src/api/talos.ts — sole VITE_TALOS_API read chokepoint + assistants lookup
|
|
58
|
+
src/hooks/useTalosSurface.ts — DB feature gate + suggestion chips (surface bundle)
|
|
44
59
|
src/hooks/useTalosSession.ts — resolves token + assistantId → a `connection` state
|
|
60
|
+
src/hooks/useTalosThreads.ts — Aegra thread list → blox TalosThreadSummary[] + title polling
|
|
45
61
|
src/providers/TalosStreamProvider.tsx — the single useStream() call, exposed via own context
|
|
46
62
|
src/utils/talosMessages.ts — LangGraph Message[] → blox TalosMessageModel[]
|
|
47
63
|
src/layout/AppLayout/AppLayout.tsx — host: provider wrapper + controlled panel props
|
|
@@ -120,6 +136,85 @@ navigation side effects (`onOpenRecord` → `/sales-orders?sales-orders=<id>`, `
|
|
|
120
136
|
**Maps the app-only `"disabled"` state to `"error"`** when passing `connection` down — blox has no
|
|
121
137
|
`"disabled"` value.
|
|
122
138
|
|
|
139
|
+
### `useTalosSurface()` — the DB feature gate (2026-09-02)
|
|
140
|
+
|
|
141
|
+
Reads the Core surface `talos-assistant` (type `DRAWER`, not record-bound) and returns
|
|
142
|
+
**whether Talos is on for this tenant** plus the suggestion chips. Two element roles in the
|
|
143
|
+
bundle:
|
|
144
|
+
|
|
145
|
+
| `config.role` | Meaning |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `featureEnabled` (one element, `config.key = 'feature'`) | **master switch** — its `isVisible` turns the launcher, the panel and the Aegra stream on or off |
|
|
148
|
+
| `suggestion` (four elements) | one chip each: `labelMessageId` → label, **`config.prompt`** → the question actually sent, `iconToken`/`iconWeightToken` → icon, `sortOrder` → order |
|
|
149
|
+
|
|
150
|
+
Chip keys: `pendingApproval`, `blockedApprovals`, `pendingFulfillment`, `statusCounts`.
|
|
151
|
+
|
|
152
|
+
**The FE FAILS CLOSED — deliberately, not defensively.** Missing bundle, failed fetch, absent
|
|
153
|
+
marker element, or *still loading* all resolve to **disabled**. Talos answers questions from a
|
|
154
|
+
tenant's live supply-chain data through an external AI service, so an unresolved gate must never
|
|
155
|
+
read as "on".
|
|
156
|
+
|
|
157
|
+
`TalosStreamProvider` gained an **`enabled`** prop off the back of this: a tenant without Talos
|
|
158
|
+
never even calls `GET /assistants`.
|
|
159
|
+
|
|
160
|
+
Seeding side, the marker-element choice, and the prod-cluster procedure:
|
|
161
|
+
[surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
|
|
162
|
+
|
|
163
|
+
### `useTalosThreads()` — the history drawer's data (2026-09-02)
|
|
164
|
+
|
|
165
|
+
Maps Aegra `AegraThread` rows → blox `TalosThreadSummary[]` (`{ id, title, updated }`); the
|
|
166
|
+
provider exposes `threads` / `threadsStatus` / `activeThreadId` / `selectThread` and `AppLayout`
|
|
167
|
+
passes them straight into the panel's new data-driven drawer props.
|
|
168
|
+
|
|
169
|
+
**Refresh fires on two edges, and both are needed:**
|
|
170
|
+
|
|
171
|
+
1. **On `threadId` assignment.** The server creates the thread at the **START** of the first run,
|
|
172
|
+
so without this the *currently active* chat is missing from its own drawer.
|
|
173
|
+
2. **On the falling edge of a run** (streaming → idle).
|
|
174
|
+
|
|
175
|
+
#### Opening an existing thread needs NO transcript parser
|
|
176
|
+
|
|
177
|
+
`@langchain/langgraph-sdk`'s **`setThreadId`** (`dist/ui/orchestrator.js`,
|
|
178
|
+
`#fetchHistoryForThread`) **clears the stream and fetches that thread's history itself** when
|
|
179
|
+
`fetchStateHistory: true`. So selecting a history row is just "set the threadId", and the
|
|
180
|
+
existing LangGraph→blox mapper handles the rest.
|
|
181
|
+
|
|
182
|
+
This took `fromTalosTranscript()` **off the critical path for history**. It is still needed for
|
|
183
|
+
the durable message ids that feedback requires — do not delete it.
|
|
184
|
+
|
|
185
|
+
#### Auto-title polling — `titlePollInterval`
|
|
186
|
+
|
|
187
|
+
Aegra names a thread **asynchronously after the first run** and emits **no SSE event**, so the
|
|
188
|
+
only way to see a title is to ask again. `titlePollInterval` feeds React Query's
|
|
189
|
+
`refetchInterval`: **4s while any RECENT thread is unnamed, `false` otherwise.**
|
|
190
|
+
|
|
191
|
+
**Two deliberate deviations from the handoff doc §8.10:**
|
|
192
|
+
|
|
193
|
+
- The doc says poll *"while the first run is active"*. **The title lands AFTER the run**, so
|
|
194
|
+
polling only during it usually misses it entirely.
|
|
195
|
+
- Added a **2-minute age window** the doc does not specify. A thread whose first message was
|
|
196
|
+
never text-bearing **never gets named at all**, so "stop when the title arrives" on its own
|
|
197
|
+
polls every 4s for the whole session.
|
|
198
|
+
|
|
199
|
+
7 tests cover the stop condition, including a server clock running ahead of the client.
|
|
200
|
+
|
|
201
|
+
> Server-side, auto-naming is currently **off or broken** for NYCHH's assistant, so this polling
|
|
202
|
+
> has nothing to find yet — see
|
|
203
|
+
> [aegra-api](../../talos/features/aegra-api.md). Supply reads `metadata.thread_name` correctly
|
|
204
|
+
> and cannot fix it.
|
|
205
|
+
|
|
206
|
+
### Gotcha — React Query sets `isError` on a FAILED BACKGROUND REFETCH while KEEPING the data
|
|
207
|
+
|
|
208
|
+
This printed **"Couldn't load your chats." over a full, usable chat list.** The status line was
|
|
209
|
+
`isError ? "error" : …`, so **one** failed 4s title poll flipped a good list into an error state.
|
|
210
|
+
|
|
211
|
+
**Fix:** a pure `threadsStatusFrom` helper that returns `"error"` **only when there is no data to
|
|
212
|
+
show**. Pair it with the blox-side fix (drawer notes gated on an empty row list) — both halves
|
|
213
|
+
were needed.
|
|
214
|
+
|
|
215
|
+
Any hook with a `refetchInterval` inherits this trap: `isError` is not "the data is unusable",
|
|
216
|
+
it is "the last fetch failed".
|
|
217
|
+
|
|
123
218
|
### `talosMessages.ts` — the LangGraph → blox mapper
|
|
124
219
|
|
|
125
220
|
- Handles LangGraph's **string-OR-content-blocks** union for `content`.
|
|
@@ -161,6 +256,15 @@ registry version, which contains no Talos. The symptom looks nothing like a depe
|
|
|
161
256
|
Full symptom + relink recipe:
|
|
162
257
|
[Local-linking blox into a consumer app §1a](../../toga-blox/workflows/local-link-into-a-consumer-app.md).
|
|
163
258
|
|
|
259
|
+
## The `talos` query namespace is EXCLUDED from the persisted cache (2026-09-02)
|
|
260
|
+
|
|
261
|
+
`App.tsx`'s `PersistQueryClientProvider` used to persist **every** query to `localStorage`. A
|
|
262
|
+
`shouldDehydrateQuery` filter now excludes the **`talos`** namespace alongside `surface-meta` /
|
|
263
|
+
`surface-meta-group`: chat titles and assistant ids are **user/session data that must not outlive
|
|
264
|
+
the session on disk**. The surface half of the same fix is what makes a Talos grant (or a
|
|
265
|
+
**revoke**) actually reach a running browser — see
|
|
266
|
+
[surface-frontend](./surface-frontend.md).
|
|
267
|
+
|
|
164
268
|
## Environment config
|
|
165
269
|
|
|
166
270
|
`VITE_TALOS_API` is set to `https://api.beta.togaiq.com` in **7 non-prod env files**. It is left
|
|
@@ -206,6 +310,27 @@ user resend. Decide deliberately rather than discovering it in QA.
|
|
|
206
310
|
passes the token it already has straight into `defaultHeaders`.
|
|
207
311
|
|
|
208
312
|
## Change history
|
|
313
|
+
- 2026-09-02 — **Talos is now SURFACE-GATED, and OFF for every client by default** (this
|
|
314
|
+
**corrects** the old "enabled for ALL clients, no per-client gating"): new `useTalosSurface()`
|
|
315
|
+
reads the Core `talos-assistant` surface for a `config.role='featureEnabled'` **marker element**
|
|
316
|
+
(master switch) plus four `config.role='suggestion'` chips whose text lives in `config.prompt` —
|
|
317
|
+
replacing blox's hardcoded `DEFAULT_SUGGESTIONS`. The FE **fails closed**: missing bundle, failed
|
|
318
|
+
fetch, absent marker, or still-loading all resolve to disabled, and `TalosStreamProvider` gained
|
|
319
|
+
an `enabled` prop so an ungranted tenant never calls `/assistants`. **NYCHH is the only granted
|
|
320
|
+
tenant.** **BUILT the history drawer + new chat** (`useTalosThreads` maps Aegra threads → blox
|
|
321
|
+
summaries; refresh on `threadId` assignment — the server creates the thread at the **start** of
|
|
322
|
+
the first run, so the active chat was otherwise missing from its own drawer — and on the falling
|
|
323
|
+
edge of a run). DISCOVERED that opening a thread needs **no transcript parser**: the SDK's
|
|
324
|
+
`setThreadId` clears the stream and fetches that thread's history itself when
|
|
325
|
+
`fetchStateHistory: true`, so `fromTalosTranscript()` leaves the history critical path (still
|
|
326
|
+
needed for durable message ids). **BUILT auto-title polling** (`titlePollInterval` → 4s while any
|
|
327
|
+
thread <2min old is unnamed) with two deliberate deviations from the handoff doc §8.10 — the title
|
|
328
|
+
lands **after** the run, not during it, and an unnamed-forever thread needs the age window to stop
|
|
329
|
+
the poll. **FIXED "Couldn't load your chats." printing over a full list**: React Query sets
|
|
330
|
+
`isError` on a failed **background** refetch while keeping the data, so one bad 4s poll flipped a
|
|
331
|
+
good list into error; new pure `threadsStatusFrom` returns `"error"` only when there is nothing to
|
|
332
|
+
show. Excluded the **`talos`** query namespace from the `localStorage`-persisted cache (chat
|
|
333
|
+
titles + assistant ids are session data). (apeterson)
|
|
209
334
|
- 2026-09-01 — **BUILT the live streaming integration.** Added `src/api/talos.ts` (env chokepoint +
|
|
210
335
|
runtime `GET /assistants` lookup — `assistantId` is **per-tenant**, never pinned in env),
|
|
211
336
|
`useTalosSession` (gates on `isAuthenticated`, not on a token, because blox leaves a **public**
|
package/knowledge/INDEX.md
CHANGED
|
@@ -31,7 +31,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
31
31
|
- **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
|
|
32
32
|
- **ai-bdr** (AI-BDR) — 13 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
|
|
33
33
|
- **toga2-commerce** (TOGa Commerce) — 20 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
|
|
34
|
-
- **toga25-supply** (TOGa 2.5 Supply) —
|
|
34
|
+
- **toga25-supply** (TOGa 2.5 Supply) — 18 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
|
|
35
35
|
- **toga-blox** (TOGa Blox) — 14 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
|
|
36
36
|
- **bdr** (BDR) — 0 doc(s) → [2.0/apps/bdr/INDEX.md](2.0/apps/bdr/INDEX.md)
|
|
37
37
|
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: elite
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: ["snaredla", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/toga2/netsuite/sync_togasupply_elite.php
|
|
@@ -125,6 +125,21 @@ the trait declares must exist as a column, a `CustomRecordFields` row **and** an
|
|
|
125
125
|
|
|
126
126
|
## Change history
|
|
127
127
|
|
|
128
|
+
- 2026-09-02 — **Elite PURCHASE_ORDERS froze on a dropped PO line that still had a receipt** (shared
|
|
129
|
+
fix, `library` only — WRITTEN + REVIEWED, NOT yet committed/deployed). Elite was the confirmed prod
|
|
130
|
+
case: `NETSUITE_EXECUTION_MODE_PURCHASE_ORDERS='1-RUNNING'`, watermark frozen at **2025-10-13**,
|
|
131
|
+
window collapsed to 1s, same poison PO re-tried every 5 min. Root cause is shared, not Elite —
|
|
132
|
+
`syncPurchaseOrderFromNetsuite`'s cleanup DELETE threw on a leftover item receipt / SO link. Elite
|
|
133
|
+
data behind it: **78 duplicate `(purchaseOrder, vendorItem)` PO line-groups**, only **4** carry a
|
|
134
|
+
receipt — the 4 that froze the cleanup. Worked example: NetSuite PO **138287** (internal 5734159)
|
|
135
|
+
kept a duplicate `SVC-FS-DEPLOY` line (PO items 138 @line2, 139 @line3); a real receipt
|
|
136
|
+
(6239473, 38 units) landed on the removed duplicate → `Client_Elite.ItemReceiptItems` 51 pinned line
|
|
137
|
+
139. In all 4 both duplicate lines carried receipts, so a naive "move the receipt" is unsafe
|
|
138
|
+
(double-count / `UNIQUE(itemReceiptId, purchaseOrderItemId)` collision) — tangled cases fail loud
|
|
139
|
+
for a human, only clean cases self-heal. **Before deploy:** confirm
|
|
140
|
+
`ItemReceiptItems.purchaseOrderItemId` is nullable on `Client_Elite`
|
|
141
|
+
(`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7). Full mechanics on the
|
|
142
|
+
[engine doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md). (jcardinal)
|
|
128
143
|
- 2026-09-01 — **Drop-ship / special-order POs were never importing for Elite** (e.g. PO tranid
|
|
129
144
|
128238). Root cause was in shared code, not Elite: `listPurchaseOrders()` filtered the PO header by
|
|
130
145
|
the VENDOR `entity` (TD Synnex 20202) instead of the customer, so Elite's 36443-scoped filter matched
|
|
@@ -5,5 +5,6 @@
|
|
|
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
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 |
|
|
9
|
-
| [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-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 |
|
|
10
|
+
| [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-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 |
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: NYCHH Talos AI Assistant Enablement (the only granted tenant)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga25-supply
|
|
5
|
+
project: TOGa 2.5 Supply
|
|
6
|
+
client: nychh
|
|
7
|
+
type: client-feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-09-02
|
|
10
|
+
owners: [apeterson]
|
|
11
|
+
files:
|
|
12
|
+
- dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql
|
|
13
|
+
- dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql
|
|
14
|
+
- toga25-supply/src/hooks/useTalosSurface.ts
|
|
15
|
+
- toga25-supply/src/providers/TalosStreamProvider.tsx
|
|
16
|
+
- toga25-supply/db-migrations/PLAYBOOK.md
|
|
17
|
+
related:
|
|
18
|
+
- ../../../2.0/apps/toga25-supply/features/talos-integration.md
|
|
19
|
+
- ../../../2.0/apps/toga-blox/features/talos-assistant.md
|
|
20
|
+
- ../../../2.0/apps/dbchanges2/features/surface-layer-schema.md
|
|
21
|
+
- ../../../2.0/apps/talos/features/aegra-api.md
|
|
22
|
+
- ../profile.md
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Summary
|
|
26
|
+
|
|
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."*
|
|
31
|
+
|
|
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
|
+
|
|
35
|
+
## How it works
|
|
36
|
+
|
|
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.
|
|
42
|
+
3. The supply FE reads the marker via `useTalosSurface()` and **fails closed** — no bundle, failed
|
|
43
|
+
fetch, missing marker, or still-loading all mean disabled. `TalosStreamProvider`'s `enabled`
|
|
44
|
+
prop means an ungranted tenant never even calls Aegra's `/assistants`.
|
|
45
|
+
|
|
46
|
+
Mechanism and seeding rules:
|
|
47
|
+
[surface-layer-schema](../../../2.0/apps/dbchanges2/features/surface-layer-schema.md).
|
|
48
|
+
FE wiring: [Talos integration](../../../2.0/apps/toga25-supply/features/talos-integration.md).
|
|
49
|
+
|
|
50
|
+
## The grants are CLIENT-WIDE — no role scoping
|
|
51
|
+
|
|
52
|
+
`roleId` / `personaId` / `languageId` are **NULL**, so **every NYCHH user, including internal
|
|
53
|
+
devteam accounts, gets Talos.** That was accepted deliberately: it matches how this tenant already
|
|
54
|
+
grants navigation (NYCHH does not scope navigation by role). **Per-role gating was NOT built** — if
|
|
55
|
+
it is ever needed, the grants have to be widened/narrowed at the *same* tier (see the shadowing rule
|
|
56
|
+
in the schema doc).
|
|
57
|
+
|
|
58
|
+
## Aegra side
|
|
59
|
+
|
|
60
|
+
- **`assistant_id` `c7981164-1fe9-5878-b115-85c38dadad43`** — name `TOGa Supply`,
|
|
61
|
+
`graph_id: deep_agent`, `read_only: true`, `customer_facing: true`. NYCHH returns exactly **one**
|
|
62
|
+
assistant. It is still **discovered at runtime**, never pinned in env.
|
|
63
|
+
- 🚨 **Thread auto-titles are broken/disabled SERVER-side for this assistant.** Every thread in
|
|
64
|
+
NYCHH's list reads `"New conversation"`, including threads days old. Naming is a per-assistant
|
|
65
|
+
setting (`thread_name_generation_enabled` / `thread_name_model` on
|
|
66
|
+
`GET`/`PATCH /api/assistants/{id}/config`), so either it is `false` on `TOGa Supply` or the naming
|
|
67
|
+
model is unreachable in that deployment. **Supply cannot fix this** — it reads
|
|
68
|
+
`metadata.thread_name` correctly. Open with `akhokhani`; details in
|
|
69
|
+
[aegra-api](../../../2.0/apps/talos/features/aegra-api.md).
|
|
70
|
+
|
|
71
|
+
## Run state — 🚨 beta and prod are NOT done
|
|
72
|
+
|
|
73
|
+
Verified on **local only** (2026-09-02, developer ran the SQL):
|
|
74
|
+
|
|
75
|
+
- `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**.
|
|
80
|
+
- **Beta and production: NOT YET RUN.**
|
|
81
|
+
|
|
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.
|
|
85
|
+
|
|
86
|
+
## 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)
|
|
@@ -17,7 +17,7 @@ project: _Underscore
|
|
|
17
17
|
client: nychh
|
|
18
18
|
type: profile
|
|
19
19
|
status: active
|
|
20
|
-
updated: 2026-09-
|
|
20
|
+
updated: 2026-09-02
|
|
21
21
|
owners: ["jcardinal", "apeterson", "bala", "akhokhani"]
|
|
22
22
|
files:
|
|
23
23
|
- dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql
|
|
@@ -28,6 +28,7 @@ files:
|
|
|
28
28
|
- dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql
|
|
29
29
|
- dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql
|
|
30
30
|
- dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql
|
|
31
|
+
- dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql
|
|
31
32
|
related:
|
|
32
33
|
- ../../2.0/apps/_underscore/features/tracking-number-bridges.md
|
|
33
34
|
- ../../2.0/apps/_underscore/features/tableview-joins.md
|
|
@@ -35,6 +36,7 @@ related:
|
|
|
35
36
|
- ./features/netsuite-transfer-order-import.md
|
|
36
37
|
- ./features/transfer-order-inventory-quantities.md
|
|
37
38
|
- ./features/netsuite-inventory-adjustment-fulfillment-link.md
|
|
39
|
+
- ./features/talos-assistant-enablement.md
|
|
38
40
|
- ../../2.0/apps/_underscore/features/sales-order-purchase-order-bridge-direction.md
|
|
39
41
|
- ../../2.0/apps/toga25-supply/features/transfer-orders-page.md
|
|
40
42
|
---
|
|
@@ -64,6 +66,15 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
64
66
|
`AclLogicGroups` → `AclLogicGroupExpressions`, reusing record 17's `all` expression) and reads
|
|
65
67
|
`AclFieldPermissions` for the displayed fields, resolving `roleId` by role **name**. Written
|
|
66
68
|
idempotent / uuid-keyed; applied to **prod and client-sandbox**.
|
|
69
|
+
- **NYCHH is the ONLY tenant granted the Talos AI assistant (2026-09-02).** Talos is seeded **off
|
|
70
|
+
in Core for every client** and enabled per tenant with 5 `SurfaceOverrides` rows on the
|
|
71
|
+
`talos-assistant` surface; `Client_Nychh/2026-09-02a - TalosAssistantEnable.sql` is the only grant
|
|
72
|
+
file. The grants are **client-wide** (`roleId` NULL), so every NYCHH user including devteam gets
|
|
73
|
+
it — matching how this tenant already grants navigation. **Applied on LOCAL only; beta and prod
|
|
74
|
+
are not run**, and the element ids are per-environment. Their Aegra `assistant_id` is
|
|
75
|
+
`c7981164-1fe9-5878-b115-85c38dadad43` (`TOGa Supply`), and thread auto-titles are broken
|
|
76
|
+
**server-side** on that assistant. See
|
|
77
|
+
[NYCHH Talos enablement](./features/talos-assistant-enablement.md).
|
|
67
78
|
- **`inventory_units` table view carries item detail as of 2026-08-18** — Part Number, Manufacturer
|
|
68
79
|
and Item Description sit directly after Asset Tag, reached through a **chained** Units → Items →
|
|
69
80
|
Manufacturers join (`dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql`, identical
|
package/package.json
CHANGED