toga-ai 1.0.594 → 1.0.596

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.
@@ -55,6 +55,13 @@ Constants in `PrimaryTable.tsx`: `DEFAULT_MIN_COLUMN_WIDTH = 80`, `ABSOLUTE_MIN_
55
55
 
56
56
  1. **Measure** — on first paint, measure header + widest body cell content per column
57
57
  (`measureCellContentWidth`), store in `frozenWidths`; per-column floors in `contentFloors`.
58
+ The **full header width is a HARD floor for every column** (`headerFloors` state): it is not
59
+ capped by max-width, not overridden by a consumer `minColumnWidth`, and is applied even to
60
+ explicitly-pinned (`columnWidths`) columns — the earlier `return def.size` bypass was why
61
+ pinned columns stayed clipped. Header width is measured **deterministically** = label text
62
+ `scrollWidth` + a fixed ~34px reserve per sort/filter control, **not** by measuring the
63
+ icon-font/SVG control elements (which can report ~0 width pre-paint on the freeze pass,
64
+ causing both header abbreviation and icon overlap).
58
65
  2. **Freeze** — apply `table-layout: fixed` + a `<colgroup>` of frozen pixel widths so columns
59
66
  don't reflow while virtualizing. Resets when the leaf column set changes. Empty tables stay
60
67
  on auto-layout.
@@ -78,6 +85,10 @@ Sticky columns use a `--sticky-offset` CSS variable summed from neighboring colu
78
85
  ## Gotchas
79
86
 
80
87
  - Frozen widths measure only **mounted** rows; virtualized tables size from the first paint set.
88
+ - **Header labels must never abbreviate or overlap.** The full header width is a hard floor
89
+ (above `maxColumnWidth`, `minColumnWidth`, and pinned `columnWidths`). Measure header width
90
+ deterministically (label `scrollWidth` + fixed per-control reserve), never by measuring the
91
+ async-loading sort/filter icon elements — they can report ~0 width before they paint.
81
92
  - `headerSpan` adds a separate top row (`colSpan`); only the leaf header row maps to colgroup.
82
93
  - Resize is non-persistent (local state, resets when columns change).
83
94
  - **A visible column can look "missing" under `shrinkColumns`.** `shrinkColumns` (modal tables)
@@ -87,6 +98,11 @@ Sticky columns use a `--sticky-offset` CSS variable summed from neighboring colu
87
98
  not the column set.
88
99
 
89
100
  ## Change history
101
+ - 2026-08-17 — Fixed clipped/abbreviated headers: full header width is now a hard floor for every
102
+ column (not capped by max-width, not overridden by `minColumnWidth`, applied even to pinned
103
+ `columnWidths` — the old `return def.size` bypass left pinned columns clipped). Added a
104
+ `headerFloors` state; header width is now measured deterministically (label `scrollWidth` +
105
+ fixed ~34px per sort/filter control) instead of measuring async-loading icon elements. (apeterson)
90
106
  - 2026-08-17 — Noted that under `shrinkColumns` a genuinely visible column can render at a sliver width and look "missing" — check `isVisible` and the measured/frozen width, not the column set (apeterson).
91
107
  - 2026-06-23 — Documented the three Primary Table entry points, measure-and-freeze sizing, virtualization, and sticky columns (apeterson).
92
108
  - 2026-06-22 — Added `shrinkColumns` / `minColumnWidth` shrink-to-fit for modal tables; freeze logic uses natural content width (apeterson).
@@ -88,13 +88,18 @@ isHovered, skin, isDisabled }`. They are inserted as columns via `buildActionCol
88
88
 
89
89
  Per-column header controls, each a popover trigger with `data-active` / `data-open`:
90
90
 
91
- - **`HeaderFilterSearch`** — text filter, 5 modes (startsWith/endsWith/exactly/includes/
92
- excludes) chosen from a **mode dropdown that excludes the currently selected mode**.
93
- **Tag-based**: confirmed values become removable tags (`activeTags`); `isActive =
94
- activeTags.length > 0`. The mode dropdown is portalled to `document.body` with fixed
95
- positioning recomputed on scroll. The search icon (solid when active, regular otherwise)
96
- is hidden while the input is focused; both the trigger button and the icon span carry
97
- `data-active`. The clear button must not widen the input row (fixed-width menu).
91
+ - **`HeaderFilterSearch`** — text filter, **multi-chip with per-chip mode**. Each confirmed
92
+ search becomes its own `TextFilterTag = { value, mode }` (mode Includes / Starts with /
93
+ Ends with / Exactly / Excludes). The mode dropdown sets the mode for the **next** chip
94
+ added; existing chips keep the mode they were created with. All chips on a column are
95
+ combined with **AND**. `onFilterChange` emits the full tags array (`{ tags }`); the
96
+ component rehydrates every chip from props and renders each as "Mode: value". `isActive =
97
+ tags.length > 0`. The mode dropdown is portalled to `document.body` with fixed positioning
98
+ recomputed on scroll. The search icon (solid when active, regular otherwise) is hidden
99
+ while the input is focused; both the trigger button and the icon span carry `data-active`.
100
+ The clear button must not widen the input row (fixed-width menu).
101
+ (Previously a column allowed only one term / one mode — a second search replaced the first
102
+ and switching mode re-applied it to everything.)
98
103
  - **`HeaderFilterRange`** — numeric, modes exactly/moreThan/lessThan + a range toggle
99
104
  (min/max); emits `{ key, value }[]` (`eq`/`min`/`max`/`between`). Reads its persisted
100
105
  operator/values back from a `filterParams` prop (not `filterValue`, which collapses to the
@@ -134,6 +139,10 @@ Per-column header controls, each a popover trigger with `data-active` / `data-op
134
139
  ## Gotchas
135
140
 
136
141
  - `MIN_COLUMN_WIDTH = 120` is the shrink floor only for columns with **no** explicit width.
142
+ - **Never truncate header labels.** `truncateCells` (`.truncateCells .headerCellLabel` in
143
+ `toga.module.css`) is for **body** values only; it must apply only `white-space: nowrap` to
144
+ the header (no `overflow:hidden`/`text-overflow:ellipsis`/wrapping) or headers show ellipses.
145
+ Headers must render in full on one line, never abbreviated, never overlapping.
137
146
  - The filter `includes` mode treats comma-separated values as OR.
138
147
  - `EditableCell` currency formats **on blur**, phone formats **on keystroke**.
139
148
  - Cell type config (`columnTypeConfig` prop) overrides the hardcoded `COLUMN_TYPE_CONFIG`.
@@ -151,24 +160,36 @@ Per-column header controls, each a popover trigger with `data-active` / `data-op
151
160
  never for data fetching. A visible column can still *look* missing when `shrinkColumns` freezes
152
161
  it to a tiny width because its measured header+body content is minimal (see
153
162
  [primary-table-templates](primary-table-templates.md)).
154
- - **Filter presentational components must seed their internal mode/value state from props.**
155
- The filter mode/operator survives the URL round-trip it is persisted as a URL key suffix
156
- (`_starts`/`_ends`/`_eq`/`_excludes` for text; `_min`/`_max`/`_eq`/`_between` for range),
157
- rehydrated into TanStack `columnFilters` by toga25-supply `useServerTableUrlState`, and
158
- carried through `PrimaryTableHeaderCell`. But `HeaderFilterSearch`/`HeaderFilterRange` are
159
- remounted fresh each time a filter popover opens, so if they don't seed local state from
160
- their props (`filterMode` / `filterParams`) they hard-default (`includes` for text,
161
- `exactly` for range) and reopening a filter *shows the wrong mode* even though the applied
162
- filter is correct. Fixed via a `useEffect` seeding `filterMode` from `filterModeProp` in
163
- `HeaderFilterSearch`, and a rehydration effect keyed on `JSON.stringify(filterParams)` in
164
- `HeaderFilterRange` that reconstructs mode + value(s) from the operator keys
165
- (`eq`→Exactly, `min`→MoreThan, `max`→LessThan, `between`→range min/max).
163
+ - **Filter presentational components must seed their internal state from props.**
164
+ Filter state survives the URL round-trip and is rehydrated into TanStack `columnFilters` by
165
+ toga25-supply `useServerTableUrlState`, carried through `PrimaryTableHeaderCell`. But
166
+ `HeaderFilterSearch`/`HeaderFilterRange` are remounted fresh each time a popover opens, so
167
+ they must seed local state from props or reopening *shows the wrong state*.
168
+ **Text (`HeaderFilterSearch`)** now persists as a single readable URL param
169
+ `<slug>_<col>_filter` = comma-separated `mode:value` pairs (values URL-encoded so `:`/`,`
170
+ inside a term can't corrupt the delimiters); `getDataTableData` parses it and pushes **one
171
+ AND WHERE condition per chip** (mode→op: includes→contains, startsWith→starts,
172
+ endsWith→ends, exactly→eq, excludes→excludes). The component rehydrates the full `tags`
173
+ array from props. Old base64/legacy single-mode `_starts`/`_ends`/… URLs don't parse under
174
+ the new scheme (graceful just re-apply the filter). The browser may percent-encode `:`/`,`
175
+ in the address bar but it round-trips.
176
+ **Range (`HeaderFilterRange`)** still persists as key suffixes (`_min`/`_max`/`_eq`/
177
+ `_between`); it reads mode + value(s) back from `filterParams` via a rehydration effect keyed
178
+ on `JSON.stringify(filterParams)` (`eq`→Exactly, `min`→MoreThan, `max`→LessThan,
179
+ `between`→range min/max).
166
180
  - **Table/surface meta is cached with `Infinity` staleTime/gcTime** (`useFetchTablePageMeta`), so
167
181
  after a DB metadata change (`TableViewFields`, `Core.RecordFields`) a **hard reload** is required
168
182
  to see it. And the app consumes toga-blox's built **`dist/`**, not `src/` — editing blox source
169
183
  requires `npm run build` before it takes effect at runtime.
170
184
 
171
185
  ## Change history
186
+ - 2026-08-17 — Built multi-chip text column filter: each `HeaderFilterSearch` search is now its
187
+ own `TextFilterTag {value, mode}`, the mode dropdown sets the mode for the next chip, existing
188
+ chips keep their mode, and all chips AND together. Persisted as a single readable URL param
189
+ `<slug>_<col>_filter` = `mode:value,...` (URL-encoded values); `getDataTableData` emits one AND
190
+ WHERE per chip. Also fixed: header labels were being ellipsis-truncated (and overlapping the
191
+ next column) — `truncateCells` is body-only, so `.truncateCells .headerCellLabel` is now just
192
+ `white-space: nowrap`. (apeterson)
172
193
  - 2026-08-17 — Fixed: column filter **mode** was not restored when a filter popover was reopened
173
194
  (text reverted to `includes`, range to `exactly`). Root cause: the URL round-trip and
174
195
  `PrimaryTableHeaderCell` preserved the mode, but `HeaderFilterSearch` (destructured
@@ -16,3 +16,4 @@
16
16
  | [Order-submit sync sequencing (useSubmitOrder) — why these calls must not run in parallel](features/order-submit-sync-sequencing.md) | Submitting an order from the cart fires **two independent sync routines** — one for the sales-order header (`syncSalesOrderData`) and one for the line items (`s | toga2-commerce/src/pages/OrderDetails/hooks/useSubmitOrder.ts, toga2-commerce/src/api/syncSalesOrdersDataFromLocalStorageCartToApi.ts, toga2-commerce/src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts |
17
17
  | [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-commerce` (React + Vite, "commerce2-react") builds and deploys on **AWS Amplify**. | toga2-commerce/amplify.yml, toga2-commerce/.gitattributes, toga2-commerce/package.json, toga2-commerce/.github/workflows/sync-stage-environments.yml |
18
18
  | [Cart e2e — Cypress conventions & harness (toga2-commerce)](workflows/cypress-testing.md) | The Cypress **e2e** convention set for `toga2-commerce`, and the first **active** e2e coverage for the **Cart** page (`cartV2.cy.ts`, slice 1 — 12 tests, verifi | toga2-commerce/cypress/e2e/cartPage/cartV2.cy.ts, toga2-commerce/cypress/fixtures/cart/fetchSingleUserAdmin.json, toga2-commerce/cypress/fixtures/cart/fetchLocations.json, toga2-commerce/cypress/fixtures/cart/fetchUserShippingMethods.json, toga2-commerce/cypress/support/commands.ts, toga2-commerce/cypress/support/e2e.ts, toga2-commerce/src/pages/Cart/CartPage.tsx, toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx, toga2-commerce/src/pages/Cart/view/cartForm/CartFormSection.tsx, toga2-commerce/src/pages/Cart/view/cartTable/CartContentsTable.tsx, toga2-commerce/src/pages/Cart/view/cartTable/CartTableItem.tsx, toga2-commerce/src/components/Inputs/AdvancedInput.tsx, toga2-commerce/src/components/BaseButton/BaseButton.tsx |
19
+ | [Diagnosing ERR_HTTP2_PROTOCOL_ERROR (one client fails, everyone else is fine)](workflows/http2-protocol-error-diagnosis.md) | When a Chromium browser (Chrome / Edge) shows **`ERR_HTTP2_PROTOCOL_ERROR`** loading a `*.togacommerce.com` tenant for **one client/network but works for the TO | |
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: Diagnosing ERR_HTTP2_PROTOCOL_ERROR (one client fails, everyone else is fine)
3
+ framework: "2.0"
4
+ repo: toga2-commerce
5
+ project: TOGa Commerce
6
+ client: shared
7
+ type: workflow
8
+ status: active
9
+ updated: 2026-08-17
10
+ owners: ["jcardinal"]
11
+ files: []
12
+ related:
13
+ - ../../../clients/prudential/workflows/http2-alb-workaround.md
14
+ - ../architecture.md
15
+ ---
16
+
17
+ ## Summary
18
+
19
+ When a Chromium browser (Chrome / Edge) shows **`ERR_HTTP2_PROTOCOL_ERROR`** loading a
20
+ `*.togacommerce.com` tenant for **one client/network but works for the TOGA team and everyone
21
+ else**, the cause is almost always the **client's corporate TLS-inspection proxy / secure web
22
+ gateway mangling HTTP/2 framing** — NOT our code and NOT a bad deploy. HTTP/2 is terminated at
23
+ the **Application Load Balancer** (there is **no CloudFront/CDN** in front — tenant subdomains
24
+ are direct aliases to the `true-togacommerce-prod` EB environment's ALB), so the ALB is the
25
+ single negotiation point and the lever. This doc captures the reasoning and the tools so the
26
+ next dev doesn't re-derive it or chase a false code/deploy lead.
27
+
28
+ ## Step 1 — Network-vs-content test (do this first)
29
+
30
+ Load the same failing URL on a **phone over CELLULAR** (off the corporate Wi-Fi):
31
+ - **Works on cellular** → the failure is **network-dependent = the client's proxy**. Stop
32
+ suspecting our code. A coincidental deploy on the failure date is **not** the cause.
33
+ - **Also fails on cellular** → the failure is **content-dependent** (our output). Look for a
34
+ response-corrupting bug — e.g. a stray trailing `?>` plus a newline in a PHP file emitting
35
+ bytes after the response body, which can break strict HTTP/2. That is a real code cause and a
36
+ different investigation.
37
+
38
+ This one test cleanly separates "their proxy" from "our bytes."
39
+
40
+ ## Step 2 — Prove the HTTP/2 state without a browser
41
+
42
+ Local `curl` often lacks HTTP/2 support (no `nghttp2` build), so `curl --http2` is unreliable.
43
+ Use openssl ALPN instead:
44
+
45
+ ```
46
+ echo | openssl s_client -connect <host>:443 -servername <host> -alpn h2,http/1.1 2>/dev/null | grep -i ALPN
47
+ ```
48
+
49
+ - `ALPN protocol: h2` → HTTP/2 is **ON** (ALB advertises `h2`).
50
+ - `No ALPN negotiated` (when `h2` was offered first) → HTTP/2 is **OFF** (ALB no longer
51
+ advertises `h2`).
52
+
53
+ Also: browser DevTools → Network → enable the **Protocol** column → shows `h2` vs `http/1.1`
54
+ per request.
55
+
56
+ ## Step 3 — The lever: ALB `routing.http2.enabled`
57
+
58
+ - It is an **AWS Application Load Balancer attribute**, all-or-nothing — there is **no
59
+ per-client / per-tenant option**. With no CDN in front, the ALB is the negotiation point.
60
+ - Setting `routing.http2.enabled=false` makes the ALB negotiate **HTTP/1.1 only for every
61
+ client** on that environment. The app already speaks HTTP/1.1 to the EC2 instances, so **no
62
+ application code affects this**.
63
+ - The change is **global** (every `togacommerce.com` tenant on the env, a modest perf trade)
64
+ and **instantly reversible** (`Value=true`). Disabling it is both the fast, we-control-it
65
+ workaround and the proof of diagnosis.
66
+
67
+ Environment reference (for `true-togacommerce-prod`): region `us-west-2`, account
68
+ `502614707982`, ALB `awseb-AWSEB-1N7AC3UGUONUM`. Note ALB **access logs may be disabled**, in
69
+ which case there is no retroactive request evidence — the phone test and openssl check are your
70
+ evidence.
71
+
72
+ ## Step 4 — Preferred permanent fix is on the client side
73
+
74
+ The clean end-state is the **client's IT** bypassing TLS inspection / allowlisting
75
+ `*.togacommerce.com`, or configuring their gateway to fall back to HTTP/1.1. Once they do, we
76
+ **re-enable HTTP/2** on the ALB. Disabling ALB HTTP/2 is the interim workaround, not the goal.
77
+
78
+ ## Change history
79
+ - 2026-08-17 — Documented from the Prudential `ERR_HTTP2_PROTOCOL_ERROR` incident: phone/cellular
80
+ network-vs-content test, openssl ALPN check, and the ALB `routing.http2.enabled` lever (no CDN
81
+ in front of `true-togacommerce-prod`). (jcardinal)
82
+ </content>
83
+ </invoke>
@@ -29,7 +29,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
29
29
  - **talos** (TOGa IQ) — 7 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
30
30
  - **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
31
31
  - **ai-bdr** (AI-BDR) — 9 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
32
- - **toga2-commerce** (TOGa Commerce) — 15 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
32
+ - **toga2-commerce** (TOGa Commerce) — 17 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
33
33
  - **toga25-supply** (TOGa 2.5 Supply) — 11 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
34
34
  - **toga-blox** (TOGa Blox) — 9 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
35
35
  - **bdr** (BDR) — 0 doc(s) → [2.0/apps/bdr/INDEX.md](2.0/apps/bdr/INDEX.md)
@@ -10,3 +10,4 @@
10
10
  | [Prudential Order Shipped Email — transmit_ordershipped_updates_prudential.php](features/transmit-ordershipped-email.md) | 1.0 | Cron script that transmits "Order Shipped" updates to ServiceNow (RITM) and sends a shipped notification email to the end user. | worker/crons/toga2/prudential/transmit_ordershipped_updates_prudential.php, worker/crons/toga2/prudential/transmit_closecomplete_updates_prudential.php, worker/crons/toga2/prudential/transmit_rejected_cancelled_updates_prudential.php, worker/crons/toga2/prudential/generate_sales_and_purchase_orders_from_service_requests.php, worker/crons/toga2/prudential_beta/generate_sales_and_purchase_orders_from_service_requests.php, worker/crons/notifications/reports/prudential_exception_report.php |
11
11
  | [Prudential Financial](profile.md) | 2.0 | Prudential is a TOGA client whose device-fulfillment flow is driven by **Dell** via the Dell API (`Client_Prudential.Apis.id = 2`). | |
12
12
  | [Prudential: Dell ASN failed POST backfill replay](workflows/dell-asn-backfill-replay.md) | 2.0 | When Dell ASN POSTs fail in bulk (e.g. | |
13
+ | [Prudential — HTTP/2 disabled on the Commerce ALB (proxy workaround) + open re-enable action](workflows/http2-alb-workaround.md) | 2.0 | Prudential users hit **`ERR_HTTP2_PROTOCOL_ERROR`** in Chrome and Edge (both Chromium) loading `https://prudential.togacommerce.com`, while the site worked fine | |
@@ -9,11 +9,12 @@ apps:
9
9
  - worker
10
10
  - worker2
11
11
  - library
12
+ - toga2-commerce
12
13
  project: _Underscore
13
14
  client: prudential
14
15
  type: profile
15
16
  status: active
16
- updated: 2026-07-28
17
+ updated: 2026-08-17
17
18
  owners: ["jcardinal", "rgirish", "bala"]
18
19
  files: []
19
20
  related:
@@ -22,6 +23,7 @@ related:
22
23
  - features/service-request-rejection-alert-email.md
23
24
  - features/dell-lch-iop-transmissions.md
24
25
  - features/device-information-import-and-contact-linking.md
26
+ - workflows/http2-alb-workaround.md
25
27
  ---
26
28
 
27
29
  ## Summary
@@ -61,6 +63,10 @@ order-status transmissions.
61
63
  `ServiceRequests.serviceRequestTypeId`: **1 = New Hire, 2 = Breakfix, 3 = Refresh, 5 = Reclaim**.
62
64
 
63
65
  ## Gotchas
66
+ - **Commerce (`prudential.togacommerce.com`):** Prudential's corporate TLS-inspection proxy
67
+ breaks HTTP/2, causing `ERR_HTTP2_PROTOCOL_ERROR` in Chrome/Edge. HTTP/2 is currently disabled
68
+ globally on the `true-togacommerce-prod` ALB as a workaround — **open action to re-enable once
69
+ their IT fixes the proxy.** See workflows/http2-alb-workaround.md.
64
70
  - Dell will not change their payload shape — see the Dell ASN units interceptor feature doc for the
65
71
  PRE/POST translation that keeps their feed working after the tracking-number bridge migration.
66
72
  - Service request region is resolved from `Customers.name` string (`'USA'`/`'India'`/`'Ireland'`).
@@ -0,0 +1,55 @@
1
+ ---
2
+ title: Prudential — HTTP/2 disabled on the Commerce ALB (proxy workaround) + open re-enable action
3
+ framework: "2.0"
4
+ repo: toga2-commerce
5
+ project: TOGa Commerce
6
+ client: prudential
7
+ type: workflow
8
+ status: active
9
+ updated: 2026-08-17
10
+ owners: ["jcardinal"]
11
+ files: []
12
+ related:
13
+ - ../../../2.0/apps/toga2-commerce/workflows/http2-protocol-error-diagnosis.md
14
+ - ../profile.md
15
+ ---
16
+
17
+ ## Summary
18
+
19
+ Prudential users hit **`ERR_HTTP2_PROTOCOL_ERROR`** in Chrome and Edge (both Chromium) loading
20
+ `https://prudential.togacommerce.com`, while the site worked fine for the TOGA team. Root cause:
21
+ **Prudential's corporate network runs a TLS-inspection proxy / secure web gateway that mangles
22
+ HTTP/2 framing.** Proven by loading the same URL on the user's phone over **cellular** (off
23
+ corporate Wi-Fi) — it worked, so the failure is network-dependent, ruling out a code/content
24
+ cause and a coincidental same-day deploy.
25
+
26
+ ## Current state (as of 2026-08-17)
27
+
28
+ - **HTTP/2 is turned OFF globally** on the `true-togacommerce-prod` Application Load Balancer
29
+ (`routing.http2.enabled=false`), so the ALB negotiates **HTTP/1.1 only for all clients** on
30
+ that environment. This immediately fixed Prudential and was **confirmed fixed by the client**.
31
+ - This is a **global** setting (all `togacommerce.com` tenants on the env, a modest perf trade)
32
+ and **instantly reversible** — there is no per-client option because the ALB is the single
33
+ HTTP/2 negotiation point (no CDN in front).
34
+
35
+ ## ⚠ OPEN ACTION — re-enable HTTP/2
36
+
37
+ The intended end-state is for **Prudential's IT to fix their proxy** (allowlist / bypass TLS
38
+ inspection for `*.togacommerce.com`, or configure the gateway to fall back to HTTP/1.1), and
39
+ then **re-enable HTTP/2 on the ALB** (`routing.http2.enabled=true`). Until that happens, HTTP/2
40
+ stays OFF for every togacommerce tenant on `true-togacommerce-prod`. Do not forget this — it is
41
+ a live workaround, not the final state.
42
+
43
+ ## Reference
44
+
45
+ - Env `true-togacommerce-prod`, region `us-west-2`, account `502614707982`, ALB
46
+ `awseb-AWSEB-1N7AC3UGUONUM`. ALB access logs were disabled during the incident (no retroactive
47
+ request evidence).
48
+ - Full diagnostic reasoning (phone/cellular test, openssl ALPN check, the ALB lever) is the
49
+ reusable Commerce workflow: `2.0/apps/toga2-commerce/workflows/http2-protocol-error-diagnosis.md`.
50
+
51
+ ## Change history
52
+ - 2026-08-17 — Disabled HTTP/2 on the `true-togacommerce-prod` ALB to work around Prudential's
53
+ corporate TLS-inspection proxy breaking HTTP/2; client confirmed fixed. Open action: re-enable
54
+ once Prudential's IT fixes their proxy. (jcardinal)
55
+ </content>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.594",
3
+ "version": "1.0.596",
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",