toga-ai 1.0.51 → 1.0.52

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.
@@ -3,4 +3,3 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
5
  | [API (api2 / TOGa API v2) Architecture](architecture.md) | `api2` is the backend powering the public **TOGa 2.0 API**. | api2/Controller/Index.php, api2/Component/Api/V2/V2.php, api2/Component/Api/Cxml/Cxml.php, api2/Component/Api/V2/Response/Response.php, api2/Config/ |
6
- | [Nested Field Foreign-Key Resolution (getChildOptionRecords)](bug/nested-field-foreign-key-resolution.md) | When a request supplies a `fields` option, the V2 JSON engine does NOT use the depth-bounded `getFullModelData()` serializer. | api2/Component/Api/V2/V2.php, _underscore/Model/Client/ItemCategoryFeature.php, _underscore/Model/Client/ItemFeature.php, _underscore/Model/Client/ItemCategoryFeatureGroup.php |
@@ -11,10 +11,9 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
11
11
 
12
12
  - **_underscore** (_Underscore) _(framework core)_ — 4 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
13
13
  - **worker2** (Worker) — 5 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
14
- - **api2** (API) — 2 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
14
+ - **api2** (API) — 1 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
15
15
  - **dbchanges2** (Database Changes) _(framework core)_ — 1 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
16
16
  - **toga2-supply** (TOGa Supply) — 2 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
17
- - **toga2-commerce** (TOGa Commerce) — 2 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
18
17
 
19
18
  ## Clients
20
19
 
@@ -5,6 +5,5 @@
5
5
  { "repo": "dbchanges2", "project": "Database Changes", "framework": "2.0", "role": "core", "dependsOn": [] },
6
6
  { "repo": "library", "project": "Library", "framework": "1.0", "role": "core", "dependsOn": [] },
7
7
  { "repo": "worker", "project": "Worker", "framework": "1.0", "role": "app", "dependsOn": [] },
8
- { "repo": "toga2-supply", "project": "TOGa Supply", "framework": "2.0", "role": "app", "dependsOn": ["api2"] },
9
- { "repo": "toga2-commerce", "project": "TOGa Commerce", "framework": "2.0", "role": "app", "dependsOn": ["api2"] }
8
+ { "repo": "toga2-supply", "project": "TOGa Supply", "framework": "2.0", "role": "app", "dependsOn": ["api2"] }
10
9
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.51",
3
+ "version": "1.0.52",
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",
@@ -1,108 +0,0 @@
1
- ---
2
- title: Nested Field Foreign-Key Resolution (getChildOptionRecords)
3
- framework: "2.0"
4
- repo: api2
5
- project: API
6
- client: shared
7
- type: feature
8
- status: active
9
- updated: 2026-06-11
10
- owners: [bala]
11
- files:
12
- - api2/Component/Api/V2/V2.php
13
- - _underscore/Model/Client/ItemCategoryFeature.php
14
- - _underscore/Model/Client/ItemFeature.php
15
- - _underscore/Model/Client/ItemCategoryFeatureGroup.php
16
- related:
17
- - 2.0/apps/toga2-commerce/bug/item-view-bundle-vs-direct-fetch.md
18
- ---
19
-
20
- ## Summary
21
-
22
- When a request supplies a `fields` option, the V2 JSON engine does NOT use the
23
- depth-bounded `getFullModelData()` serializer. It uses a fields-driven serializer
24
- (`V2.php` ~line 3383) that splits each nested field path into:
25
-
26
- - **foreign-key sub-objects** (belongsTo) → resolved by `getChildOptionRecords()`
27
- - **inherent children** (one-to-many) → resolved by recursive `processRoutePairs()`
28
-
29
- There was a bug in `getChildOptionRecords()`: a foreign-key sub-object that sits on an
30
- **inherent child which is itself reached underneath another foreign-key parent** was
31
- fetched but never attached to the output, so it silently disappeared from the JSON.
32
-
33
- This surfaced as the `toga2-commerce` item-view "Specifications" headings showing
34
- `undefined` when an item was opened **through a bundle** (but not through a category).
35
-
36
- ## Key files / entry points
37
-
38
- - `api2/Component/Api/V2/V2.php`
39
- - `execute()` / `processRoutePairs()` — request lifecycle, depth setup (`DEFAULT_DEPTH = 3`).
40
- - fields-driven serialization branch at ~line 3383 (`if (empty($selectFields))` else).
41
- - `getChildOptionRecords()` (~line 5279) — resolves FK sub-objects and nested inherent children.
42
-
43
- ## How it works
44
-
45
- For a request like `GET /items/{uuid}` with
46
- `...itemFeatures.itemCategoryFeatures.itemCategoryFeatureGroup.name`:
47
-
48
- - The item is the **top-level record**, so `itemFeatures` and `itemCategoryFeatures` are
49
- handled as inherent children via the main `processRoutePairs` recursion, and
50
- `itemCategoryFeatureGroup` (an FK on `ItemCategoryFeature`) resolves correctly. WORKS.
51
-
52
- For `GET /bundles/{uuid}` with
53
- `bundleItems.item.itemFeatures.itemCategoryFeatures.itemCategoryFeatureGroup.name`:
54
-
55
- - The top record is the bundle; `item` is an **FK** on `bundleItem`. Stepping through that FK
56
- routes the whole `item` subtree into `getChildOptionRecords()`.
57
- - Inside it: `feature` is an FK → handled correctly (so `feature.measure.name` came back).
58
- But `itemCategoryFeatures` is an **inherent child**, so it fell into the branch at
59
- ~line 5779 ("requesting all available data for an inherent child"). That branch fetched
60
- each row's FK (`itemCategoryFeatureGroup`) into `$res` (~line 5840) and **discarded it** —
61
- only scalar columns (`uuid`, `limitDirection`) were attached. The group fell out.
62
-
63
- ## The fix
64
-
65
- Attach the resolved FK result to the inherent child row (after the `getChildOptionRecords`
66
- call at ~line 5850):
67
-
68
- ```php
69
- $outDataThisInherentChild[$inherentChildField] = $res;
70
- ```
71
-
72
- ## Data model
73
-
74
- Relationship chain (all `_Model_Client_*`, DB_CLIENT):
75
-
76
- - `ItemFeature` (`itemId`, `featureId`) — inherent child of `Item`.
77
- - `ItemCategoryFeature` (`itemFeatureId`, `itemCategoryFeatureGroupId`, `limitDirection`) —
78
- inherent child of `ItemFeature`.
79
- - `ItemCategoryFeatureGroup` (`itemCategoryId`, `name`) — FK target that holds the heading.
80
- - `Feature` (`measureId`) -> `Measure` — the FK chain that always worked (different code arm).
81
-
82
- ## Client variations
83
-
84
- None — this is a core engine fix affecting every client and every endpoint whose `fields`
85
- request has the shape "FK -> inherent child -> FK". The common top-level case
86
- (`/items`, `/sales-orders`, etc.) uses the other code path and is unaffected.
87
-
88
- ## Gotchas / known issues
89
-
90
- - It is NOT a depth, ACL, or data problem. Verified against prod `Client_Compass`:
91
- the group data exists (0 null `itemCategoryFeatureGroupId`), the
92
- `item-category-feature-groups` record (126) has READ with `indirectRecordId = NULL`
93
- (readable on any path), and no `minDepth` interceptor applies — both calls run at
94
- `DEFAULT_DEPTH = 3`. `feature.measure.name` and `itemCategoryFeatureGroup.name` are at
95
- identical nesting depth, so depth could never explain why one survived and the other did
96
- not — the real cause is FK-vs-inherent-child handling.
97
- - Tell-tale sign of the buggy branch: it ignores the requested sub-fields and dumps all
98
- permitted scalar columns, so the response contained a `uuid` that was never requested.
99
- - This branch still ignores `$varOptions` (over-returns columns) — the fix only makes FK
100
- sub-objects consistent with the scalars. Acceptable and ACL-bounded, but slightly more
101
- payload.
102
- - Leftover dead code: a second `getChildOptionRecords()` call at ~line 5887 also computes
103
- `$res` and discards it (and references `$newPreviousIdentifiers`, only defined when an FK
104
- field was seen). Left untouched to keep the fix minimal; worth removing separately.
105
-
106
- ## Related docs
107
-
108
- - 2.0/apps/toga2-commerce/bug/item-view-bundle-vs-direct-fetch.md
@@ -1,6 +0,0 @@
1
- # toga2-commerce (TOGa Commerce) — 2.0 knowledge
2
-
3
- | Doc | Summary | Files |
4
- |-----|---------|-------|
5
- | [TOGa Commerce (toga2-commerce) Architecture](architecture.md) | TOGa Commerce (npm package `commerce2-react`) is the multi-tenant 2.0 storefront. | toga2-commerce/src/App.tsx, toga2-commerce/src/routes.tsx, toga2-commerce/src/api/axiosInstance.ts, toga2-commerce/src/contexts/AuthContext.tsx, toga2-commerce/src/themeConfig/ThemeContext.tsx, toga2-commerce/src/stores/index.ts |
6
- | [Item-View Bundle vs Direct Fetch](bug/item-view-bundle-vs-direct-fetch.md) | The item-view page (`/item-view`) fetches a single item two different ways depending on how the user reached it and what access they have: - **Direct** — `GET / | toga2-commerce/src/pages/ItemsView/api/ItemsApi.ts, toga2-commerce/src/pages/ItemsView/viewModel/useItemDetailsViewModel.ts |
@@ -1,68 +0,0 @@
1
- ---
2
- title: TOGa Commerce (toga2-commerce) Architecture
3
- framework: "2.0"
4
- repo: toga2-commerce
5
- project: TOGa Commerce
6
- client: shared
7
- type: architecture
8
- status: active
9
- updated: 2026-06-11
10
- owners: [bala]
11
- files:
12
- - toga2-commerce/src/App.tsx
13
- - toga2-commerce/src/routes.tsx
14
- - toga2-commerce/src/api/axiosInstance.ts
15
- - toga2-commerce/src/contexts/AuthContext.tsx
16
- - toga2-commerce/src/themeConfig/ThemeContext.tsx
17
- - toga2-commerce/src/stores/index.ts
18
- related: []
19
- ---
20
-
21
- ## Summary
22
-
23
- TOGa Commerce (npm package `commerce2-react`) is the multi-tenant 2.0 storefront. React 18 +
24
- Vite + TypeScript + Tailwind. Its backend is api2 (2.0 REST); shared UI comes from the
25
- `@agilant/toga-blox` component library. Multi-tenant by hostname — dev hosts include
26
- `compass.togacommerce`, `compasscanada.togacommerce`, `quad.togacommerce`, `togacommerce`
27
- (see the `npm run` scripts in `package.json`).
28
-
29
- ## Routing & shell
30
-
31
- - `src/routes.tsx` — `createBrowserRouter` (react-router-dom v7). `PrivateRoute` gates all
32
- authed pages behind `useAuthenticationFlow`; authed pages are wrapped in `AuthLayout`.
33
- Pages: Home, Filter, ItemsView (`/item-view`), BundleView, Cart, OrderDetails, Account,
34
- GetSupport, Login, ResetPassword, Privacy/Terms.
35
- - `src/App.tsx` — provider stack: `PersistQueryClientProvider` (TanStack Query persisted to
36
- `localStorage`, key `commerce`, 24h staleTime) -> `AuthProvider` -> `ThemeProvider` ->
37
- `ToasterProvider` -> `RouterProvider`.
38
-
39
- ## State management
40
-
41
- - **Server state:** TanStack Query, persisted to localStorage. Query defs in `src/queries/`.
42
- - **Client state:** Zustand stores in `src/stores/` — cart (`useCartStoreZu`), edit-order mode
43
- (`useEditOrderZu`), bundle builder, user, fields, view-as impersonation, etc.
44
- - **Cart <-> API sync:** `src/api/` holds `syncSalesOrderFromApiToLocalStorage`,
45
- `syncSalesOrderItemsFromLocalStorageCartToApi`, `syncSalesOrdersDataFromLocalStorageCartToApi`.
46
- The cart lives in localStorage and syncs to api2 sales orders; "Edit Order" mode lets users
47
- edit an existing sales order (App.tsx tracks a `synced` flag).
48
-
49
- ## Cross-cutting
50
-
51
- - **Auth:** `src/contexts/AuthContext.tsx`, token-based, plus a "View As" user-impersonation
52
- flow (`useReturnOriginalUserFromViewAsStore`).
53
- - **Theming:** `src/themeConfig/ThemeContext.tsx` + `themes.json`, per tenant.
54
- - **Dynamic fields:** `src/fieldsConfig/` and per-page `FIELDS/<CLIENT>/<LANG>/<ROLE>/*.json`
55
- drive client-specific form/field sets.
56
- - **HTTP:** axios instance in `src/api/axiosInstance.ts`; Sentry for error reporting;
57
- react-hook-form for forms.
58
-
59
- ## Key decisions / gotchas
60
-
61
- - `dependsOn: ["api2"]` — runtime dependency on the api2 REST surface (not a PHP class
62
- dependency). Field requests use the api2 `fields` option; deep nested field paths are
63
- subject to api2's serializer behavior (see related api2 doc on FK resolution).
64
-
65
- ## Related docs
66
-
67
- - 2.0/apps/api2/bug/nested-field-foreign-key-resolution.md
68
- - 2.0/apps/toga2-commerce/bug/item-view-bundle-vs-direct-fetch.md
@@ -1,69 +0,0 @@
1
- ---
2
- title: Item-View Bundle vs Direct Fetch
3
- framework: "2.0"
4
- repo: toga2-commerce
5
- project: TOGa Commerce
6
- client: shared
7
- type: feature
8
- status: active
9
- updated: 2026-06-11
10
- owners: [bala]
11
- files:
12
- - toga2-commerce/src/pages/ItemsView/api/ItemsApi.ts
13
- - toga2-commerce/src/pages/ItemsView/viewModel/useItemDetailsViewModel.ts
14
- related:
15
- - 2.0/apps/api2/bug/nested-field-foreign-key-resolution.md
16
- ---
17
-
18
- ## Summary
19
-
20
- The item-view page (`/item-view`) fetches a single item two different ways depending on how
21
- the user reached it and what access they have:
22
-
23
- - **Direct** — `GET /items/{uuid}` via `fetchItemDetails()`. Used for users with direct item
24
- access. Fields come from the client/role `ITEMVIEWPAGEFIELDS.json` config.
25
- - **Via bundle** — `GET /bundles/{uuid}` via `fetchItemDetailsViaBundle()`, then the bundle's
26
- `bundleItems` are filtered client-side to the requested item uuid. Used for **indirect-access**
27
- users (they can read the item through the bundle ACL but not the item endpoint directly).
28
- Triggered when the URL has `type=bundleItem` and a `bundleUuid` query param.
29
-
30
- ## Key files / entry points
31
-
32
- - `src/pages/ItemsView/api/ItemsApi.ts`
33
- - `fetchItemDetails()` — direct `/items/{uuid}` fetch (fields from config, optional persona where).
34
- - `fetchItemDetailsViaBundle()` — `/bundles/{uuid}` fetch with `bundleItems.item.*` fields,
35
- then client-side filter to the matching item (API does not support nested-uuid where clauses).
36
- - `src/pages/ItemsView/viewModel/useItemDetailsViewModel.ts`
37
- - Chooses the fetch by `viewTypeParam === "bundleItem" && bundleUuid`.
38
- - Groups features for the Specifications section by
39
- `itemCategoryFeatures[0].itemCategoryFeatureGroup?.name`. If that group object is missing,
40
- the heading key becomes the string `undefined`.
41
-
42
- ## How it works
43
-
44
- 1. Read `uuid`, `type`, `bundleUuid` from the URL query string.
45
- 2. If `type=bundleItem` and `bundleUuid` present → `fetchItemDetailsViaBundle`; else
46
- `fetchItemDetails`.
47
- 3. Normalize the response to `{ data: { items } }` and group `itemFeatures` by their
48
- category-feature group name for display.
49
-
50
- ## Client variations
51
-
52
- Multi-tenant by host (compass / compasscanada / quad / togacommerce). Field sets are
53
- per-client/per-language/per-role under
54
- `src/pages/ItemsView/viewModel/FIELDS/<CLIENT>/<LANG>/<ROLE>/ITEMVIEWPAGEFIELDS.json`.
55
-
56
- ## Gotchas / known issues
57
-
58
- - The two fetch paths request the same leaf fields but at different nesting depths
59
- (`bundle -> bundleItems -> item -> ...` adds two levels). This exposed an api2 serializer
60
- bug where the `itemCategoryFeatureGroup` foreign key was dropped only on the bundle path,
61
- making Specifications headings render as `undefined`. Root cause and fix are documented in
62
- the api2 feature doc below — the frontend grouping logic itself is correct and identical
63
- on both paths.
64
- - `fetchItemDetailsViaBundle` exists specifically for indirect-access users, so you cannot
65
- simply switch it to `/items/{uuid}` — those users would hit an ACL denial.
66
-
67
- ## Related docs
68
-
69
- - 2.0/apps/api2/bug/nested-field-foreign-key-resolution.md