toga-ai 1.0.691 โ 1.0.693
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/2.0/apps/api2/features/tableview-field-metadata.md +39 -0
- package/knowledge/2.0/apps/dbchanges2/INDEX.md +2 -2
- package/knowledge/2.0/apps/dbchanges2/features/rerunnable-additive-inserts.md +46 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +69 -1
- package/knowledge/2.0/apps/dbchanges2/workflows/client-onboarding.md +15 -0
- package/knowledge/2.0/apps/dbchanges2/workflows/client-schema-drift-audit.md +37 -1
- package/knowledge/2.0/apps/toga-blox/INDEX.md +2 -2
- package/knowledge/2.0/apps/toga-blox/features/blox-authoring-defects.md +25 -1
- package/knowledge/2.0/apps/toga-blox/workflows/local-link-into-a-consumer-app.md +38 -1
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +3 -3
- package/knowledge/2.0/apps/toga25-supply/features/column-visibility.md +48 -3
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +25 -0
- package/knowledge/2.0/apps/toga25-supply/features/transfer-orders-page.md +141 -6
- package/knowledge/2.0/apps/toga25-supply/workflows/amplify-deployment.md +26 -1
- package/knowledge/clients/nychh/INDEX.md +2 -2
- package/knowledge/clients/nychh/features/transfer-order-inventory-quantities.md +66 -2
- package/knowledge/clients/nychh/profile.md +12 -1
- package/knowledge/sessions/2026-08-28-talos-streaming-supply-apeterson.md +209 -0
- package/package.json +1 -1
|
@@ -235,10 +235,41 @@ Applied for Quad: the sales-orders listing now defaults to `dateOrder DESC` (mos
|
|
|
235
235
|
first) instead of `number DESC` (`dbchanges2/Client_Quad/2026-08-18c -
|
|
236
236
|
SalesOrderListingSortByDateOrderDesc.sql`).
|
|
237
237
|
|
|
238
|
+
### ๐จ An ACL gap on the DEFAULT-SORT column surfaces as a 500 SQL error, not an authorization message
|
|
239
|
+
|
|
240
|
+
Found 2026-08-28 on the NYCHH `transfer-orders` view. The two mechanisms are individually sane and
|
|
241
|
+
compose into a MySQL 1054:
|
|
242
|
+
|
|
243
|
+
1. **`AclFieldPermissions` filters the SELECT column list, and an ungranted field is dropped
|
|
244
|
+
SILENTLY** โ no message, no `EZ-2`. (That is the same silent-drop behaviour as a missing
|
|
245
|
+
`AclRecordPermissions` grant deleting a *join*.)
|
|
246
|
+
2. **`_Model_Client_TableView::meta()` still emits `table.sort` naming that field's slug.** The sort
|
|
247
|
+
block (`TableView.php:343`) sits **OUTSIDE** the permitted-field branch, so it does not know the
|
|
248
|
+
column was just filtered away.
|
|
249
|
+
3. blox's `getDataTableData` then misses on `lookupTableViewFieldsBySlug`, **`console.warn`s โ and
|
|
250
|
+
pushes `"-" + undefined` anyway**, producing `ORDER BY TransferOrders.undefined DESC` โ
|
|
251
|
+
**MySQL 1054**, surfaced as a 500.
|
|
252
|
+
|
|
253
|
+
So the visible failure is a broken SQL query while the actual defect is one missing field grant, on
|
|
254
|
+
one column, in one tenant. **Any tenant missing a field grant on its view's default-sort column hits
|
|
255
|
+
this** โ it is not transfer-order-specific.
|
|
256
|
+
|
|
257
|
+
Two candidate fixes, neither applied yet (worth a ticket):
|
|
258
|
+
|
|
259
|
+
- **Backend:** emit `meta.table.sort` only for fields that survived the ACL filter.
|
|
260
|
+
- **Frontend (blox):** skip a sort whose slug does not resolve โ it *already* warns; it just pushes
|
|
261
|
+
the bad value regardless.
|
|
262
|
+
|
|
263
|
+
Until one lands, treat "table 500s with `ORDER BY <table>.undefined`" as a **field-ACL diagnosis**,
|
|
264
|
+
not a SQL-authoring one.
|
|
265
|
+
|
|
238
266
|
## Gotchas / known issues
|
|
239
267
|
|
|
240
268
|
- **Default-sort updates must JOIN `TableViewFields`, not subquery `TableViews`** โ a subquery
|
|
241
269
|
on the same view triggers MySQL error 1093. See *Default sort* above.
|
|
270
|
+
- **`ORDER BY <table>.undefined` (MySQL 1054) means a missing FIELD GRANT, not a bad migration.**
|
|
271
|
+
The default-sort column was dropped from the projection by `AclFieldPermissions` while
|
|
272
|
+
`meta.table.sort` kept naming it. See *An ACL gap on the default-sort column* above.
|
|
242
273
|
- **`recordFieldId` is cross-DB** โ it indexes `Core.RecordFields`, not anything in the client DB.
|
|
243
274
|
Resolve the id in `Core`, but insert the `TableViewFields` row in the client DB.
|
|
244
275
|
- **Don't assume `Core.RecordFields`/`Core.Records` ids are stable across environments** โ look them
|
|
@@ -255,6 +286,14 @@ SalesOrderListingSortByDateOrderDesc.sql`).
|
|
|
255
286
|
*and* that query โ and a grep for the property alone under-reports what a missing column breaks.
|
|
256
287
|
|
|
257
288
|
## Change history
|
|
289
|
+
- 2026-08-28 โ Recorded a **platform bug worth a ticket**: a missing `AclFieldPermissions` grant on a
|
|
290
|
+
view's **default-sort** column produces a 500 (MySQL 1054), not an authorization message. The ACL
|
|
291
|
+
filter drops the column from the SELECT silently, `TableView::meta()`'s sort block
|
|
292
|
+
(`TableView.php:343`) sits outside the permitted-field branch and still emits `table.sort` for it,
|
|
293
|
+
and blox's `getDataTableData` warns but pushes `"-" + undefined` anyway โ
|
|
294
|
+
`ORDER BY <table>.undefined`. Two candidate fixes noted (emit the sort only for surviving fields;
|
|
295
|
+
or have blox skip an unresolvable sort slug). Found on the NYCHH `transfer-orders` view.
|
|
296
|
+
(apeterson)
|
|
258
297
|
- 2026-08-28 โ Pinned **where `isGroupable` actually exists**: present on local and sandbox-client
|
|
259
298
|
(both hand-applied), **absent on production and beta**. So a `TableViewFields` INSERT naming the
|
|
260
299
|
column passes locally and fails on prod โ omit it from new TableView migrations until the fan-out
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
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
|
-
| [Re-runnable additive
|
|
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 | 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-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 |
|
|
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/Client_Compass/2026-08-28 - GrantViewOrdersRoleSalesOrdersNavigation.sql, dbchanges2/Core/2026-08-27a - Insert - Netsuite Location SyncAll CronJob.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 | 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, 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 |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Re-runnable additive
|
|
2
|
+
title: Re-runnable additive migrations (uuid4 in SQL, INSERT guards, the DISTINCT trap, conditional ALTERs)
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: dbchanges2
|
|
5
5
|
project: Database Changes
|
|
@@ -163,6 +163,43 @@ open-orders cron that reads the dimension it fills runs at 3:00 AM.
|
|
|
163
163
|
**Test a registration file twice against a throwaway local copy of the table**: the first run must
|
|
164
164
|
insert exactly one row, the second must insert zero and raise no error. That is the whole contract.
|
|
165
165
|
|
|
166
|
+
### Re-runnable ALTERs: MySQL 8 has no `ADD COLUMN IF NOT EXISTS`
|
|
167
|
+
|
|
168
|
+
A `Client/` file runs against **every tenant**, and tenants disagree about which columns they
|
|
169
|
+
already have โ so a bare `ALTER TABLE โฆ ADD COLUMN` breaks on the tenants that already have it and
|
|
170
|
+
aborts the rest of the file. MySQL 8 offers no `IF NOT EXISTS` for `ADD COLUMN` (MariaDB does; do
|
|
171
|
+
not copy MariaDB SQL here). Gate each ALTER on `information_schema` and run it through
|
|
172
|
+
`PREPARE`/`EXECUTE`, so it compiles to a harmless `DO 0` where the column already exists:
|
|
173
|
+
|
|
174
|
+
```sql
|
|
175
|
+
SET @sql = (
|
|
176
|
+
SELECT IF(
|
|
177
|
+
COUNT(*) = 0,
|
|
178
|
+
'ALTER TABLE TransferOrderItems ADD COLUMN dtCreated DATETIME NULL',
|
|
179
|
+
'DO 0'
|
|
180
|
+
)
|
|
181
|
+
FROM information_schema.COLUMNS
|
|
182
|
+
WHERE
|
|
183
|
+
TABLE_SCHEMA = DATABASE() AND
|
|
184
|
+
TABLE_NAME = 'TransferOrderItems' AND
|
|
185
|
+
COLUMN_NAME = 'dtCreated'
|
|
186
|
+
);
|
|
187
|
+
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- **`TABLE_SCHEMA = DATABASE()`** keeps it cluster-safe โ the file never names a database, so the
|
|
191
|
+
same statement is correct in every `Client_*` schema.
|
|
192
|
+
- Guard **indexes** the same way against `information_schema.STATISTICS` (and remember a composite
|
|
193
|
+
unique key is only recorded against its first column โ see
|
|
194
|
+
[tableview-field-metadata](../../api2/features/tableview-field-metadata.md)).
|
|
195
|
+
- The `PREPARE` shape is what makes the guard work at all: **DDL cannot sit inside an `IF`** in a
|
|
196
|
+
plain script, so the condition has to select the *statement text*, not branch around it.
|
|
197
|
+
|
|
198
|
+
Worked example: `Client/2026-08-28b - TransferOrderItemsTimestamps.sql` (adds
|
|
199
|
+
`TransferOrderItems.dtCreated`/`dtUpdated` + indexes, and a guarded `qtyCommitted`). **The existing
|
|
200
|
+
`Client/2026-08-18` `qtyCommitted` file uses a bare `ALTER` and is exactly the shape this avoids** โ
|
|
201
|
+
it is not the template to copy.
|
|
202
|
+
|
|
166
203
|
### `LAST_INSERT_ID()` into a session variable ties the whole file to one session
|
|
167
204
|
|
|
168
205
|
The common shape โ `INSERT` a parent, `SET @parentId = LAST_INSERT_ID();`, then insert children
|
|
@@ -201,6 +238,14 @@ concluding a migration is production-safe.
|
|
|
201
238
|
[dbchanges2 architecture](../architecture.md).
|
|
202
239
|
|
|
203
240
|
## Change history
|
|
241
|
+
- 2026-08-28 โ Added **conditional (re-runnable) ALTERs**: MySQL 8 has no
|
|
242
|
+
`ADD COLUMN IF NOT EXISTS`, so a `Client/` all-tenant file must gate each `ALTER` on
|
|
243
|
+
`information_schema` (with `TABLE_SCHEMA = DATABASE()` for cluster safety) and run it through
|
|
244
|
+
`PREPARE`/`EXECUTE`, compiling to `DO 0` where the column already exists โ DDL cannot sit inside an
|
|
245
|
+
`IF`, which is why the condition selects the statement *text*. From
|
|
246
|
+
`Client/2026-08-28b - TransferOrderItemsTimestamps.sql`; the existing `Client/2026-08-18`
|
|
247
|
+
`qtyCommitted` file's bare `ALTER` is the anti-pattern it replaces. Retitled the doc, since it now
|
|
248
|
+
covers re-runnable migrations generally rather than INSERTs only. (apeterson)
|
|
204
249
|
- 2026-08-28 โ Added the **local dry-run pattern for destructive/rebuild migrations**: replay the
|
|
205
250
|
file against a local copy inside `START TRANSACTION; โฆ ROLLBACK;`, then prove idempotence by
|
|
206
251
|
concatenating the file to itself and running both copies in one transaction. Used while writing
|
|
@@ -14,6 +14,7 @@ files:
|
|
|
14
14
|
- dbchanges2/Client_Compass/
|
|
15
15
|
- dbchanges2/Client_CompassCanada/
|
|
16
16
|
- dbchanges2/Client_Quad/
|
|
17
|
+
- dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql
|
|
17
18
|
- dbchanges2/Core/2026-08-24b - SalesOrderStatusFilterSurfaceSeed.sql
|
|
18
19
|
- dbchanges2/Client_Compass/2026-08-24b - SalesOrderStatusFilterHides.sql
|
|
19
20
|
- dbchanges2/Client_CompassCanada/2026-08-24b - SalesOrderStatusFilterHides.sql
|
|
@@ -181,6 +182,14 @@ it `isVisible: true`. Both `Core/2026-06-29a` (Item) and `Core/2026-06-29d` (Ven
|
|
|
181
182
|
off." A client opts a role in via a `SurfaceOverride` (`IS_VISIBLE=1`), scoped by role/persona. This is
|
|
182
183
|
the seed-authoring corollary of the Core neutral-default re-baseline recorded in the resolver doc.
|
|
183
184
|
|
|
185
|
+
**Neutral does NOT automatically mean OFF โ test (b), not the habit (2026-08-28).** The rule is
|
|
186
|
+
"universal **and** safe-if-silently-inherited", so a control that passes both seeds **ON**.
|
|
187
|
+
`Core/2026-08-28b` seeds the three `transfer-orders-list-actions` buttons (Refresh / Columns /
|
|
188
|
+
Transfer Order) **`isVisible = 1`**: a list screen with no refresh and no create is not a useful
|
|
189
|
+
neutral default, none of the three is an unsafe affordance to inherit, and only NYCHH can reach the
|
|
190
|
+
page at all. Contrast the record-modal **Edit** button, which fails (b) outright. State which of the
|
|
191
|
+
two tests you applied in the file header.
|
|
192
|
+
|
|
184
193
|
## Sales-order button overrides โ element identities & the id-agnostic authoring house style
|
|
185
194
|
|
|
186
195
|
When a client opts its roles into the sales-order action/filter buttons (the resolved per-client
|
|
@@ -589,7 +598,11 @@ See [sales-order-status-filter-surface](../../_underscore/features/sales-order-s
|
|
|
589
598
|
|
|
590
599
|
Blocks assigned by `Core/2026-08-28a - TransferOrderSurfaceSeed.sql`: **`Core.Messages` 227โ250**,
|
|
591
600
|
**`Core.Surfaces` 47โ51**, **`Core.SurfaceElements` 170โ186** (the `transfer-orders` nav element +
|
|
592
|
-
the LIST surface + 4 record-modal SECTION surfaces + the `transfer-order-stage` vocabulary)
|
|
601
|
+
the LIST surface + 4 record-modal SECTION surfaces + the `transfer-order-stage` vocabulary), then
|
|
602
|
+
**extended the same day** by `Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql` to
|
|
603
|
+
**`Messages` 227โ260**, **`Surfaces` 47โ52**, **`SurfaceElements` 170โ189** (the
|
|
604
|
+
`transfer-orders-list-actions` BUTTON_BAR + the shared columns-modal messages). Every id was
|
|
605
|
+
verified free on **local AND production** before authoring. See
|
|
593
606
|
[Transfer Orders page](../../toga25-supply/features/transfer-orders-page.md).
|
|
594
607
|
|
|
595
608
|
**โ The reserved blocks are not an inventory of every surface id โ pre-2026-08-21 surfaces sit
|
|
@@ -905,6 +918,41 @@ next reserved block, and a second confirmation of the "verify the block is free
|
|
|
905
918
|
environment, not just yours" rule from the 2026-08-24 incident โ sandbox-client's lower ceiling would
|
|
906
919
|
have made 45โ46 look free.
|
|
907
920
|
|
|
921
|
+
### SHARED (`common.*`) message keys โ guard on `messageKey`, not on `id`
|
|
922
|
+
|
|
923
|
+
Copy that belongs to a **generic component** rather than to one record type is seeded under a
|
|
924
|
+
**shared `common.*` key**, alongside the existing `common.yes` / `common.no` / `common.active` /
|
|
925
|
+
`common.inactive`. `Core/2026-08-28b` moved the seven column-visibility modal strings to
|
|
926
|
+
**`common.columns.modal.*`** for exactly this reason: a tenant then translates them **once for every
|
|
927
|
+
list screen** instead of once per record type. (The older per-record
|
|
928
|
+
`items.columns.modal.*` / `salesOrder.columns.modal.*` keys are left alone โ folding those three
|
|
929
|
+
shipped screens onto the common keys is a clean follow-up.)
|
|
930
|
+
|
|
931
|
+
**The guard changes with the key's ownership.** A record-scoped seed can be guarded on its literal
|
|
932
|
+
reserved `id`; a **shared** key cannot, because another seed may legitimately create it first, at a
|
|
933
|
+
different id. So guard a `common.*` insert with `NOT EXISTS (โฆ WHERE messageKey = 'โฆ')` โ the
|
|
934
|
+
*semantic* key โ and accept that the row may end up outside your reserved block.
|
|
935
|
+
|
|
936
|
+
### A partial-migration cleanup: fix the ENVIRONMENT, not the migration file
|
|
937
|
+
|
|
938
|
+
When a developer has already run an **earlier revision** of a seed, re-running the corrected file
|
|
939
|
+
hits `Duplicate entry '251' for key 'messages.PRIMARY'`. The resolution that keeps the repo honest:
|
|
940
|
+
|
|
941
|
+
- **Leave the migration file UNCHANGED.** It is correct for a fresh environment, and prod/sandbox
|
|
942
|
+
never ran it. Editing it to dodge one developer's local state corrupts the record of intent (see
|
|
943
|
+
the 2.0 standard: *migrations record intent, not deployed state*).
|
|
944
|
+
- **Hand over a scratchpad-only undo** โ never a committed file, because the executor runs every
|
|
945
|
+
folder against production.
|
|
946
|
+
- **Delete BY UUID, never by id range.** Per the 2026-08-24 incident, an id-range cleanup nearly
|
|
947
|
+
took the legitimate deny-action toast rows with it.
|
|
948
|
+
- **Delete in FK order: `SurfaceElements` โ `Surfaces` โ `Messages`.** Both
|
|
949
|
+
`SurfaceElements.labelMessageId` and `SurfaceElements.surfaceId` are `RESTRICT`.
|
|
950
|
+
|
|
951
|
+
> โ **`SurfaceOverrides.surfaceElementId` is a SOFT FK โ no constraint.** So an override written
|
|
952
|
+
> against a deleted element **survives the delete** and silently re-attaches to whatever row takes
|
|
953
|
+
> that id next. This is the same failure shape as the NYCHH inventory-topology landmine above:
|
|
954
|
+
> sweep the overrides yourself when you delete elements; the database will not.
|
|
955
|
+
|
|
908
956
|
## A new-vertical Core seed: two authoring rules confirmed on the transfer-order build (2026-08-28)
|
|
909
957
|
|
|
910
958
|
- **A TABLE surface names its view by SLUG, never by `tableViewId`.** `Core/2026-08-28a` seeds
|
|
@@ -922,8 +970,28 @@ have made 45โ46 look free.
|
|
|
922
970
|
the same distinction). โ Any `Client/` file writing to `ThemeTokens` inherits a dependency on
|
|
923
971
|
`Client/2026-06-25a - SurfaceClientTables.sql` having run in that schema โ audit the tenant list
|
|
924
972
|
before a prod run.
|
|
973
|
+
- **A BUTTON_BAR element needs an `Actions` row only when it needs a REGISTRY HANDLER.** The three
|
|
974
|
+
`transfer-orders-list-actions` elements seed with **no `Actions` rows at all**: with nothing
|
|
975
|
+
registered in the FE `actionRegistry`, `SurfaceActionBar` falls through to
|
|
976
|
+
`onDispatch(config.valueKey)`, which is precisely the host-owned behaviour Refresh / Columns /
|
|
977
|
+
Create want. Seed an `Action` when the *behaviour* lives in the registry; seed a bare
|
|
978
|
+
`config.valueKey` when it lives in the page.
|
|
925
979
|
|
|
926
980
|
## Change history
|
|
981
|
+
- 2026-08-28 โ **Extended the transfer-order reserved blocks** to `Messages` **227โ260**, `Surfaces`
|
|
982
|
+
**47โ52**, `SurfaceElements` **170โ189** (`Core/2026-08-28b`, the `transfer-orders-list-actions`
|
|
983
|
+
BUTTON_BAR; all ids verified free on local **and** production first). Three authoring rules added:
|
|
984
|
+
a **neutral default can be ON** โ apply the safe-if-inherited test rather than the seed-OFF habit
|
|
985
|
+
(the three list-action buttons seed `isVisible=1`; a record-modal Edit button still seeds off);
|
|
986
|
+
a BUTTON_BAR element needs an **`Actions` row only when it needs a registry handler**, otherwise a
|
|
987
|
+
bare `config.valueKey` falls through to `onDispatch`; and **shared `common.*` Messages must be
|
|
988
|
+
guarded on `messageKey`, not id**, since another seed may create the key first at a different id
|
|
989
|
+
(the columns-modal copy moved to `common.columns.modal.*` so a tenant translates it once for every
|
|
990
|
+
list screen). Also recorded the **partial-migration cleanup** procedure โ leave the migration file
|
|
991
|
+
unchanged, hand over a scratchpad-only undo, delete **by uuid** in FK order
|
|
992
|
+
(`SurfaceElements` โ `Surfaces` โ `Messages`, both element FKs are RESTRICT) โ and that
|
|
993
|
+
**`SurfaceOverrides.surfaceElementId` is a soft FK with no constraint**, so an override outlives a
|
|
994
|
+
deleted element and silently re-attaches to whatever takes that id next. (apeterson)
|
|
927
995
|
- 2026-08-28 โ Registered the **transfer-order reserved blocks** (`Messages` 227โ250, `Surfaces`
|
|
928
996
|
47โ51, `SurfaceElements` 170โ186, `Core/2026-08-28a`) and recorded the read-only ceiling snapshot
|
|
929
997
|
taken before reserving them: local **and** production identical at 46/169/226 while sandbox-client
|
|
@@ -84,6 +84,15 @@ Order matters โ each step assumes the prior one ran.
|
|
|
84
84
|
- **Drift before consolidation** โ loose `Client/` files must already be applied to all existing
|
|
85
85
|
clients before they're consolidated/archived (otherwise the new blank diverges from live
|
|
86
86
|
clients). See the dbchanges2 architecture doc.
|
|
87
|
+
- **๐จ The blank can be WRONG, not just behind โ an open example (2026-08-28).**
|
|
88
|
+
`Client/2026-06-03- BLANK_CLIENT_DATABASE.sql` creates `TransferOrderItems` (`CREATE` at ~line
|
|
89
|
+
9462) **without `dtCreated`/`dtUpdated`**, but `_Model_Client_TransferOrderItem` declares them as
|
|
90
|
+
`FIELD_DATETIME_CREATED`/`FIELD_DATETIME_UPDATED`, so `_Model` puts them in **every SELECT it
|
|
91
|
+
builds**. **Every tenant provisioned from this blank therefore has a table the model cannot
|
|
92
|
+
read** โ nothing in a request has to ask for the columns. `Client/2026-08-28b -
|
|
93
|
+
TransferOrderItemsTimestamps.sql` repairs existing tenants; **the blank itself is still unfixed**,
|
|
94
|
+
so the next onboarding reproduces it. When a model-vs-blank mismatch like this is found, fix
|
|
95
|
+
**both**: a catch-up `Client/` file for live tenants *and* the blank.
|
|
87
96
|
|
|
88
97
|
## Gotchas
|
|
89
98
|
|
|
@@ -100,6 +109,12 @@ Order matters โ each step assumes the prior one ran.
|
|
|
100
109
|
|
|
101
110
|
## Change history
|
|
102
111
|
|
|
112
|
+
- 2026-08-28 โ Recorded an **open blank-template defect**: `TransferOrderItems` is created without
|
|
113
|
+
the `dtCreated`/`dtUpdated` columns the model declares as `FIELD_DATETIME_CREATED/UPDATED` and
|
|
114
|
+
therefore selects on every read, so every tenant built from the blank ships a table the model
|
|
115
|
+
cannot read. Live tenants are repaired by `Client/2026-08-28b`; the blank is still unfixed. Added
|
|
116
|
+
the general rule: a model-vs-blank mismatch needs **both** a catch-up `Client/` file and a blank
|
|
117
|
+
fix. (apeterson)
|
|
103
118
|
- 2026-07-20 โ Noted that a local browser wizard now automates steps 2โ9 and the dbchanges2 blank
|
|
104
119
|
consolidation (TRUE-79864). (mhammontree)
|
|
105
120
|
- 2026-06-23 โ Initial capture from the Fordham onboarding (TRUE-79702). (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-28
|
|
10
10
|
owners: ["bala", "mhammontree", "apeterson"]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Client_Growrk/2026-05-28.sql
|
|
@@ -101,6 +101,33 @@ part, and it is what makes a missing column fatal. Standard, non-`c_` fields use
|
|
|
101
101
|
`Core.RecordFields` + `AclFieldPermissions` instead; see
|
|
102
102
|
[ACL permission chain](../../_underscore/features/acl-permission-chain.md).)
|
|
103
103
|
|
|
104
|
+
## The drift can run the OTHER way: prod has state the repo never created
|
|
105
|
+
|
|
106
|
+
Everything above assumes the client DB is **behind** the repo. The mirror case is just as common and
|
|
107
|
+
harder to see: **production carries hand-applied state that no migration file creates**, so the
|
|
108
|
+
*fresh* environment is the broken one.
|
|
109
|
+
|
|
110
|
+
Worked example (NYCHH transfer orders, 2026-08-28) โ four layers, all applied by hand when the
|
|
111
|
+
NetSuite TransferOrder POST was enabled, none of them in `dbchanges2`: `AclRecordPermissions` for
|
|
112
|
+
records 312/313, their `AclLogicGroups`/`AclLogicGroupExpressions`/`AclRecordExpressions` chain,
|
|
113
|
+
`AclFieldPermissions` for all 17 of their `RecordFields`, and the
|
|
114
|
+
`TransferOrderItems.dtCreated`/`dtUpdated` columns. Local had none of it and **failed one layer at a
|
|
115
|
+
time, each with a completely different-looking error**.
|
|
116
|
+
|
|
117
|
+
Three things this changes about the audit:
|
|
118
|
+
|
|
119
|
+
- **ACL rows are drift too, not just columns.** An `information_schema` diff will never find a
|
|
120
|
+
missing `AclFieldPermissions` row. Diff the ACL tables for the records in scope as well.
|
|
121
|
+
- **Diff the WHOLE feature area up front rather than chasing one 500 at a time.** One systematic
|
|
122
|
+
local-vs-prod diff of every table (and ACL row) the feature touches is cheaper than four
|
|
123
|
+
sequential debugging sessions, and it is the only way to find layers that have not failed *yet*.
|
|
124
|
+
- **The blast radius is never just the screen you are on.** The same missing rows meant the NetSuite
|
|
125
|
+
transfer-order **importer** could not be stood up in a fresh environment either.
|
|
126
|
+
|
|
127
|
+
The repair is a **faithful reproduction** file โ same roles, same permission bits as prod, no
|
|
128
|
+
widening โ anti-joined on natural keys so it is a no-op where the state already exists. Detail:
|
|
129
|
+
[NYCHH transfer-order inventory & quantities](../../../../clients/nychh/features/transfer-order-inventory-quantities.md).
|
|
130
|
+
|
|
104
131
|
## Steps
|
|
105
132
|
|
|
106
133
|
1. **Get the real exception first.** Resolve `error.id` from the response envelope against
|
|
@@ -162,6 +189,15 @@ part, and it is what makes a missing column fatal. Standard, non-`c_` fields use
|
|
|
162
189
|
|
|
163
190
|
## Change history
|
|
164
191
|
|
|
192
|
+
- 2026-08-28 โ Added the **inverse drift direction**: production carrying hand-applied state the repo
|
|
193
|
+
never created, so the *fresh* environment is the broken one. From the NYCHH transfer-order case โ
|
|
194
|
+
four untracked layers (record ACL for 312/313, their logic-group/expression chain, field ACL for
|
|
195
|
+
all 17 RecordFields, and two `TransferOrderItems` timestamp columns) that failed locally one at a
|
|
196
|
+
time with different-looking errors. Durable additions: **ACL rows are drift too** and an
|
|
197
|
+
`information_schema` diff will never see them; **diff the whole feature area up front** instead of
|
|
198
|
+
chasing sequential 500s; the blast radius extends past the UI (the NetSuite importer was equally
|
|
199
|
+
unbuildable); and the repair is a **faithful reproduction** โ same roles, same bits, no widening,
|
|
200
|
+
anti-joined so it no-ops where the state exists. (apeterson)
|
|
165
201
|
- 2026-08-27 โ Added a pointer to the **third drift direction**: when the 1054 starts right after a
|
|
166
202
|
deploy, is not client-specific, and the column exists in no environment, the **framework branch is
|
|
167
203
|
ahead of schema** and the repair is a branch merge, not a catch-up file โ see
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| [AdvancedSelect (virtualized single/multi select)](features/advanced-select.md) | `AdvancedSelect<T>` is a **fully controlled, virtualized** single/multi select built from scratch (not react-select) on **`@tanstack/react-virtual`**, for large | toga-blox/src/components/AdvancedSelect/AdvancedSelect.tsx, toga-blox/src/components/AdvancedSelect/AdvancedSelect.types.ts, toga-blox/src/components/AdvancedSelect/AdvancedSelect.module.css |
|
|
8
8
|
| [API client (axios wrapper, auth, table-data fetchers)](features/api-client.md) | `src/api/` is a thin **axios** wrapper that standardizes the **2.0 API envelope**, manages auth (Bearer + refresh), serializes complex query options, and provid | toga-blox/src/reactQuery/queryHelpers.ts, toga-blox/src/api/index.ts, toga-blox/src/api/axiosInstance.ts, toga-blox/src/api/apiFunctions.ts, toga-blox/src/api/auth.ts, toga-blox/src/api/genericApi.ts, toga-blox/src/api/types.ts, toga-blox/src/api/tableData |
|
|
9
9
|
| [BaseInput (react-hook-form field factory)](features/base-input.md) | `BaseInput` is a **form-field factory** driven by react-hook-form. | toga-blox/src/components/BaseInput/BaseInput.tsx, toga-blox/src/components/BaseInput/BaseInput.types.ts, toga-blox/src/components/BaseInput/BaseInput.module.css, toga-blox/src/components/BaseInput/components |
|
|
10
|
-
| [blox authoring defect backlog (fix IN blox, not around it)](features/blox-authoring-defects.md) | The standing list of **defects to fix inside `@agilant/toga-blox`**. | toga-blox/src/api/auth.ts, toga-blox/src/api/axiosInstance.ts, toga-blox/src/reactQuery/queryHelpers.ts, toga-blox/src/components/BaseInput, toga-blox/tsconfig.json, toga-blox/package.json |
|
|
10
|
+
| [blox authoring defect backlog (fix IN blox, not around it)](features/blox-authoring-defects.md) | The standing list of **defects to fix inside `@agilant/toga-blox`**. | toga-blox/src/components/BaseButton, toga-blox/src/api/auth.ts, toga-blox/src/api/axiosInstance.ts, toga-blox/src/reactQuery/queryHelpers.ts, toga-blox/src/components/BaseInput, toga-blox/tsconfig.json, toga-blox/package.json |
|
|
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, 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 |
|
|
@@ -15,4 +15,4 @@
|
|
|
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 |
|
|
18
|
-
| [Local-linking an unpublished blox into a consumer app (incl. a React 19 / Next host)](workflows/local-link-into-a-consumer-app.md) | How to develop a **blox component that is not published yet** against a real consuming app, on Windows, including the hard case: a **React 19 / Next.js (Turbopa | toga-blox/package.json, toga-blox/postcss.config.cjs |
|
|
18
|
+
| [Local-linking an unpublished blox into a consumer app (incl. a React 19 / Next host)](workflows/local-link-into-a-consumer-app.md) | How to develop a **blox component that is not published yet** against a real consuming app, on Windows, including the hard case: a **React 19 / Next.js (Turbopa | toga-blox/package.json, toga-blox/postcss.config.cjs, toga25-supply/vite.config.ts |
|
|
@@ -6,9 +6,10 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-28
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
|
+
- toga-blox/src/components/BaseButton
|
|
12
13
|
- toga-blox/src/api/auth.ts
|
|
13
14
|
- toga-blox/src/api/axiosInstance.ts
|
|
14
15
|
- toga-blox/src/reactQuery/queryHelpers.ts
|
|
@@ -89,6 +90,24 @@ mechanism), and a **fix direction** (a suggestion, not a decision). Every one of
|
|
|
89
90
|
today's `"/"` for compatibility), or have `performLogout` simply not navigate and let the caller's
|
|
90
91
|
`onLogout` own it.
|
|
91
92
|
|
|
93
|
+
## Styling defects
|
|
94
|
+
|
|
95
|
+
### 4. `BaseButton` โ `primary` and `primaryAction` are NOT interchangeable variants
|
|
96
|
+
|
|
97
|
+
**Mechanism.** The two class names look like a naming pair but carry different property sets:
|
|
98
|
+
`.primaryBtnAction` sets a **font-size** (default 14px) **and** `height: 26px`; `.primaryBtn` sets
|
|
99
|
+
**neither** โ it inherits font-size from context and has no height at all.
|
|
100
|
+
|
|
101
|
+
**Consumer symptom.** Switching a button between the two variants โ the obvious move when a surface
|
|
102
|
+
element is seeded `PRIMARY` โ silently changes **type size and button height**, with nothing in the
|
|
103
|
+
prop name suggesting geometry is involved. It reads as a CSS bug in the consuming app.
|
|
104
|
+
|
|
105
|
+
**Fix direction.** Make the two variants differ in **colour only**, moving the shared size/height
|
|
106
|
+
declarations to the base button class. Until then a consumer must compensate at the instance โ
|
|
107
|
+
`toga25-supply`'s `SurfaceActionBar` variant map pins `fontSize: 14px` for `primary` (see
|
|
108
|
+
[surface-frontend](../../toga25-supply/features/surface-frontend.md)). **Do not patch the shared blox
|
|
109
|
+
stylesheet from a consumer**: every one of the ~40 call sites inherits it.
|
|
110
|
+
|
|
92
111
|
## Already-known authoring items (carried over from ยง22)
|
|
93
112
|
|
|
94
113
|
These are long-standing and were previously recorded only as consumer-side advice:
|
|
@@ -122,6 +141,11 @@ These are long-standing and were previously recorded only as consumer-side advic
|
|
|
122
141
|
time**, with no PR. Sequence auth fixes deliberately.
|
|
123
142
|
|
|
124
143
|
## Change history
|
|
144
|
+
- 2026-08-28 โ Added styling defect **4: `BaseButton` `primary` vs `primaryAction` are not
|
|
145
|
+
interchangeable** โ `.primaryBtnAction` sets font-size **and** `height: 26px`, `.primaryBtn` sets
|
|
146
|
+
neither, so swapping variants silently changes type size and button height. Found wiring the
|
|
147
|
+
`transfer-orders-list-actions` PRIMARY button in toga25-supply; compensated at the instance (the
|
|
148
|
+
`SurfaceActionBar` variant map pins 14px) rather than by editing the shared stylesheet. (apeterson)
|
|
125
149
|
- 2026-08-27 โ Created the doc ยง22 had been pointing at since 2026-08-18. Seeded with the three auth
|
|
126
150
|
defects found while debugging toga25-supply SSO (`handleClientAuthentication`'s misleading name +
|
|
127
151
|
untyped return, `fetchPublicToken` persisting the public token to `localStorage`,
|
|
@@ -7,10 +7,11 @@ client: shared
|
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
9
|
updated: 2026-08-28
|
|
10
|
-
owners: [tcox]
|
|
10
|
+
owners: [tcox, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/package.json
|
|
13
13
|
- toga-blox/postcss.config.cjs
|
|
14
|
+
- toga25-supply/vite.config.ts
|
|
14
15
|
related:
|
|
15
16
|
- ./dynamic-publish-pipeline.md
|
|
16
17
|
- ../features/talos-assistant.md
|
|
@@ -59,6 +60,35 @@ Result: exactly one React in the graph. **framer-motion 11 and Pro FontAwesome b
|
|
|
59
60
|
React 19** โ the `^18` peer range is a declaration, not a runtime wall (a published consumer
|
|
60
61
|
still needs the peer range widened).
|
|
61
62
|
|
|
63
|
+
### 2b. Vite consumers: dedupe EVERY peer that carries a React context โ not just React
|
|
64
|
+
|
|
65
|
+
**The rule: any peer dep that provides a React context or hooks must be in `resolve.dedupe` when
|
|
66
|
+
blox is consumed through a symlink.** It is not enough that blox declares the peer correctly โ Vite
|
|
67
|
+
resolves a symlinked package's imports **relative to the link target**, so blox's hooks pick up
|
|
68
|
+
whatever sits in `toga-blox-npm/node_modules`, and you end up with two copies of the library.
|
|
69
|
+
|
|
70
|
+
Worked example (toga25-supply, 2026-08-28) โ **"No QueryClient set, use QueryClientProvider" with
|
|
71
|
+
`QueryClientProvider` plainly visible in the component stack.** Two copies of
|
|
72
|
+
`@tanstack/react-query`: the app's provider wrote one React context while blox's hooks
|
|
73
|
+
(`useFetchTablePageMeta`, `useTableData`) read another. `vite.config.ts` deduped
|
|
74
|
+
`react` / `react-dom` / `@tanstack/react-table` but **not react-query**.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
dedupe: [
|
|
78
|
+
"react", "react-dom", "@tanstack/react-table",
|
|
79
|
+
"@tanstack/react-query", // โ the fix
|
|
80
|
+
"@tanstack/react-query-persist-client",
|
|
81
|
+
"@tanstack/react-query-devtools",
|
|
82
|
+
]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- **Editing `dedupe` is not enough on its own โ `rm -rf node_modules/.vite` and hard-reload.** The
|
|
86
|
+
duplicate is baked into Vite's **pre-bundle**, so the old graph is served until the cache is
|
|
87
|
+
cleared.
|
|
88
|
+
- **This only reproduces on machines using the local checkout**, so across a team it reads as an
|
|
89
|
+
*intermittent* bug that "works for everyone else." A `QueryClient`/context error with the provider
|
|
90
|
+
visibly mounted is almost always this.
|
|
91
|
+
|
|
62
92
|
### 3. Disable blox's PostCSS config while linked
|
|
63
93
|
|
|
64
94
|
Rename `toga-blox-npm/postcss.config.cjs` to `postcss.config.cjs.off` for the duration.
|
|
@@ -89,6 +119,13 @@ The consumer reads blox's `dist`, so rebuild blox after every change.
|
|
|
89
119
|
drags every component's CSS into the consumer bundle.
|
|
90
120
|
|
|
91
121
|
## Change history
|
|
122
|
+
- 2026-08-28 โ Added the Vite half of the one-copy rule: **every peer carrying a React context must
|
|
123
|
+
be in `resolve.dedupe`**, not just React. `toga25-supply` threw "No QueryClient set" **with
|
|
124
|
+
`QueryClientProvider` visibly in the stack** because `@tanstack/react-query` was missing from
|
|
125
|
+
`dedupe` while `react-table` was there โ the app's provider and blox's hooks held different
|
|
126
|
+
contexts. Fix = add the react-query family to `dedupe` **and** `rm -rf node_modules/.vite` (the
|
|
127
|
+
duplicate is baked into the pre-bundle). Reproduces only on machines using the local checkout, so
|
|
128
|
+
it presents as intermittent across a team. (apeterson)
|
|
92
129
|
- 2026-08-27 โ Initial doc. Recorded the procedure proven while wiring blox `Talos` into `ai-bdr`
|
|
93
130
|
from an unpublished branch: junction the checkout in, junction the host's React 19 over blox's
|
|
94
131
|
bundled React 18 (keeping `.r18` originals) so there is one React, and rename blox's Tailwind
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| [Action-Button Rule Engine (Flag / Rule grammar)](features/action-button-rule-engine.md) | A declarative, fully config-driven rule engine that resolves the boolean-ish flags (`isEnabled`, `isVisible`, `isComplete`) on SalesOrder action-button options. | toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
7
7
|
| [Client API-Fetch Fields (useClientApiFields / apiFields.json)](features/client-api-fetch-fields.md) | The mechanism that resolves a client's **API-fetch projection** โ which `fields` / `ojoin` / `join` / `where` to request from the TOGa API for a given page โ fr | toga25-supply/src/fieldsConfig/resolveApiConfig.ts, toga25-supply/src/fieldsConfig/useClientApiFields.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx |
|
|
8
8
|
| [Client-Configurable Fields (useClientFields / fieldsConfig)](features/client-configurable-fields.md) | The mechanism for config that **varies by client** (or client ร role) โ field overrides, filter buttons, group-by options, column pickers, layout toggles โ with | toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/fieldsConfig/useClientFields.ts, toga25-supply/src/pages/SalesOrders/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/README.md, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/layout/VendorItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/ |
|
|
9
|
-
| [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx |
|
|
9
|
+
| [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql |
|
|
10
10
|
| [Force Logout on Deployment (useDeploymentGuard)](features/force-logout-on-deployment.md) | On large deployments the backend bumps the Core parameter `META_LAST_REFRESH_DATETIME`. | toga25-supply/src/hooks/useDeploymentGuard.tsx, toga25-supply/src/App.tsx |
|
|
11
11
|
| [Meta-Driven Page & Table Setup](features/meta-driven-table-data.md) | A page in this app is **meta-driven end to end**: the page view model fetches *page meta* (labels, sections, ACL) and *table meta* (the columns/fields + table s | toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/TransferOrders/viewModel/useTransferOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableState.ts, toga25-supply/src/hooks/useTablePageMeta.ts, toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts, toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts, toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts, toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts |
|
|
12
12
|
| [Record Modals & Nested Tables](features/record-modals-and-nested-tables.md) | The repo's family of modal + nested-table patterns layered over toga-blox `TableRecordModal` and `PrimaryTable*Layout`. | toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/hooks/usePurchaseOrderDetails.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/layout/PrimaryTableServerLayout/PrimaryTableServerLayout.tsx, toga25-supply/src/layout/PrimaryTableServerLayout/types.ts, toga25-supply/src/layout/ItemRecordModalLayout/, toga25-supply/src/layout/SalesOrderRecordModalLayout/, toga25-supply/src/layout/SalesOrderItemsTableLayout/, toga25-supply/src/layout/ItemFulfillmentModal/, toga25-supply/src/layout/GenericNestedTables/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/handleFormatApprovalWorkflowPayload.ts, toga25-supply/src/layout/RecordApprovalModal/api/approvalDecisionsApi.ts |
|
|
@@ -14,7 +14,7 @@
|
|
|
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/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
16
|
| [Talos Integration (AppLayout host + adapter wiring)](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/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
|
-
| [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/routes.tsx, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql |
|
|
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 |
|
|
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/useTransferableItemRows.ts, 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
|
+
| [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 |
|
|
20
20
|
| [Porting a page (or query) from toga2-supply to toga25-supply](workflows/porting-a-page-from-toga2-supply.md) | `toga25-supply` re-implements pages that already exist in `toga2-supply`. | toga25-supply/src/pages/SalesOrders/viewModel/FIELDS/PRUDENTIAL/exportApiFields.json, toga25-supply/src/pages/SalesOrders/api/prudentialExportApi.ts, toga2-supply/src/pages/Orders/api/OrdersApi.ts, toga2-supply/src/components/ui/Tables/hooks/useExportableData.tsx |
|