toga-ai 1.0.883 → 1.0.884

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.
@@ -6,8 +6,8 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-16
10
- owners: [tcox, bala, jcardinal]
9
+ updated: 2026-09-24
10
+ owners: [tcox, bala, jcardinal, apeterson]
11
11
  files:
12
12
  - _underscore/Model/Core/Page.php
13
13
  - _underscore/Model/Client/TableView.php
@@ -62,6 +62,18 @@ For a **joined** column, a `ClientRecordFieldSettings` row with a null `contextR
62
62
 
63
63
  **Check which page slug the frontend ACTUALLY requests.** Every label and ACL lookup is filtered on the page's `Core.PageRecords`, so the records must be attached to **the page the frontend asks for** — not always the page you built. Elite's `service-requests` view is requested as **`meta?page=sales-orders-listing`** (Elite reuses that page), so records 99 (Tickets) and 36 (ServiceRequestTypes) had to be attached to `sales-orders-listing`, **not** only to the new `service-requests-listing` page. Attaching them to the new page alone changed nothing. **Read the Network tab first** — mis-diagnosed twice before the actual request URL settled it. A missing header is a *page-attachment* question, and only the real request identifies the page.
64
64
 
65
+ **The Core default label chain (verified against production Core, 2026-09-24).** For a base (non-custom, context-free) field the whole chain is Core-only:
66
+
67
+ ```
68
+ Pages --< PageRecords >-- Records --< RecordFields
69
+ |
70
+ DefaultRecordFieldSettings (settingId = 'label-singular')
71
+ ```
72
+
73
+ `Core.Settings` id **5** = `label-singular`; `Core.Apps` id **1** = `supply`. Client/Persona/User/Language `RecordFieldSettings` only *override* this; the Core default already labels the field for **every** tenant. So seeding headers for a new page is **one `dbchanges2` `Core/` file** — no per-tenant fan-out and no cross-cluster split. (Worked example: `Core/2026-09-24a - BundlesListingPageMeta.sql`.)
74
+
75
+ **`Sections` are a different path — and listing pages have none.** `Sections` / `SectionRecordFields` / `ClientSectionRecordFieldSettings` do not feed table column labels. In prod and sandbox **every** `*-listing` page has **0** `Sections` rows; only `*-view` pages (`items-view`, `sales-orders-view`) have any. "This page has no Sections" is normal and is never the reason a header is missing.
76
+
65
77
  **The `Core.PageRecords` gate.** `Page.php` assembles `settings.fields` from an **INNER JOIN** on `Core.RecordFields` (~L652) gated by **`Core.PageRecords`**: only fields of records *attached to the page* are emitted. A correct settings row does nothing until **both** the labelled field's record **and** the context record are attached to the page. Sequencing:
66
78
  1. **The `isset()` guard in `Page.php` (below) goes first.** Until it ships, adding context rows is actively dangerous — a context row fatals **every** page that contains the labelled field's record but not the context record.
67
79
  2. Then the `Core.PageRecords` attachments.
@@ -77,6 +89,7 @@ For a **joined** column, a `ClientRecordFieldSettings` row with a null `contextR
77
89
 
78
90
  ## Gotchas
79
91
 
92
+ - **Missing headers: check `PageRecords` + `DefaultRecordFieldSettings`, never `Sections`.** Listing pages have zero Sections by design.
80
93
  - **A page-meta 500 is not necessarily about that page.** Trace it to the offending `ClientRecordFieldSettings` row via the array key in the message (it is a `Core.RecordFields` id), not via the slug you requested.
81
94
  - **Error-handler strictness is a deploy-time behavior change.** Warnings survivable for years become 500s the moment the strict handler ships; expect a cluster of "new" failures in old code after such a deploy.
82
95
  - Do not "fix" this by deleting the client's settings row — that removes a real client customization. The guard belongs in the framework.
@@ -87,3 +100,6 @@ For a **joined** column, a `ClientRecordFieldSettings` row with a null `contextR
87
100
  - [TableView field/column metadata](../../api2/features/tableview-field-metadata.md)
88
101
  - [Error reporting (Issue/Event)](error-reporting-issue-event.md)
89
102
  - [Elite supply2 TableView config drift](../../../clients/elite/features/supply2-tableview-config-drift.md)
103
+
104
+ ## Change history
105
+ - 2026-09-24 — Documented the Core-only label chain (`Pages`→`PageRecords`→`RecordFields`→`DefaultRecordFieldSettings` `label-singular`, setting id 5) and that `Sections` are a separate path listing pages never have. (apeterson)
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-23
9
+ updated: 2026-09-24
10
10
  owners: [apeterson]
11
11
  files:
12
12
  - toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx
@@ -16,7 +16,8 @@ files:
16
16
  - toga25-supply/src/pages/Bundles/viewModel/useBundlesPageViewModel.tsx
17
17
  - toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts
18
18
  - toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts
19
- - toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts
19
+ - toga-blox-npm/dist/components/Table/hooks/useAssignTableFieldLabels.js
20
+ - _underscore/Model/Core/Page.php
20
21
  - toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts
21
22
  related:
22
23
  - ../architecture.md
@@ -79,17 +80,39 @@ The view model then returns `salesOrdersTableMeta` (after column-visibility adju
79
80
  - **Table slug** (`tableState.slug`) identifies the *table* and is used for **both** the table-meta fetch (step 3) and the table-data fetch (step 5) — they must match so the columns and the rows describe the same table. In Sales Orders this slug is derived from the route pathname via `useServerTableUrlState`, not read out of `pageMeta`.
80
81
  - `useAssignTableFieldLabels` is the bridge: it overlays the page meta's per-field labels (`pageMeta.settings.fields[table][field].label`) onto the table meta's fields, so page-scoped copy wins without re-fetching.
81
82
 
82
- ### 🚨 There is NO label in the table meta — a page with no page-meta record shows raw slugs
83
+ ### 🚨 There is NO label in the table meta — labels come from Core page meta
83
84
 
84
- `_Model_Client_TableView::meta()` emits, per field: `slug`, `settings{table, field, contextTable, contextField}`, `tableViewField`, `type`, `precision` and the `is*` flags — **and no `label`.** Every column header in this app is therefore supplied by the *page* meta and grafted on in step 4.
85
+ `_Model_Client_TableView::meta()` emits, per field: `slug`, `settings{table, field, contextTable, contextField}`, `tableViewField`, `type`, `precision` and the `is*` flags — **and no `label`.** `Core.TableViewFields` has no label column at all. Every column header in this app is therefore supplied by the *page* meta and grafted on in step 4.
85
86
 
86
- `useAssignTableFieldLabels` bails at **`if (!pageFields) return table`** when `pageMeta` is null. So **any list screen built without a legacy `<slug>-listing` page-meta record renders raw camelCase slugs** — and not only in the grid: the **Columns-visibility modal** reads the same merged meta, so the two stay consistently wrong. This is a general platform trap for every new page, not a per-screen bug. (`toga2-supply`'s equivalent fallback and the joined-column context rules are in [page-meta-context-field-settings](../../_underscore/features/page-meta-context-field-settings.md).)
87
+ **The label chain (verified against production Core, 2026-09-24):**
88
+
89
+ ```
90
+ Pages --< PageRecords >-- Records --< RecordFields
91
+ |
92
+ DefaultRecordFieldSettings (settingId = 'label-singular')
93
+ ```
94
+
95
+ - `_Model_Core_Page::meta()` (`_underscore/Model/Core/Page.php`, the "fields settings" block ~L640-745) scopes which `RecordFields` a page exposes through **`PageRecords`** — the join table, **not** `Sections`.
96
+ - The label value is `Core.DefaultRecordFieldSettings` where `settingId` = the `label-singular` setting (`Core.Settings` id **5**; `Core.Apps` id **1** = `supply`). Per-tenant overrides layer on top via `Client/Persona/User/LanguageRecordFieldSettings` in the client DB, but the **Core default carries the label for every tenant**.
97
+ - **So seeding labels is a Core-only change** — one `dbchanges2` `Core/` file, no per-tenant fan-out, no cross-cluster split.
98
+ - `Sections` / `SectionRecordFields` / `ClientSectionRecordFieldSettings` are a **separate** settings path with nothing to do with table column labels.
99
+
100
+ **Trap — "the page has no Sections" is a dead end.** In prod (and sandbox) **every** `*-listing` page has **0 rows in `Sections`**; only the `*-view` pages (`items-view`, `sales-orders-view`) have any (5 each). Missing Sections is normal for a listing page and is never why labels are missing. Time was lost chasing this on 2026-09-24.
101
+
102
+ **Two failure shapes in `useAssignTableFieldLabels` that look identical on screen** (`toga-blox-npm/dist/components/Table/hooks/useAssignTableFieldLabels.js`):
103
+
104
+ | Cause | Code path | Result |
105
+ |---|---|---|
106
+ | `pageMeta` is null (no page-meta record) | `if (!pageFields) return table` | fields carry **no `label` key at all** |
107
+ | `pageMeta` present but the page slug is wrong / field unmatched | `const label = labelSingular \|\| fieldSlug` | label is stamped as the **raw slug** |
108
+
109
+ Pointing a page at the **wrong** page-meta slug is therefore not harmless — the merge runs, matches nothing, and writes slugs in as labels. Both render as camelCase headers, so the symptom does not tell you which one you have; check the `pageMeta` response.
87
110
 
88
111
  **Interim workaround (Transfer Orders, 2026-08-28):** a slug humanizer applied to the **merged** meta — `sourceLocation` → "Source Location" — deliberately after the merge so headers and the Columns modal cannot drift. It cannot recover a label the slug does not contain: the design's "TO Number" is unreachable from slug `number`.
89
112
 
90
- **The real fix is a label source**, one of:
91
- 1. a legacy `<slug>-listing` **page-meta record** (matches every existing page), or
92
- 2. extending the **list surface** with per-column `FIELD` elements carrying a `labelMessageId` — which additionally makes headers translatable through `MessageTranslations`, something the page-meta route cannot do. See [surface-frontend](./surface-frontend.md).
113
+ **Preferred fix for a new list page (decided 2026-09-24): seed the Core page meta.** One `dbchanges2` `Core/` migration with a `<slug>-listing` `Pages` row + `PageRecords` link + `DefaultRecordFieldSettings` `label-singular` rows. This was previously mis-estimated as expensive (assumed per-tenant `SectionRecordFields` fan-out across ~30 client DBs plus a cross-cluster split) — **it is not; it is Core-only, one file.** Prefer it over the `humanizeSlug` fallback, which stays valid only where the slug already reads correctly. A third option, extending the **list surface** with per-column `FIELD` elements carrying a `labelMessageId`, additionally makes headers translatable through `MessageTranslations` — something the page-meta route cannot do. See [surface-frontend](./surface-frontend.md).
114
+
115
+ Worked example (Bundles, TRUE-82137, 2026-09-24) — `dbchanges2` `Core/2026-09-24a - BundlesListingPageMeta.sql`: `Pages(bundles-listing, appId 1)`, `PageRecords` → `Records` id 43 (bundles), and 5 `DefaultRecordFieldSettings` `label-singular` rows (`number` 265 → Number, `name` 248 → Name, `description` 1446 → Description, `isActive` 1356 → Active, `sortOrder` 1314 → Sort Order). The `Records` and `RecordFields` ids already existed in prod, so no team-reserved ids were needed.
93
116
 
94
117
  ### 🚨 Never hardcode a table meta in a view model — it fakes the flags too
95
118
 
@@ -104,9 +127,14 @@ Inventory page), and called `useTablePageMeta` but **threw the fetched result aw
104
127
  **masked** the fact that the real served meta had them all off (the flags were NULL in
105
128
  `TableViewFields`). The page looked configured while the database said otherwise.
106
129
 
107
- Use the fetched server meta plus a `humanizeSlug` label fallback — the pattern in
108
- `useTransferOrdersPageViewModel`. A hardcoded meta is never a workaround for missing data; it hides
109
- the data problem.
130
+ Use the fetched server meta. A hardcoded meta is never a workaround for missing data; it hides the
131
+ data problem.
132
+
133
+ The label half was closed on **2026-09-24**, and it takes **two changes that must land together**:
134
+ `useBundlesPageViewModel.tsx:40` `"inventory"` → `"bundles-listing"` (commit `87b4442`), **and** the
135
+ Core seed `dbchanges2 Core/2026-09-24a - BundlesListingPageMeta.sql` (commit `89d88d7`). Shipping the
136
+ slug alone leaves the page with no page-meta record; shipping the seed alone leaves it fetching
137
+ `inventory`.
110
138
 
111
139
  ### What's in the table meta (the columns)
112
140
 
@@ -157,7 +185,10 @@ App-side glue (NOT in toga-blox):
157
185
  - **`additionalData` must be in the query key** (it is, inside `useTableData`) — required for WHERE-filtered modal tables or React Query serves a stale unfiltered first render.
158
186
  - **Never hardcode a table meta object in a view model.** It silently fakes `isSortable`/`isFilterable`/`isCopyable` and hides a real `TableViewFields` gap; always render the fetched meta.
159
187
  - **toga-blox edits need a rebuild** — the app consumes `dist/`; editing only `src` is a silent no-op.
188
+ - **Listing pages have zero `Sections` rows by design** — never diagnose missing headers by looking at `Sections`.
189
+ - **A wrong page-meta slug is worse than none** — the merge stamps raw slugs in as labels; it looks the same on screen as having no page meta.
160
190
  - **A new page with no page-meta record has no column labels at all** — the table meta carries no `label` and the merge hook no-ops on a null `pageMeta`, so headers *and* the Columns modal render raw camelCase slugs. Plan the label source before building the screen (see above).
161
191
 
162
192
  ## Change history
193
+ - 2026-09-24 — Corrected the column-label source: labels come from `Core.DefaultRecordFieldSettings` (`label-singular`) scoped by `Pages`→`PageRecords`→`RecordFields`, **not** from `Sections`; listing pages legitimately have zero Sections. Recorded the two `useAssignTableFieldLabels` failure shapes (null pageMeta vs. wrong slug → raw slug stamped as label), corrected the cost of seeding page meta to Core-only/one-file, and closed the Bundles label gap (slug fix + `Core/2026-09-24a`, TRUE-82137). (apeterson)
163
194
  - 2026-09-23 — Added the hardcoded-table-meta anti-pattern found on the Bundles page: it produced raw slug headers and, worse, faked `isSortable`/`isFilterable`/`isCopyable` as true, masking NULL flags in `TableViewFields` (TRUE-82137). (apeterson)
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-23
9
+ updated: 2026-09-24
10
10
  owners: [apeterson, bala, "jcardinal"]
11
11
  files:
12
12
  - toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts
@@ -191,7 +191,7 @@ One `SurfaceOverrides` row, `IS_VISIBLE = '1'`, **client-wide** (`personaId`/`ro
191
191
 
192
192
  `src/pages/TransferOrders/`, wired into `ROUTE_REGISTRY` in `src/routes.tsx` as `transfer-orders`.
193
193
  - **The TableView slug comes from the surface** (`listSurfaceMeta.surface.tableViewSlug`, falling back to a local constant), so re-pointing the page at a different view is a data change. The page never derives its identity from the route.
194
- - **Column labels are humanized from the slug.** There is no `transfer-orders-listing` page-meta record, so blox's label merge contributes nothing and headers would render raw camelCase. The humanizer is applied to the **merged** meta so headers and the Columns-visibility modal cannot drift. It cannot produce the design's "TO Number" from slug `number` — see [meta-driven-table-data](./meta-driven-table-data.md) for the real fix.
194
+ - **Column labels are humanized from the slug.** There is no `transfer-orders-listing` page-meta record, so blox's label merge contributes nothing and headers would render raw camelCase. The humanizer is applied to the **merged** meta so headers and the Columns-visibility modal cannot drift. It cannot produce the design's "TO Number" from slug `number`. Seeding a `transfer-orders-listing` page-meta record is the fix, and it is cheap (Core-only, one `dbchanges2` file) — see [meta-driven-table-data](./meta-driven-table-data.md).
195
195
 
196
196
  ### The record modal — two fetches, one modal element
197
197
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.883",
3
+ "version": "1.0.884",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",