toga-ai 1.0.815 → 1.0.816

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: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-03
10
- owners: [apeterson]
9
+ updated: 2026-09-15
10
+ owners: [apeterson, tcox]
11
11
  files:
12
12
  - toga25-supply/src/fieldsConfig/index.ts
13
13
  - toga25-supply/src/fieldsConfig/resolveRole.ts
@@ -196,11 +196,29 @@ modal's chrome from the same per-client JSON, not from the component:
196
196
  the Enter-PO modal fields — its nested `valueKey`s are **not** action buttons. `DEFAULT/orderViewFields.json`
197
197
  has no `recordActionFields` at all. Any tooling that walks action buttons must scope strictly to the
198
198
  `actionOptions` array.
199
+ - **⚠ A client JSON file existing does NOT mean it is wired.** Verified 2026-09-15: the per-client
200
+ `vendorItemRecordCreateFields.json` files (COMPASS, COMPASSCANADA, NYCHH) are **not imported into
201
+ `fieldsConfig/index.ts`**, so `FIELDS` resolves `DEFAULT` for every client and edits to those
202
+ files change nothing. Adding the file is step 1 of five (see *Adding a new client-configurable
203
+ field*) — if your override has no effect, check the wiring before you debug the JSON.
204
+ - **A missing `header.modalTag` renders a BLANK badge, not no badge.** The modal chrome is fully
205
+ config-driven, so a client file that omits a block another client has just draws empty chrome.
206
+ Found on the create modal: COMPASSCANADA's and NYCHH's `itemRecordCreateFields.json` lacked the
207
+ `header.modalTag` block COMPASS had. **When you add a header/action key to one client's file, add
208
+ it to every client's file for that modal mode** — there is no per-key fallback to DEFAULT once a
209
+ client bundle exists.
199
210
  - Worked examples: `salesOrdersPageFields` (client × role) and `inventoryGroupings` (client-only +
200
211
  hydration; full write-up in `src/pages/Inventory/README.md`); `itemRecordViewFields` /
201
212
  `vendorItemRecordViewFields` are record-modal-layout examples living under `src/layout/.../viewModel/FIELDS/`.
202
213
 
203
214
  ## Change history
215
+ - 2026-09-15 — Added the `header.modalTag` block to COMPASSCANADA's and NYCHH's
216
+ `itemRecordCreateFields.json` (both rendered a **blank** badge in the create modal because the
217
+ block was missing, not absent-by-design). Recorded two gotchas found while writing the create-modal
218
+ Cypress specs: a client JSON file can exist and be **unwired** (the per-client
219
+ `vendorItemRecordCreateFields.json` files are never imported, so `FIELDS` always uses DEFAULT), and
220
+ a header key added to one client's file must be added to every client's file for that modal mode.
221
+ (tcox)
204
222
  - 2026-09-03 — Documented the actual `resolveRole` flag → role table (COMPASS/COMPASSCANADA:
205
223
  `_isAdmin`/`_isSuperAdmin`/`_isSupervisor`, in that order, else `USER`; QUAD:
206
224
  `_isGlobalAdmin`/`_isBuyer`/`_isItShopper`, else `USER`; every other client `DEFAULT`) and the
@@ -6,8 +6,8 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-08-31
10
- owners: [jcardinal, apeterson]
9
+ updated: 2026-09-15
10
+ owners: [jcardinal, apeterson, tcox]
11
11
  files:
12
12
  - toga25-supply/amplify.yml
13
13
  - toga25-supply/src/api/api.ts
@@ -16,6 +16,7 @@ files:
16
16
  - toga25-supply/package.json
17
17
  - toga25-supply/.env.development
18
18
  related:
19
+ - ./cypress-testing.md
19
20
  - ../architecture.md
20
21
  - ../../toga-blox/workflows/dynamic-publish-pipeline.md
21
22
  - ../features/transfer-orders-page.md
@@ -70,6 +71,23 @@ production.
70
71
  - Node: Vite 7 requires Node 20.19+ or 22.12+ (Node 22 recommended). Amplify's default image
71
72
  is fine; optionally pin via `.nvmrc`.
72
73
 
74
+ ### The Cypress test phase (added 2026-09-15, `_sandbox-client`)
75
+
76
+ `amplify.yml` now runs the full Cypress suite in the build. This is the app's **only** CI — there
77
+ is no GitHub Actions workflow. See [cypress-testing](./cypress-testing.md) for the suite itself.
78
+
79
+ - **`preTest`** — writes the four `*.togasupply` entries into `/etc/hosts` (the tenant is resolved
80
+ from the hostname, so without them every spec visits the wrong client), installs Cypress with
81
+ `CYPRESS_CACHE_FOLDER` set so the binary is cacheable, installs `mochawesome`, and serves the
82
+ built app with **`pm2` + `vite preview` on 5173**. `vite.config.ts` needs
83
+ `preview.allowedHosts: ['.togasupply']` or preview rejects the tenant hostnames.
84
+ - **`test`** — one Cypress run **per tenant** (one superdomain per run) plus the component project.
85
+ - **`postTest`** — `mochawesome-merge` combines the per-tenant reports into one.
86
+ - **`USER_DISABLE_TESTS`** gates the phase per branch, so a branch can deploy without the suite.
87
+ - `.gitignore` excludes `cypress/reports` and `.cypress-cache`.
88
+
89
+ A failing spec fails the deploy — same posture as the `npx tsc -b` step.
90
+
73
91
  ### Why the exact `--mode` matters at runtime
74
92
 
75
93
  `import.meta.env.MODE` (the `--mode` value) is baked into the bundle and read at **runtime**
@@ -219,6 +237,11 @@ branch does NOT reconnect it**: pushes land in GitHub and Amplify ignores them e
219
237
  has ever been developed against a linked local checkout.
220
238
 
221
239
  ## Change history
240
+ - 2026-09-15 — Documented the **Cypress test phase** added to `amplify.yml` on `_sandbox-client`
241
+ (`preTest` hosts entries + cached Cypress install + `pm2`/`vite preview` on 5173, one `test` run
242
+ per tenant plus the component project, `postTest` mochawesome merge, `USER_DISABLE_TESTS` gate),
243
+ the required `preview.allowedHosts: ['.togasupply']` in `vite.config.ts`, and the new `.gitignore`
244
+ entries. This build phase is the app's only CI. (tcox)
222
245
  - 2026-08-31 — Restored the branch's deploy inputs and recorded three systemic findings.
223
246
  (1) **`amplify.yml` was absent from the root of every remote branch** (it lived in a single commit
224
247
  `5a276be`), so Amplify had been building from a **console-stored spec** and the fail-loud
@@ -6,36 +6,30 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-09-14
10
- owners: [apeterson]
9
+ updated: 2026-09-15
10
+ owners: [apeterson, tcox]
11
11
  files:
12
12
  - toga25-supply/cypress.config.ts
13
13
  - toga25-supply/cypress/README.md
14
- - toga25-supply/cypress/tsconfig.json
15
14
  - toga25-supply/cypress/support/e2e.ts
16
- - toga25-supply/cypress/support/commands.ts
17
- - toga25-supply/cypress/support/component.tsx
18
15
  - toga25-supply/cypress/support/tenants.ts
16
+ - toga25-supply/cypress/support/tenantExpectations.ts
19
17
  - toga25-supply/cypress/support/api/stubApi.ts
20
- - toga25-supply/cypress/support/api/envelope.ts
21
- - toga25-supply/cypress/support/api/users.ts
22
- - toga25-supply/cypress/support/api/surfaces.ts
23
- - toga25-supply/cypress/support/api/session.ts
24
18
  - toga25-supply/cypress/e2e/emptyNavigation.cy.ts
25
- - toga25-supply/cypress/component/harness.cy.tsx
26
- - toga25-supply/cypress/fixtures/tenants/COMPASS/surfaces/navigation.json
27
- - toga25-supply/cypress/fixtures/tenants/COMPASSCANADA/surfaces/navigation.json
28
- - toga25-supply/cypress/fixtures/tenants/NYCHH/surfaces/navigation.json
29
- - toga25-supply/cypress/fixtures/tenants/QUAD/surfaces/navigation.json
19
+ - toga25-supply/cypress/e2e/itemModalCreate.cy.ts
20
+ - toga25-supply/cypress/e2e/vendorItemModalCreate.cy.ts
21
+ - toga25-supply/cypress/e2e/vendorItemModalEdit.cy.ts
22
+ - toga25-supply/cypress/fixtures/tenants/COMPASS/expectedUI/itemCreate.ts
23
+ - toga25-supply/cypress/fixtures/tenants/COMPASS/expectedUI/vendorItemCreate.ts
30
24
  - toga25-supply/scripts/cypress.mjs
31
- - toga25-supply/tsconfig.node.json
32
- - toga25-supply/package.json
33
25
  related:
34
26
  - ../features/surface-frontend.md
35
27
  - ../features/client-configurable-fields.md
28
+ - ../features/record-modals-and-nested-tables.md
36
29
  - ../features/side-navigation-and-default-route.md
37
30
  - ../features/sso-redirect-and-session-gating.md
38
31
  - ../features/force-logout-on-deployment.md
32
+ - ./amplify-deployment.md
39
33
  - ../architecture.md
40
34
  - ../../../2.0/standards/frontend.md
41
35
  ---
@@ -47,8 +41,10 @@ a stub layer that answers **every** `/v2` call so no test touches a backend. It
47
41
  the thing that actually breaks in this app — **tenant × role × feature** — without needing real
48
42
  logins or a seeded database.
49
43
 
50
- Current state (2026-09-03, branch `TRUE-81735`, working tree only): **26 tests green** —
51
- 6 e2e specs × 4 tenants (COMPASS, COMPASSCANADA, NYCHH, QUAD) + 2 component tests.
44
+ Current state (2026-09-15, branch `TRUE-79810`): the base harness (2026-09-03, `TRUE-81735`)
45
+ plus the **record-modal specs** — create + edit for Items and Vendor Items, per tenant. Tenants are
46
+ COMPASS, COMPASSCANADA, NYCHH, QUAD; only COMPASS and COMPASSCANADA have a role that reaches
47
+ `/items` and `/vendor-items`, so NYCHH and QUAD run a single placeholder test per modal spec.
52
48
  `cypress/README.md` is the hands-on version of this doc for a developer working without Claude
53
49
  (run commands, recipes, traps). Read that to *write* a test; read this to understand *why* it is
54
50
  shaped this way.
@@ -121,6 +117,80 @@ Three custom commands: `cy.stubApi()`, `cy.visitAsGuest(path)` (storage cleared
121
117
  stable semantic attribute (`input[name]`, `button[type=submit]`, `a[href]`) rather than adding a
122
118
  testid for its own sake.
123
119
 
120
+ ## Record-modal specs (create + edit)
121
+
122
+ `e2e/itemModalCreate.cy.ts` (16 tests) and `e2e/vendorItemModalCreate.cy.ts` (14 tests) are the
123
+ template for any new create/edit modal spec. They test the Pattern 6 form modal — see
124
+ [record-modals-and-nested-tables](../features/record-modals-and-nested-tables.md).
125
+
126
+ **Shape — expectations live in a per-tenant fixture, never in the spec.**
127
+
128
+ - `cypress/fixtures/tenants/<SLUG>/expectedUI/<modal>.ts` holds everything tenant-specific:
129
+ section and field labels, the option text to pick, and the **names of the records to act on**
130
+ (`vendorNameWithoutIntegrationMethods`, `vendorNameWithIntegrationMethods`, `itemName`).
131
+ - The shape is typed in `cypress/support/tenantExpectations.ts`
132
+ (`ItemCreateExpectations`, `VendorItemCreateExpectations`), so a tenant that adds a field fails
133
+ type-check instead of failing at runtime.
134
+ - **A tenant with no fixture must fail as a failing `it()`, not a module-level `throw`.** A throw at
135
+ import time kills the whole spec file with no readable reason; a failing test names the missing
136
+ fixture.
137
+ - Tenants whose role cannot reach the page get **one placeholder test**, so the run count stays
138
+ honest instead of the spec silently registering zero tests.
139
+
140
+ **What the create specs cover** (both modals): empty-form state, the header label following the
141
+ picked vendor, the exact POST body (relations sent as `{uuid}`), required-field errors **per
142
+ field**, currency/number formats, `POST` 500 → error toast + modal closes, the unsaved-input guard
143
+ dialog, a clean form after discard, and the table refetch.
144
+
145
+ Item-create adds: Etilize ID lookup loading images (`GET /vendor-items` join row →
146
+ `GET /items/{uuid}`), option **objects** in the body, an `itemPrices` row for the Default Purchase
147
+ Method, `personaItems` nested as `{persona:{uuid}}`, **no** `itemAssortments` on the body (one
148
+ `POST /assortment-items` per assortment instead), and the *"Unit/Lease price is required when
149
+ Default Price Type is Each/Lease"* rule — which is only reachable on **create**, because on edit
150
+ blox formats the empty price to `0.00` and the rule never fires.
151
+
152
+ Vendor-item-create adds the PO-integration select, which only unlocks for a vendor that has
153
+ integrations: the spec serves `GET /integrations` **keyed on the vendor uuid in the URL**, because
154
+ the tenant-wide stub returns the same rows for every vendor. Use `stubNoIntegrations()` for the
155
+ "vendor without integrations" cases.
156
+
157
+ **New stubs (`support/api/stubApi.ts`):** a default `POST /vendor-items` and a `POST /items` that
158
+ echoes the body back under `data.items` with a 201. Both `/assortment-items` and
159
+ `/assortment-items/` are matched — the app posts the trailing-slash form from create hooks and the
160
+ bare form from edit hooks (see *App findings* below).
161
+
162
+ **Assertion rules learned here — follow them:**
163
+
164
+ - **Refetch = exactly one more GET than before**, asserted as `before + 1` on `cy.get('@alias.all')`.
165
+ `> 1` passes on a double-fetch bug.
166
+ - **Never assert "no create happened" without first proving the submit ran.** Untouched-field
167
+ errors only appear *after* a submit, so assert an error (or `cy.focused()` landing on the first
168
+ invalid field) and *then* assert no POST. Otherwise the test passes when the button did nothing.
169
+ - **Per-field required errors:** find the smallest ancestor that contains the message and assert it
170
+ contains **exactly one input** — the field's. Counting message text passes when the wrong field
171
+ is the one complaining.
172
+ - **Null-safe header assertions:** the vendor header falls back, so assert `number || "Part Number"`.
173
+
174
+ ## Selector rules for blox inputs
175
+
176
+ The edit spec (`vendorItemModalEdit.cy.ts`) failed **0/5 on both tenants** because it was written
177
+ against selectors that do not exist. These rules are why:
178
+
179
+ - **Only use a `data-testid` that is actually published in blox.** `getByTestId('Active')`,
180
+ `'Vendor Sku'`, `'toggleTrack'`, `'virtualRows'` exist in **no** published blox build — not the
181
+ pinned `1.1.2-production.144`, not the newest `1.1.4-sandbox-client.148`, not the local checkout.
182
+ Cypress just fails to find them. Check the built package before using a testid.
183
+ - **Target a field by `input#<valueKey>`** — the field's config key is its DOM id, and that is
184
+ stable across tenants.
185
+ - **Pick select options by their exact text, never by menu row index.** Option order differs per
186
+ tenant, so an index-based pick tests a different value on COMPASSCANADA than on COMPASS. Put the
187
+ option text in the tenant's `expectedUI` fixture.
188
+ - **Assert the guard dialog as `dialog[open]`.** `cy.get('cancelDialogGuardrail')` is a bogus
189
+ selector — it looks for a `<cancelDialogGuardrail>` element.
190
+ - **Re-picking a record's current value is not a change** and fires no dependent request. The
191
+ Canada edit spec had to switch its "vendor with integrations" case to **COMPASS CANADA**, because
192
+ re-selecting the record's existing vendor (GRAND & TOY) triggered no `/integrations` fetch.
193
+
124
194
  ## Guardrails built into the suite
125
195
 
126
196
  ### The fall-through check (the main one)
@@ -163,11 +233,13 @@ detectable instead of silently self-healing.
163
233
  `cypress/component/harness.cy.tsx` mounts a blox component and asserts one React + one
164
234
  QueryClient. It fails when the component-test Vite config drifts from `vite.config.ts`.
165
235
 
166
- ### Not built: CI
236
+ ### CI — now an Amplify build phase (not GitHub Actions)
167
237
 
168
- There is **no** CI for this yet. Worth knowing before you plan it: a workflow must add the four
169
- `*.togasupply` `/etc/hosts` entries as a step (the tenant is resolved from the hostname) and serve
170
- a build (`vite preview` / `start-server-and-test`).
238
+ The suite runs in the **Amplify build** for `_sandbox-client`: `preTest` writes the four
239
+ `*.togasupply` `/etc/hosts` entries and serves the build, `test` runs one Cypress run per tenant
240
+ plus the component project, `postTest` merges the mochawesome reports, and `USER_DISABLE_TESTS`
241
+ gates it per branch. Full spec and the reasoning live in
242
+ [amplify-deployment](./amplify-deployment.md).
171
243
 
172
244
  ## How to run it
173
245
 
@@ -245,10 +317,51 @@ Every `cy:*` script goes through **`node scripts/cypress.mjs`** — see the firs
245
317
  branch.** Otherwise mounted components get a second copy of React / react-query — "Invalid hook
246
318
  call", or "No QueryClient set" with the provider plainly visible in the stack.
247
319
  `component/harness.cy.tsx` is the canary that fails when it drifts.
320
+ - **⚠ A blox `AdvancedSelect` input collapses to ZERO width once a value is picked** — so clicking
321
+ the `<input>` to reopen the menu fails ("element has no size"). Click the **control**, i.e. the
322
+ input's `.parent()`.
323
+ - **⚠ Ignore the ResizeObserver error PER SPEC, never globally.** Chrome throws
324
+ `ResizeObserver loop completed with undelivered notifications` when the
325
+ `CancelDialogGuardrail` `<dialog>` opens, because the app's `ButtonPair.tsx` writes button widths
326
+ **inside** a `ResizeObserver` callback. Swallow that one message with `cy.on('uncaught:exception')`
327
+ in the spec that opens the dialog. A global handler would hide real app exceptions — the harness
328
+ is meant to fail loud (CTO call).
329
+ - **⚠ Some lists are fetched TWICE when they open** (once as the unfiltered search source, once for
330
+ the filtered options), so a `cy.wait('@alias')` can consume the wrong request and assert against
331
+ the wrong body. Assert on **`cy.get('@alias.all')`** and pick the request you mean.
332
+ - **⚠ After merging a branch off `_feature-cypress`, run `npm install` before anything else.** Those
333
+ branches import Talos packages (`@assistant-ui/react`, `@assistant-ui/react-markdown`,
334
+ `@langchain/langgraph-sdk`, `remark-gfm`); with a stale `node_modules` **every page** dies with
335
+ *"Failed to resolve import"* and every spec fails for a reason that looks nothing like the cause.
336
+ The lockfile already has them, so plain `npm install` fixes it. **Note:** `npm install` replaces a
337
+ locally linked blox with the registry copy — relink afterwards if you were testing a local blox.
248
338
  - **Scrub captured payloads.** Copying a real body out of Chrome DevTools (Network → filter
249
339
  `/v2/` → Response → Copy response) is the fastest way to build a fixture — but those payloads
250
340
  carry **real customer data**. Remove emails, names, addresses and tokens before committing.
251
341
 
342
+ ## App findings surfaced by the suite (open — not fixed)
343
+
344
+ Writing the create specs turned up five real app defects. They are recorded here because the specs
345
+ are what proves them; fix them in the app, then tighten the spec.
346
+
347
+ - **`useItemCreateForm`'s success toast says "New Vendor Item Created" for an item.** Copy bug.
348
+ - **`useVendorItemCreateForm`'s follow-up `POST /assortment-items/` can never fire.** It reads
349
+ `response.data.items.uuid`, but the API returns `data.vendorItems`. It also posts
350
+ `_unitPrice: { enteredPrice }`.
351
+ - **Trailing-slash split:** create hooks post to `"/assortment-items/"`, edit hooks to
352
+ `"/assortment-items"`. The stub matches both; api2's real behaviour is **unverified**.
353
+ - **`ButtonPair.tsx` writes button widths inside a `ResizeObserver`** — the source of the Chrome
354
+ ResizeObserver warning above.
355
+ - **The per-client `vendorItemRecordCreateFields.json` files are not wired** (COMPASS,
356
+ COMPASSCANADA, NYCHH) — `FIELDS` always resolves DEFAULT. See
357
+ [client-configurable-fields](../features/client-configurable-fields.md).
358
+
359
+ ## Why e2e per tenant, and not component tests
360
+
361
+ Decided 2026-09-15 (CTO + reviewer): the record modals are tested as **e2e specs, one run per
362
+ tenant**. There is no component mount harness for these modals, and building one inside a test
363
+ ticket is the bigger risk — the tenant axis is the URL, which only the e2e project models.
364
+
252
365
  ## Rest of the repo's test state (for context)
253
366
 
254
367
  - **Vitest is the other half.** `npm test` (`vitest run`) currently has **one failing test file**:
@@ -265,6 +378,19 @@ Every `cy:*` script goes through **`node scripts/cypress.mjs`** — see the firs
265
378
  `SurfaceOverrides IS_VISIBLE` row in `dbchanges2`; the fixture only simulates the grant.
266
379
 
267
380
  ## Change history
381
+ - 2026-09-15 — Added the **record-modal create specs** on `TRUE-79810`: `itemModalCreate.cy.ts`
382
+ (16 tests) and `vendorItemModalCreate.cy.ts` (14 tests), per-tenant `expectedUI` fixtures typed by
383
+ `support/tenantExpectations.ts`, and `POST /items` / `POST /vendor-items` stubs. **Rewrote the
384
+ failing `vendorItemModalEdit.cy.ts`** (0/5 on both tenants) — it used `data-testid`s that exist in
385
+ no published blox build and picked select options by menu row index; now `input#<valueKey>` +
386
+ exact option text + record names in the fixtures, `dialog[open]` for the guard dialog, and
387
+ `stubNoIntegrations()` for the no-integrations cases. Recorded three new traps (zero-width
388
+ `AdvancedSelect` input → click `.parent()`; the `ButtonPair` ResizeObserver error, ignored
389
+ per-spec only; double-fetched lists → assert on `@alias.all`), the per-field required-error
390
+ assertion pattern, the refetch-as-`before+1` rule, and the stale-`node_modules` /
391
+ `npm install` trap after merging from `_feature-cypress`. Logged five open app defects the specs
392
+ surfaced, and the decision to test these modals e2e-per-tenant rather than build a component
393
+ mount harness. CI section updated: the suite now runs as an Amplify build phase. (tcox)
268
394
  - 2026-09-14 — Re-confirmed the `useTalosSurface.test.ts` / blox `dist/global.css` Vitest load
269
395
  failure on a clean tree and refreshed the pass counts (148/148 on the rest). Still unfixed; it
270
396
  silently costs one whole suite. No code change. (apeterson)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.815",
3
+ "version": "1.0.816",
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",