toga-ai 1.0.449 → 1.0.451
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/_underscore/INDEX.md +1 -1
- package/knowledge/2.0/apps/_underscore/features/apirequest-json-content-type.md +41 -5
- package/knowledge/2.0/apps/toga2-commerce/INDEX.md +1 -0
- package/knowledge/2.0/apps/toga2-commerce/features/cart-notification-emails.md +12 -3
- package/knowledge/2.0/apps/toga2-commerce/features/cart-page-config-architecture.md +21 -6
- package/knowledge/2.0/apps/toga2-commerce/features/expedited-shipping-gating.md +14 -4
- package/knowledge/2.0/apps/toga2-commerce/workflows/cypress-testing.md +209 -0
- package/knowledge/2.0/apps/worker2/features/creating-worker-actions.md +20 -1
- package/knowledge/2.0/apps/worker2/features/talos-transcript-ingestion.md +59 -1
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| [_underscore Framework Architecture](architecture.md) | `_underscore` is the shared PHP backend framework for **all 2.0 applications**. | _underscore/_underscore.php, _underscore/Loader.php, _underscore/Framework.php, _underscore/Model.php, _underscore/Database.php, _underscore/Query.php, _underscore/Route.php, _underscore/Component.php |
|
|
7
7
|
| [ACL Permission Chain (Record & Field Authorization)](features/acl-permission-chain.md) | Authorization in the 2.0 API is **metadata-driven**: whether a role may Create/Read/Update/Delete a record is decided by rows across **four linked tables**, not | api2/Component/Api/V2/V2.php, _underscore/Model/Core/Page.php, _underscore/Model/Client/TrackingNumber.php, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql, dbchanges2/Client/2026-06-23b - ItemTranslationsAcl.sql, dbchanges2/Client/2026-07-02c - TrackingNumberNeedsReturnLabelFieldPermission.sql, dbchanges2/Client/2026-07-22c - TrackingNumberMeasureIdsFieldPermission.sql |
|
|
8
8
|
| [Address Validation (carrier waterfall + validateAddress scripted endpoint)](features/address-validation.md) | `_Model_Client_Address::validateAddress` verifies a US address against a **carrier waterfall (USPS → FedEx → UPS)** and returns a single canonical, carrier-norm | _underscore/Model/Client/Address.php |
|
|
9
|
-
| [_ApiRequest
|
|
9
|
+
| [_ApiRequest — JSON encode/decode & api-logging behavior](features/apirequest-json-content-type.md) | `_ApiRequest` is the 2.0 outbound HTTP client. | _underscore/ApiRequest.php |
|
|
10
10
|
| [Assortment Name Translation (AssortmentTranslations sidecar)](features/assortment-name-translation.md) | Serves Assortment (product-grouping) **names** in multiple languages by adding a per-language **sidecar** table `AssortmentTranslations`, reusing the platform's | _underscore/Model/Client/AssortmentTranslation.php, dbchanges2/Client/2026-06-26a - AssortmentTranslations.sql, dbchanges2/Core/2026-06-26a - AssortmentTranslationsRecord.sql, dbchanges2/Client/2026-06-26b - AssortmentTranslationsAcl.sql |
|
|
11
11
|
| [Asynchronous Query Execution (writes-only, via Worker)](features/async-query-execution.md) | `_Query` can run a **write** query asynchronously so a long/slow write does not hold a request-scoped DB connection open long enough to hit **"MySQL server has | _underscore/Query.php, worker2/Worker/Infrastructure/Database.php, worker2/Worker/Team/Transcripts.php |
|
|
12
12
|
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: _ApiRequest
|
|
2
|
+
title: "_ApiRequest — JSON encode/decode & api-logging behavior"
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: _underscore
|
|
5
5
|
project: _Underscore
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-28
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/ApiRequest.php
|
|
13
13
|
related:
|
|
14
14
|
- ../../worker2/features/oneuptime-worker2-monitoring.md
|
|
15
|
+
- ../../worker2/features/creating-worker-actions.md
|
|
16
|
+
- ../../worker2/features/talos-transcript-ingestion.md
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
## Summary
|
|
18
|
-
`_ApiRequest
|
|
20
|
+
`_ApiRequest` is the 2.0 outbound HTTP client. This doc collects its non-obvious
|
|
21
|
+
encode/decode/retry/logging behavior that callers keep getting bitten by; the original subject
|
|
22
|
+
was the Content-Type fix below.
|
|
23
|
+
|
|
24
|
+
**Original fix (2026-07-14):** `_ApiRequest::execute()`'s `ENCODE__JSON` branch json-encoded the request body but never set
|
|
19
25
|
a `Content-Type` header. cURL therefore defaulted to `application/x-www-form-urlencoded`, and
|
|
20
26
|
receivers parsed the entire JSON string as a single form-field NAME — observed at OneUptime as
|
|
21
27
|
`{"<json>":""}`. Every 2.0 `ENCODE__JSON` POST/PUT/PATCH caller was silently shipping
|
|
@@ -30,10 +36,40 @@ case-insensitive scan of already-set headers, so a caller-supplied `Content-Type
|
|
|
30
36
|
- This changes the wire format of ALL existing `ENCODE__JSON` callers (they were previously
|
|
31
37
|
sending form-urlencoded). This is a correction, but smoke-test heavy JSON callers post-deploy:
|
|
32
38
|
ClickUp, NetSuite, Vapi.
|
|
39
|
+
- **`execute()` decodes a JSON response with `json_decode($payload)` — NO assoc flag — so you
|
|
40
|
+
get `stdClass`, not an array.** Code that indexes the response as `$res['result']['x']` breaks.
|
|
41
|
+
If a caller needs arrays, don't rely on the built-in decode: leave the response handling local
|
|
42
|
+
(`json_decode($response, true)`).
|
|
43
|
+
- **`ENCODE__JSON` re-encodes the payload without `JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE`.**
|
|
44
|
+
For large non-ASCII bodies (e.g. a full meeting transcript) this inflates every such char to
|
|
45
|
+
`\uXXXX`. To preserve the exact wire bytes, `json_encode` with the flags you want yourself and
|
|
46
|
+
pass the **ready JSON string with payloadEncoding left null**.
|
|
47
|
+
- **`setAutoRetry()` is flat-delay and retries EVERY non-2xx.** If you need exponential backoff
|
|
48
|
+
or fail-fast on 4xx (other than 429), keep your own retry loop and construct one `_ApiRequest`
|
|
49
|
+
per attempt — that also gives you one api-log row per attempt.
|
|
50
|
+
- **Transport failures surface as a thrown Exception** from `execute()` (the equivalent of
|
|
51
|
+
`curl_error()`), not a return value. Catch it if your method has an error-return contract.
|
|
52
|
+
- **Api logging depends on `DB_CLIENT_LOGS` being registered.** The logging branch writes via
|
|
53
|
+
`_Model_Client_Logs_Api`, whose `DATABASE` const is `_underscore::DB_CLIENT_LOGS`. In a context
|
|
54
|
+
that hasn't registered it (most workers, CLI harnesses) logging is a **silent no-op** — or
|
|
55
|
+
throws `Unknown database 'ClientLogs'`. Either register the logs DB under that alias or pass
|
|
56
|
+
`setLogging(false)` deliberately. See
|
|
57
|
+
[Creating Worker Actions](../../worker2/features/creating-worker-actions.md#gotchas).
|
|
58
|
+
- **OPEN / not fixed: logging happens in two halves around the HTTP call.** `execute()` inserts
|
|
59
|
+
the request row and **commits before** the call, then `save()`s the response fields **after**.
|
|
60
|
+
For long calls (e.g. a 600s AI timeout) that second save runs on a connection idle for
|
|
61
|
+
minutes — the classic stale-connection failure mode — leaving api-log rows with null
|
|
62
|
+
`responseCode`/`responsePayload`. The correct fix (reconnect/re-register before the post-call
|
|
63
|
+
save) lives **inside `execute()`** and would affect every framework caller, so it is deferred
|
|
64
|
+
pending architecture review. Do **not** work around it per-caller.
|
|
33
65
|
|
|
34
66
|
## Change history
|
|
67
|
+
- 2026-07-28 — Broadened from the Content-Type fix to the general `_ApiRequest` contract:
|
|
68
|
+
documented the non-assoc `json_decode` response (returns `stdClass`), the `ENCODE__JSON`
|
|
69
|
+
re-encode losing `JSON_UNESCAPED_*`, `setAutoRetry()`'s flat-delay/retry-all behavior,
|
|
70
|
+
exception-on-transport-failure, the `DB_CLIENT_LOGS` dependency for api logging, and the
|
|
71
|
+
**open** two-phase logging / stale-connection issue on long calls. Surfaced migrating
|
|
72
|
+
`worker2 Worker/Team/Transcripts.php::callTalosEndpoint()` off raw curl. (jcardinal)
|
|
35
73
|
- 2026-07-14 — Fixed `ENCODE__JSON` to send `Content-Type: application/json` (guarded so a
|
|
36
74
|
caller-set Content-Type wins). Root-caused via OneUptime receiving `{"<json>":""}`. Affects
|
|
37
75
|
all 2.0 ENCODE__JSON callers. (jcardinal)
|
|
38
|
-
</content>
|
|
39
|
-
</invoke>
|
|
@@ -10,3 +10,4 @@
|
|
|
10
10
|
| [Config-Driven Expedited Shipping Gating (Cart)](features/expedited-shipping-gating.md) | On the toga2-commerce **Cart** page, expedited shipping options (**"2nd Day EOB"** and **"Next Day Air"**) are only offered in the *Shipping Method* dropdown wh | toga2-commerce/src/pages/Cart/helpers/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/viewModel/FIELDS/shared/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx, toga2-commerce/src/pages/Cart/CartPage.tsx |
|
|
11
11
|
| [Multi-Tenant Resolution & Theming](features/multi-tenant-theming.md) | `toga2-commerce` serves multiple clients from one codebase. | src/themeConfig/themes.json, src/themeConfig/ThemeContext.tsx, src/themeConfig/types.ts, src/components/ThemeSwitcher/ThemeSwitcher.tsx, src/components/AuthLayout/AuthLayout.tsx, src/api/axiosInstance.ts, src/contexts/AuthContext.tsx, tailwind.config.js |
|
|
12
12
|
| [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 |
|
|
13
|
+
| [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 |
|
|
@@ -6,8 +6,8 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["bala"]
|
|
9
|
+
updated: 2026-07-27
|
|
10
|
+
owners: ["bala", "tcox"]
|
|
11
11
|
files:
|
|
12
12
|
- src/pages/Cart/CartPage.tsx
|
|
13
13
|
- src/pages/Cart/view/cartForm/CartForm.tsx
|
|
@@ -17,6 +17,7 @@ files:
|
|
|
17
17
|
related:
|
|
18
18
|
- 2.0/apps/toga2-commerce/features/cart-page-config-architecture.md
|
|
19
19
|
- 2.0/apps/toga2-commerce/features/client-fields.md
|
|
20
|
+
- 2.0/apps/toga2-commerce/workflows/cypress-testing.md
|
|
20
21
|
---
|
|
21
22
|
|
|
22
23
|
## Summary
|
|
@@ -72,10 +73,18 @@ the message resolves to `undefined` and renders blank.
|
|
|
72
73
|
- Adding the label to only some `CARTPAGE.ts` files leaves other client/role/language users with a
|
|
73
74
|
blank message.
|
|
74
75
|
- Do not use a toaster or `setError` (red) for this — product wants the sapphire info style.
|
|
76
|
+
- **Now pinned by e2e.** `cypress/e2e/cartPage/cartV2.cy.ts` covers both the auto-population
|
|
77
|
+
(order-for user + supervisor emails, protected rows unremovable) and the case-insensitive
|
|
78
|
+
duplicate block surfacing the *"This email has already been added"* banner. These are exactly
|
|
79
|
+
the parity behaviors the config-cart refactor spike silently lost, so keep the spec green when
|
|
80
|
+
touching this flow (see [cypress-testing](../workflows/cypress-testing.md)).
|
|
75
81
|
|
|
76
82
|
## Change history
|
|
83
|
+
- 2026-07-27 — No behavior change. This duplicate-prevention UX (email auto-population +
|
|
84
|
+
case-insensitive duplicate banner) is now covered by cart e2e slice 1 (`cartV2.cy.ts`); linked
|
|
85
|
+
the [cypress-testing](../workflows/cypress-testing.md) workflow doc. (tcox)
|
|
77
86
|
- 2026-06-18 — Initial: case-insensitive de-dupe in both cart stores, sapphire `InfoBanner` message in
|
|
78
87
|
CartForm, `duplicateEmailAddressError` label added to all CARTPAGE.ts variants (bala)
|
|
79
88
|
|
|
80
89
|
## Related docs
|
|
81
|
-
|
|
90
|
+
- [cypress-testing](../workflows/cypress-testing.md) — e2e coverage that pins this behavior.
|
|
@@ -6,8 +6,8 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["apeterson"]
|
|
9
|
+
updated: 2026-07-27
|
|
10
|
+
owners: ["apeterson", "tcox"]
|
|
11
11
|
files:
|
|
12
12
|
- src/pages/Cart/CartPage.tsx
|
|
13
13
|
- src/pages/Cart/view/cartForm/CartForm.tsx
|
|
@@ -22,6 +22,7 @@ related:
|
|
|
22
22
|
- 2.0/apps/toga2-commerce/architecture.md
|
|
23
23
|
- 2.0/apps/toga2-commerce/features/client-fields.md
|
|
24
24
|
- 2.0/apps/toga2-commerce/features/cart-notification-emails.md
|
|
25
|
+
- 2.0/apps/toga2-commerce/workflows/cypress-testing.md
|
|
25
26
|
- 2.0/apps/toga25-supply/features/client-configurable-fields.md
|
|
26
27
|
---
|
|
27
28
|
|
|
@@ -140,10 +141,18 @@ behavior (email seeding, duplicate-email validation, edit-order cost-center/addr
|
|
|
140
141
|
### Phases
|
|
141
142
|
|
|
142
143
|
**Phase 0 — Branch + safety net.** Branch `feature/cart-config-driven-form` off `_production`.
|
|
143
|
-
**Pin a behavior oracle:**
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
**Pin a behavior oracle:** cart **e2e slice 1 now exists** and *is* part of this oracle —
|
|
145
|
+
`cypress/e2e/cartPage/cartV2.cy.ts` (12 tests, COMPASS / COMPASSCANADA admin; QUAD skipped; see
|
|
146
|
+
[cypress-testing](../workflows/cypress-testing.md)). It pins seeded-items rendering, quantity /
|
|
147
|
+
removal + subtotal, checkout enable/disable gating, clear- and empty-cart states, and — critically
|
|
148
|
+
— the two behaviors the prior spike silently dropped: **notification-email auto-population**
|
|
149
|
+
(user + supervisor rows, protected rows unremovable) and **case-insensitive duplicate-email
|
|
150
|
+
surfacing**. Still **uncovered** and to be added before/with the refactor: **bundle scenarios**
|
|
151
|
+
(blocked on stale Cypress bundle seeds), **expedited-shipping gating + guardrail modal**,
|
|
152
|
+
**edit-order mode**, and **QUAD**. The repo has no RTL/unit runner (Cypress only), so extend the
|
|
153
|
+
e2e specs — not RTL — to cover both **cart** and **edit-order** modes, and document any remaining
|
|
154
|
+
flows from the running app (edit-order original address). These guard against the "renders nothing"
|
|
155
|
+
failure the spike hit. **Inventory
|
|
147
156
|
the hardcoded client branches (C6):** grep `src/pages/Cart` for `clientSlug`/client-name comparisons
|
|
148
157
|
and per-client conditionals (start with `DuplicateKitGuardrail` and the order-for
|
|
149
158
|
cost-center/manager/email seeding) — this list is the C6 work-list for Phases 3–4.
|
|
@@ -276,6 +285,12 @@ extension** of toga2.5's philosophy, not a literal copy.
|
|
|
276
285
|
edit-order mode?
|
|
277
286
|
|
|
278
287
|
## Change history
|
|
288
|
+
- 2026-07-27 — Phase 0 oracle is now partly real: cart e2e slice 1 (`cartV2.cy.ts`, 12 tests)
|
|
289
|
+
landed and pins the exact parity behaviors the prior spike lost (notification-email
|
|
290
|
+
auto-population, duplicate-email surfacing) plus render/gating/clear/empty flows. Corrected the
|
|
291
|
+
"no tests / add RTL smoke tests" note — the repo is Cypress-only; listed what slice 1 covers and
|
|
292
|
+
what remains (bundle, expedited gating, edit-order, QUAD). See the new
|
|
293
|
+
[cypress-testing](../workflows/cypress-testing.md) workflow doc. (tcox)
|
|
279
294
|
- 2026-06-24 — Initial: documented the current `_production` Cart architecture (field-level config
|
|
280
295
|
via `CartFormSection`, hardcoded section layout in `CartForm`, dual `EditCart`/`EditOrder` forms +
|
|
281
296
|
dispatcher VM, prop-drilling), its gotchas (config-key drift, hardcoded union `defaultValues` with
|
|
@@ -6,7 +6,7 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-27
|
|
10
10
|
owners: [tcox]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-commerce/src/pages/Cart/helpers/shippingOptionGates.ts
|
|
@@ -16,6 +16,7 @@ files:
|
|
|
16
16
|
related:
|
|
17
17
|
- ../../../../clients/compass-usa/profile.md
|
|
18
18
|
- ../../../../clients/compass-canada/profile.md
|
|
19
|
+
- 2.0/apps/toga2-commerce/workflows/cypress-testing.md
|
|
19
20
|
---
|
|
20
21
|
|
|
21
22
|
## Summary
|
|
@@ -92,11 +93,20 @@ behavior is expressed in config, not in code branches.
|
|
|
92
93
|
for the computer category (consistent with the existing `checkCartContainsComputerKit`
|
|
93
94
|
detector). Fine today, but a miss if a `COMPUTERS` item ever lives only in a nested child
|
|
94
95
|
bundle.
|
|
95
|
-
- **
|
|
96
|
-
via `tsc -b` (clean, twice) and an independent maker≠checker
|
|
97
|
-
run locally (missing `eslint-plugin-react-compiler` — environment
|
|
96
|
+
- **Still no automated test for the gating itself.** The repo has no unit-test runner (Cypress
|
|
97
|
+
only). The gating shipped verified via `tsc -b` (clean, twice) and an independent maker≠checker
|
|
98
|
+
code review. ESLint could not run locally (missing `eslint-plugin-react-compiler` — environment
|
|
99
|
+
issue). **A cart Cypress e2e harness now exists** (see
|
|
100
|
+
[cypress-testing](../workflows/cypress-testing.md)) and a gating spec is now feasible: the
|
|
101
|
+
`cart/fetchUserShippingMethods.json` fixture already carries the gated option names
|
|
102
|
+
(**Next Day Air**, **2nd Day EOB**) with no `c_cost`. This gating + guardrail-modal scenario is
|
|
103
|
+
on that doc's slice-2 work-list.
|
|
98
104
|
|
|
99
105
|
## Change history
|
|
106
|
+
- 2026-07-27 — No behavior change. Noted that a cart Cypress e2e harness now exists and a
|
|
107
|
+
gating + guardrail-modal spec is feasible (the `cart/fetchUserShippingMethods.json` fixture
|
|
108
|
+
already ships the gated names with no `c_cost`); it is on the slice-2 work-list. Linked
|
|
109
|
+
[cypress-testing](../workflows/cypress-testing.md). (tcox)
|
|
100
110
|
- 2026-06-30 — Built config-driven expedited-shipping gating: expedited methods shown only
|
|
101
111
|
when the cart holds a COMPUTERS-category computer kit, with auto-reset to Standard Ground
|
|
102
112
|
otherwise; tenant opt-in via `optionGates` (no client-slug branching). Code review fixed 3
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cart e2e — Cypress conventions & harness (toga2-commerce)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga2-commerce
|
|
5
|
+
project: TOGa Commerce
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-27
|
|
10
|
+
owners: [tcox]
|
|
11
|
+
files:
|
|
12
|
+
- toga2-commerce/cypress/e2e/cartPage/cartV2.cy.ts
|
|
13
|
+
- toga2-commerce/cypress/fixtures/cart/fetchSingleUserAdmin.json
|
|
14
|
+
- toga2-commerce/cypress/fixtures/cart/fetchLocations.json
|
|
15
|
+
- toga2-commerce/cypress/fixtures/cart/fetchUserShippingMethods.json
|
|
16
|
+
- toga2-commerce/cypress/support/commands.ts
|
|
17
|
+
- toga2-commerce/cypress/support/e2e.ts
|
|
18
|
+
- toga2-commerce/src/pages/Cart/CartPage.tsx
|
|
19
|
+
- toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx
|
|
20
|
+
- toga2-commerce/src/pages/Cart/view/cartForm/CartFormSection.tsx
|
|
21
|
+
- toga2-commerce/src/pages/Cart/view/cartTable/CartContentsTable.tsx
|
|
22
|
+
- toga2-commerce/src/pages/Cart/view/cartTable/CartTableItem.tsx
|
|
23
|
+
- toga2-commerce/src/components/Inputs/AdvancedInput.tsx
|
|
24
|
+
- toga2-commerce/src/components/BaseButton/BaseButton.tsx
|
|
25
|
+
related:
|
|
26
|
+
- 2.0/apps/toga2-commerce/features/cart-page-config-architecture.md
|
|
27
|
+
- 2.0/apps/toga2-commerce/features/cart-notification-emails.md
|
|
28
|
+
- 2.0/apps/toga2-commerce/features/expedited-shipping-gating.md
|
|
29
|
+
- 2.0/apps/toga2-commerce/features/cart-bundle-submission-and-identity.md
|
|
30
|
+
- 2.0/apps/toga25-supply/workflows/cypress-testing.md
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## What it is
|
|
34
|
+
|
|
35
|
+
The Cypress **e2e** convention set for `toga2-commerce`, and the first **active** e2e coverage
|
|
36
|
+
for the **Cart** page (`cartV2.cy.ts`, slice 1 — 12 tests, verified green by the developer in
|
|
37
|
+
the live app). Read this doc before adding any new spec here. It doubles as the **Phase 0
|
|
38
|
+
behavior oracle** for the planned config-driven cart refactor (see
|
|
39
|
+
[cart-page-config-architecture](../features/cart-page-config-architecture.md)): the cart specs
|
|
40
|
+
pin the exact parity behaviors the prior refactor spike silently lost (notification-email
|
|
41
|
+
auto-population, duplicate-email error surfacing), so the refactor can restructure the DOM and
|
|
42
|
+
still prove behavior parity.
|
|
43
|
+
|
|
44
|
+
This is the toga2-commerce sibling of
|
|
45
|
+
[toga25-supply's Cypress harness](../../toga25-supply/workflows/cypress-testing.md); the
|
|
46
|
+
patterns differ because toga2-commerce is **e2e-only** (no component-test project) and is
|
|
47
|
+
tenant/host-driven.
|
|
48
|
+
|
|
49
|
+
## V2-only convention (V1 is dead)
|
|
50
|
+
|
|
51
|
+
Only the **V2** specs are active: `homePageContentV2`, `itemViewPageV2`, `headerV2`, `footerV2`,
|
|
52
|
+
`getSupportV2`, and now `cartV2`. **Every V1 spec was bulk-commented-out on 2026-03-05**
|
|
53
|
+
(commit `e48c968d`, *"Comment out failing and unreliably passing Cypress tests"*) and is stale —
|
|
54
|
+
do not resurrect a V1 spec; write a V2 replacement. The old fully-commented
|
|
55
|
+
`cypress/e2e/cartPage/cart.cy.ts` was **deleted** this session (it violated the no-commented-code
|
|
56
|
+
standard and was actually an *"add items from filter page"* flow, not a cart-page spec) and is
|
|
57
|
+
superseded by `cartV2.cy.ts`.
|
|
58
|
+
|
|
59
|
+
## The V2 spec pattern
|
|
60
|
+
|
|
61
|
+
- `import { tenant } from '../../support/e2e';` and build **per-tenant data maps** keyed by
|
|
62
|
+
tenant.
|
|
63
|
+
- `beforeEach` sets `cy.viewport(1920, 1080)` then `switch (tenant)` → the login command:
|
|
64
|
+
- `cy.compassAdminLogin(tenant, assortmentsStatus, bundlesStatus, path)` for COMPASS /
|
|
65
|
+
COMPASSCANADA admin;
|
|
66
|
+
- `cy.quadLogin(status, path)` for QUAD.
|
|
67
|
+
- The login command performs the `cy.visit(path)` itself and seeds `zu-user`, `fields-key`,
|
|
68
|
+
`current-client-id`, `synced=false`, and tokens via `onBeforeLoad` (never paste real token
|
|
69
|
+
values into a spec — the command handles auth seeding).
|
|
70
|
+
- **Intercept exact URLs** — `https://api.beta.togahub.com/v2/...`. V2 replaced V1's
|
|
71
|
+
`**/`-wildcard intercepts; keep new intercepts exact so an unstubbed call surfaces instead of
|
|
72
|
+
matching a wildcard.
|
|
73
|
+
- Select elements with `cy.getDataTestId(id)` — the helper takes **only** the id string (not a
|
|
74
|
+
selector), and `defaultCommandTimeout` is **8s**.
|
|
75
|
+
|
|
76
|
+
### Distinguishing the order-for single-user fetch
|
|
77
|
+
|
|
78
|
+
When an admin selects the "order for" user, the app fires a single-user fetch that must be
|
|
79
|
+
stubbed separately from the login-user stubs. It is distinguished by its **field set** — it
|
|
80
|
+
requests `c_erpEntityId`. Intercept it with a regex lookahead so it does not collide with the
|
|
81
|
+
login user request:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
/\/v2\/users\/[^?]+\?(?=.*c_erpEntityId)/
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Seeding the cart (localStorage + rehydrate)
|
|
88
|
+
|
|
89
|
+
Seed commands live in `cypress/support/commands.ts`:
|
|
90
|
+
|
|
91
|
+
- **`seedCart` / `seedSalesQuote`** — 3 standalone Compass items, subtotal **531.31**. These
|
|
92
|
+
match the **current** `CartItems` store shape and are what `cartV2.cy.ts` uses.
|
|
93
|
+
- **`seedCartBundle` / `seedSalesQuoteBundle`** — ⚠ write the **LEGACY** bundle shape with a
|
|
94
|
+
top-level `uuid` field. The current `CartBundles` contract (`src/pages/Cart/types.ts`) is
|
|
95
|
+
**`bundleZuCartUuid` + `bundleProgressContents` with NO top-level `uuid`** (the
|
|
96
|
+
`bundleUuid`/`bundleZuCartUuid` identity contract — see
|
|
97
|
+
[cart-bundle-submission-and-identity](../features/cart-bundle-submission-and-identity.md)).
|
|
98
|
+
**These bundle seeds must be rebuilt to the current shape before any bundle e2e scenario.**
|
|
99
|
+
- **All four seeds only write `localStorage`.** A spec **must `cy.visit('/cart')` AFTER seeding**
|
|
100
|
+
so the zustand `persist` stores rehydrate — in-app navigation does **not** re-hydrate them.
|
|
101
|
+
|
|
102
|
+
**Cart seeding contract** (which keys to seed for which mode):
|
|
103
|
+
|
|
104
|
+
- **Fresh cart:** `zu-cart` + `zu-user` + `fields-key`.
|
|
105
|
+
- **Edit-order:** additionally `in-edit-mode-zu`, `edit-order-uuid-zu`, `zu-cart-sales-quote`,
|
|
106
|
+
`synced="true"`, and visit `/cart?uuid=<orderUuid>`.
|
|
107
|
+
|
|
108
|
+
## Cart-page specifics (what to assert)
|
|
109
|
+
|
|
110
|
+
The cart page fires **no order-submit API**. **"Proceed to Checkout"** only navigates to
|
|
111
|
+
`/order-details?type=cart` (or `?uuid=<salesOrder>&type=editOrder` in edit mode); the actual
|
|
112
|
+
submission lives in **OrderDetails**. So a cart spec asserts **navigation** plus the
|
|
113
|
+
progressively-built **`zu-cart-sales-quote`** payload — not a submit request.
|
|
114
|
+
|
|
115
|
+
## data-testid instrumentation (behavior-anchored selectors)
|
|
116
|
+
|
|
117
|
+
The cart UI was instrumented with `data-testid` attributes so specs anchor on **behavior**, not
|
|
118
|
+
DOM structure, and survive the planned config-driven refactor's DOM restructuring. Naming scheme
|
|
119
|
+
(across `CartPage.tsx`, `CartForm.tsx`, `CartFormSection.tsx`, `CartContentsTable.tsx`,
|
|
120
|
+
`CartTableItem.tsx`):
|
|
121
|
+
|
|
122
|
+
- Actions: `proceed-to-checkout-button`, `clear-cart-button`,
|
|
123
|
+
`cart-item-delete-button-{uuid}`, `edit-order-cancel-button`, `edit-order-add-items-button`,
|
|
124
|
+
`add-new-address-button`, `edit-address-button`, `edit-original-address-button`,
|
|
125
|
+
`add-notification-email-button`, `remove-notification-email-button`.
|
|
126
|
+
- Empty state: `empty-cart-message`, `empty-cart-start-shopping-button`, `empty-cart-support-link`.
|
|
127
|
+
- Inputs: `select-address-input`, `shipping-method-input`, `cost-center-input`,
|
|
128
|
+
`special-instructions-input`, `notification-email-input`, `notification-email-checkbox`.
|
|
129
|
+
- Rows / banners / text: `notification-email-row-{email}`, `duplicate-email-banner`,
|
|
130
|
+
`cart-form-errors-text`, `invalid-address-text`.
|
|
131
|
+
- Config-driven field ids: `cart-form-input-{valueKey}`, `cart-form-display-{valueKey}`,
|
|
132
|
+
`label-button-{valueKey}`, `input-button-{valueKey}`.
|
|
133
|
+
- Shared **`AdvancedInput`** gained: `advanced-input-{valueKey}`, `advanced-input-field-{valueKey}`,
|
|
134
|
+
`advanced-input-toggle-{valueKey}`, `advanced-input-clear-{valueKey}`,
|
|
135
|
+
`advanced-input-option-{optionUuid}`.
|
|
136
|
+
|
|
137
|
+
`BaseButton` / `BaseText` / `BaseInput` / `InfoBanner` already accepted a `dataTestId` prop;
|
|
138
|
+
`IncrementDecrement` already ships `increment-quantity-button` / `decrement-quantity-button` /
|
|
139
|
+
`quantity`.
|
|
140
|
+
|
|
141
|
+
## What slice 1 covers (`cartV2.cy.ts`)
|
|
142
|
+
|
|
143
|
+
12 tests for **COMPASS / COMPASSCANADA admin** (**QUAD** is an explicit, visible `skip`
|
|
144
|
+
placeholder):
|
|
145
|
+
|
|
146
|
+
- Seeded-items render smoke (items, part numbers, prices, summary + all form sections, checkout
|
|
147
|
+
button rendered twice).
|
|
148
|
+
- Quantity increment → subtotal update; item removal → subtotal update.
|
|
149
|
+
- Checkout **disabled** while the form is incomplete; **enabled** once an address is picked
|
|
150
|
+
(shipping method defaults from the seeded quote; cost center auto-seeds from the user's
|
|
151
|
+
`c_erpEntityId`).
|
|
152
|
+
- Clear-cart → empty state; the empty-cart state (message / start-shopping / support link).
|
|
153
|
+
- Order-for user selection → notification-email auto-population (user + supervisor emails,
|
|
154
|
+
protected rows have no remove button); email add (checkbox selected); **case-insensitive
|
|
155
|
+
duplicate blocked** with the sapphire info banner (*"This email has already been added"* — see
|
|
156
|
+
[cart-notification-emails](../features/cart-notification-emails.md)); invalid email rejected;
|
|
157
|
+
an added email is removable.
|
|
158
|
+
|
|
159
|
+
### Not yet covered — slice 2 work-list
|
|
160
|
+
|
|
161
|
+
- **Bundle scenarios** — blocked on the stale bundle seeds (rebuild `seedCartBundle` /
|
|
162
|
+
`seedSalesQuoteBundle` first, above).
|
|
163
|
+
- **Expedited-shipping gating + guardrail modal** — the `cart/fetchUserShippingMethods.json`
|
|
164
|
+
fixture already carries the gated option names (**Next Day Air**, **2nd Day EOB**) with no
|
|
165
|
+
`c_cost`, ready for a gating spec (see
|
|
166
|
+
[expedited-shipping-gating](../features/expedited-shipping-gating.md)).
|
|
167
|
+
- **Edit-order mode.**
|
|
168
|
+
- **QUAD tenant** (currently the visible skip placeholder).
|
|
169
|
+
|
|
170
|
+
## Running the specs
|
|
171
|
+
|
|
172
|
+
- The **only** npm script is `"cypress": "cypress open"` — interactive. There is **no headless
|
|
173
|
+
script and no CI wiring**; `.github/workflows` never runs Cypress. Specs are validated by
|
|
174
|
+
running them locally / interactively.
|
|
175
|
+
- The **dev server must run separately** first: `npm run compass` | `npm run compasscanada` |
|
|
176
|
+
`npm run quad`.
|
|
177
|
+
- **Windows invocation:** `$env:TENANT="compass"; npm run cypress` — `cross-env` is **not**
|
|
178
|
+
installed, so set the env var in the shell.
|
|
179
|
+
- Requires **hosts-file entries** `127.0.0.1 compass|compasscanada|quad.togacommerce` (present on
|
|
180
|
+
this machine but **undocumented in the README** — a new dev must add them).
|
|
181
|
+
|
|
182
|
+
## Gotchas
|
|
183
|
+
|
|
184
|
+
- **`BaseButton` never sets the native `disabled` attribute (test-critical).**
|
|
185
|
+
`src/components/BaseButton/BaseButton.tsx` sets `aria-disabled="true"|"false"` and suppresses
|
|
186
|
+
`onClick` inside `handleClick` — it deliberately leaves the element enabled so a disabled button
|
|
187
|
+
(e.g. **Proceed to Checkout**) still receives mouse events and its `BaseTooltip` (*"Please
|
|
188
|
+
complete all required fields to proceed."*) works. Cypress assertions **MUST** use
|
|
189
|
+
`.should('have.attr', 'aria-disabled', 'true'|'false')`, **never** `.should('be.disabled')` —
|
|
190
|
+
`be.disabled` times out even though the button is visually/functionally disabled. (This cost the
|
|
191
|
+
only 2 first-run failures.)
|
|
192
|
+
- **Seed → then `cy.visit('/cart')`.** The persist stores rehydrate on load only; seeding
|
|
193
|
+
`localStorage` and navigating in-app leaves the stores stale (see seeding section).
|
|
194
|
+
- **Bundle seeds are on the legacy shape** — do not write a bundle spec against them until they
|
|
195
|
+
are rebuilt to the `bundleZuCartUuid` + `bundleProgressContents` contract.
|
|
196
|
+
- **ESLint fails repo-wide in a fresh checkout** because `.eslintrc.cjs` references
|
|
197
|
+
`eslint-plugin-react-compiler`, which is not installed; `npm install --save-dev
|
|
198
|
+
eslint-plugin-react-compiler` fixes it. This is a local-environment issue, not a spec problem.
|
|
199
|
+
|
|
200
|
+
## Change history
|
|
201
|
+
- 2026-07-27 — Added the first active Cart-page e2e coverage (`cartV2.cy.ts`, 12 tests, COMPASS /
|
|
202
|
+
COMPASSCANADA admin; QUAD skipped) and documented the toga2-commerce V2 e2e conventions:
|
|
203
|
+
V2-only (V1 bulk-commented 2026-03-05, dead V1 cart spec deleted), the tenant-login/seed/
|
|
204
|
+
rehydrate contract, the order-for `c_erpEntityId` regex-lookahead intercept, the cart's
|
|
205
|
+
"navigate-not-submit" checkout, the `data-testid` instrumentation scheme, the **`BaseButton`
|
|
206
|
+
`aria-disabled` (never native `disabled`)** test-critical gotcha, the legacy bundle-seed
|
|
207
|
+
blocker, and the interactive-only runner. Slice 1 pins the refactor's Phase 0 parity oracle
|
|
208
|
+
(email auto-population, duplicate-email surfacing). (tcox)
|
|
209
|
+
</content>
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-28
|
|
10
10
|
owners: [jcardinal, dfranks, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/
|
|
@@ -189,10 +189,29 @@ be reattempted.
|
|
|
189
189
|
`$environment = (substr(_Environment::$name, 0, 4) === 'dev-') ? 'dev' : _Environment::$name`.
|
|
190
190
|
Without it, every `_Model_Client_*` load fails. Proven pattern: `_Worker_Startech` (and
|
|
191
191
|
`_Worker_Notification_EmailTemplate`). Do this in `initialize()` or at the top of the method.
|
|
192
|
+
- **`_ApiRequest` logging is a silent no-op unless the Logs DB is registered as
|
|
193
|
+
`DB_CLIENT_LOGS`.** `_ApiRequest`'s logging branch writes through `_Model_Client_Logs_Api`,
|
|
194
|
+
whose `DATABASE` const is `_underscore::DB_CLIENT_LOGS`. A worker whose `initialize()` only
|
|
195
|
+
registers, say, `DB_TEAM` will produce **zero** api-log rows (or, in a CLI/test harness,
|
|
196
|
+
`Unknown database 'ClientLogs'`). So for any worker that makes outbound `_ApiRequest` calls,
|
|
197
|
+
pick one deliberately:
|
|
198
|
+
- **Want the calls logged** → register the logs DB in `initialize()`, e.g.
|
|
199
|
+
`_Database::registerDatabase(_Config::databaseLogs('Logs_True', …), _underscore::DB_CLIENT_LOGS)`
|
|
200
|
+
(proven pattern: `Worker/Team/Sprint.php::initialize()`, `Worker/Team/Transcripts.php::initialize()`).
|
|
201
|
+
- **Don't want them logged** → pass `setLogging(false)` explicitly, with a comment saying why
|
|
202
|
+
(proven: `Worker/Clickup/Fluffer.php`, `Worker/Vapi.php`, `Worker/Ai/Bdr/Vapi.php`).
|
|
203
|
+
|
|
204
|
+
Swapping raw curl for `_ApiRequest` "to get logging" without doing the first of those is the
|
|
205
|
+
common trap — the code looks right and logs nothing.
|
|
192
206
|
- See [architecture.md](../architecture.md) for the always-HTTP-200 rule and the
|
|
193
207
|
commit-before-SQS transaction pattern that the worker relies on.
|
|
194
208
|
|
|
195
209
|
## Change history
|
|
210
|
+
- 2026-07-28 — Added the **`_ApiRequest` logging / `DB_CLIENT_LOGS` gotcha**: api-log rows are
|
|
211
|
+
written via `_Model_Client_Logs_Api` (`DATABASE = DB_CLIENT_LOGS`), so a worker must either
|
|
212
|
+
register the Logs DB under that alias in `initialize()` or pass `setLogging(false)`
|
|
213
|
+
deliberately. Surfaced migrating the Talos AI caller in `Worker/Team/Transcripts.php` off raw
|
|
214
|
+
curl. (jcardinal)
|
|
196
215
|
- 2026-07-21 — Documented that adding a recurring job is **pure data** (a `Core.CronJobs` row;
|
|
197
216
|
CronScheduler Lambda matches via croniter every minute, no code wiring beyond the row — but
|
|
198
217
|
the action must be deployed), and the **biweekly self-gating pattern**: for schedules cron
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-28
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Team/Transcripts.php
|
|
@@ -30,6 +30,7 @@ related:
|
|
|
30
30
|
- ./creating-worker-actions.md
|
|
31
31
|
- ../architecture.md
|
|
32
32
|
- ../../_underscore/features/async-query-execution.md
|
|
33
|
+
- ../../_underscore/features/apirequest-json-content-type.md
|
|
33
34
|
- ../../../1.0/apps/tools/features/talos-kb-documents-admin.md
|
|
34
35
|
- ../../../1.0/apps/test/features/talos-kb-pipeline.md
|
|
35
36
|
---
|
|
@@ -136,6 +137,35 @@ helpers).
|
|
|
136
137
|
[Background Email-Template Worker](./notification-email-template.md); the template is the
|
|
137
138
|
TOGA Technology `True`-client template).
|
|
138
139
|
|
|
140
|
+
### `callTalosEndpoint()` — the shared low-level Talos AI caller
|
|
141
|
+
The private static `callTalosEndpoint()` is the single HTTP path used by **both** AI passes
|
|
142
|
+
(`callAICleaningAPI()` = clean+classify, and `buildRecapEmailBody()` = recap JSON). As of
|
|
143
|
+
2026-07-28 it uses **`_ApiRequest`** instead of raw `curl_init`/`curl_exec`, so every outbound
|
|
144
|
+
Talos call is recorded in the api log. Its contract to the two callers is unchanged:
|
|
145
|
+
`array{success: bool, result?: array, error?: string}`.
|
|
146
|
+
|
|
147
|
+
Three deliberate deviations from the usual worker2 `_ApiRequest` idiom — do not "normalize" them:
|
|
148
|
+
|
|
149
|
+
1. **No `ENCODE__JSON`.** The payload is `json_encode`d by the caller with
|
|
150
|
+
`JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE` and passed as a **ready JSON string with
|
|
151
|
+
payloadEncoding left null**; the response is decoded locally with `json_decode($r, true)`.
|
|
152
|
+
Reasons: (a) `_ApiRequest::execute()` decodes JSON responses with `json_decode($payload)` —
|
|
153
|
+
**no assoc flag** — returning `stdClass`, while both callers index
|
|
154
|
+
`$res['result']['knowledge_doc']` / `$res['result']['recap_json']` as **arrays**;
|
|
155
|
+
(b) `ENCODE__JSON` re-encodes without the unescaped flags, inflating every non-ASCII char of
|
|
156
|
+
a full meeting transcript to `\uXXXX`. Pre-encoding preserves the wire bytes exactly.
|
|
157
|
+
2. **The local retry loop is kept; `setAutoRetry()` is not used.** `setAutoRetry()` waits a
|
|
158
|
+
**flat** delay and retries **every** non-2xx. This endpoint needs exponential backoff
|
|
159
|
+
(`AI_BASE_DELAY_MS * 2^(n-1)`, `AI_MAX_ATTEMPTS = 2`) and must fail fast on 4xx other than
|
|
160
|
+
429. So the surrounding `for` loop is retained and **one `_ApiRequest` is constructed per
|
|
161
|
+
attempt** — which also yields one api-log row per attempt, useful for diagnosing Talos
|
|
162
|
+
flakiness.
|
|
163
|
+
3. **Transport failures arrive as a thrown Exception** from `execute()` (its equivalent of
|
|
164
|
+
`curl_error()`), caught to preserve the same error-array return shape.
|
|
165
|
+
|
|
166
|
+
Timeouts/attempts are class constants: `AI_DEFAULT_TIMEOUT = 600`, `AI_CONNECT_TIMEOUT = 20`,
|
|
167
|
+
`AI_MAX_ATTEMPTS = 2`, `AI_BASE_DELAY_MS`.
|
|
168
|
+
|
|
139
169
|
### Organizer email resolution
|
|
140
170
|
- `resolveOrganizerEmail()` calls Graph `/users/{id}?$select=mail,userPrincipalName` — but
|
|
141
171
|
this needs the **`User.Read.All`** app permission, which the app registration **lacks**.
|
|
@@ -240,12 +270,40 @@ part of the ingestion loop** (raw reads removed). Credential values live only in
|
|
|
240
270
|
`bin/sync-knowledge-bases.php`) to discover `development-team-*` KBs from Bedrock and upsert
|
|
241
271
|
them into `Team.KnowledgeBases`. **Open architectural point:** two competing registries —
|
|
242
272
|
`Team.KnowledgeBases` (worker2) vs `Client_True.VectorIndexes` (Tools UI).
|
|
273
|
+
- **`initialize()` MUST register the Logs DB under `DB_CLIENT_LOGS` or the Talos api-logging is
|
|
274
|
+
a silent no-op.** `_ApiRequest`'s logging branch writes via `_Model_Client_Logs_Api`, whose
|
|
275
|
+
`DATABASE` const is `_underscore::DB_CLIENT_LOGS`. `Transcripts::initialize()` previously
|
|
276
|
+
registered only `DB_TEAM`, so swapping curl → `_ApiRequest` would have produced **zero** log
|
|
277
|
+
rows. It now also registers `Logs_True` under `DB_CLIENT_LOGS` via `_Config::databaseLogs(...)`
|
|
278
|
+
(same pattern as `Worker/Team/Sprint.php::initialize()`). The general rule is on
|
|
279
|
+
[Creating Worker Actions](./creating-worker-actions.md#gotchas) — other workers hitting the
|
|
280
|
+
same Talos host instead pass `setLogging(false)` deliberately
|
|
281
|
+
(`Worker/Clickup/Fluffer.php`, `Worker/Vapi.php`, `Worker/Ai/Bdr/Vapi.php`).
|
|
282
|
+
- **KNOWN LIMITATION (deliberately NOT fixed — do not patch it locally): long AI calls can leave
|
|
283
|
+
api-log rows with null `responseCode`/`responsePayload`.** `_ApiRequest` logs in two halves —
|
|
284
|
+
it inserts the request row and commits **before** the HTTP call, then `save()`s the response
|
|
285
|
+
fields **after**. With `AI_DEFAULT_TIMEOUT = 600`, that second save runs on a connection idle
|
|
286
|
+
for up to 10 minutes, which is exactly the worker2 stale-connection failure mode. Expected
|
|
287
|
+
symptom: on the slowest transcripts the api-log row has a request but no response. A real fix
|
|
288
|
+
belongs **inside `_ApiRequest::execute()`** (reconnect/re-register before the post-call save)
|
|
289
|
+
and affects every framework caller, so it is deferred pending architecture review. Do **not**
|
|
290
|
+
work around it in `Transcripts.php`.
|
|
243
291
|
- **Cross-ACCOUNT S3.** `togaiq` (us-east-1) approved/archive writes use the `[talos]` key;
|
|
244
292
|
`CopyObject` across accounts is impossible, so archive is get(in-memory)+put. (The old
|
|
245
293
|
toga-private cross-account read is no longer in the loop.)
|
|
246
294
|
|
|
247
295
|
## Change history
|
|
248
296
|
|
|
297
|
+
- 2026-07-28 — **`callTalosEndpoint()` migrated from raw curl to `_ApiRequest`** so both AI
|
|
298
|
+
passes are recorded in the api log; `initialize()` now also registers `Logs_True` under
|
|
299
|
+
`_underscore::DB_CLIENT_LOGS` (without it the logging is a silent no-op). Deliberately kept
|
|
300
|
+
the pre-encoded JSON string (no `ENCODE__JSON` — `execute()` returns `stdClass`, and re-encode
|
|
301
|
+
loses `JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE`) and the local exponential-backoff loop
|
|
302
|
+
(no `setAutoRetry()` — it is flat-delay and retries every non-2xx). Also fixed an undefined
|
|
303
|
+
`$data` in the exhausted-retry error message (it was `print_r`'d before assignment; now uses
|
|
304
|
+
the raw response string) and replaced the hardcoded curl connect timeout with
|
|
305
|
+
`AI_CONNECT_TIMEOUT = 20`. Caller return contract unchanged, so no caller changed.
|
|
306
|
+
`php -l` clean; not committed. (jcardinal)
|
|
249
307
|
- 2026-07-13 — Recap email **subject-line date** now displays as `n/j/y` (e.g. `7/9/26`)
|
|
250
308
|
instead of `Y-m-d`, via `DateTime::createFromFormat` with a fallback to the raw
|
|
251
309
|
`$dateSegment`. Cosmetic — `$dateSegment` (S3 filename, metadata sidecar, AI recap body)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -28,7 +28,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
28
28
|
- **talos** (TOGa IQ) — 7 doc(s) → [2.0/apps/talos/INDEX.md](2.0/apps/talos/INDEX.md)
|
|
29
29
|
- **voice-to-voice** (TOGa Voice) — 4 doc(s) → [2.0/apps/voice-to-voice/INDEX.md](2.0/apps/voice-to-voice/INDEX.md)
|
|
30
30
|
- **ai-bdr** (AI-BDR) — 8 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
|
|
31
|
-
- **toga2-commerce** (TOGa Commerce) —
|
|
31
|
+
- **toga2-commerce** (TOGa Commerce) — 10 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
|
|
32
32
|
- **toga25-supply** (TOGa 2.5 Supply) — 11 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
|
|
33
33
|
- **toga-blox** (TOGa Blox) — 8 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
|
|
34
34
|
- **bdr** (BDR) — 0 doc(s) → [2.0/apps/bdr/INDEX.md](2.0/apps/bdr/INDEX.md)
|
package/package.json
CHANGED