toga-ai 1.0.1189 → 1.0.1190

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,16 +6,17 @@ project: Library
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-16
10
- owners: [jcardinal]
9
+ updated: 2026-10-29
10
+ owners: [jcardinal, rgirish]
11
11
  files:
12
12
  - library/app/api/officedepot.php
13
13
  - library/app/api/usps.php
14
14
  related:
15
15
  - ../../toga/features/odp-customer-search-loyalty-persistence.md
16
+ - ../../../../clients/office-depot/profile.md
16
17
  ---
17
18
 
18
- The shared 1.0 address-validation gateway (`App_Api_OfficeDepot::validateAddress` → USPS fallback); open when standardizing/verifying a typed address before save, or debugging a saved-raw-address bug.
19
+ The shared 1.0 address-validation gateway (`App_Api_OfficeDepot::validateAddress` → USPS fallback); open when standardizing/verifying a typed address before save, debugging a saved-raw-address bug, or an address step that hangs.
19
20
 
20
21
  ## Summary
21
22
 
@@ -27,17 +28,23 @@ Entry points:
27
28
  - `library/app/api/officedepot.php` — `App_Api_OfficeDepot::validateAddress()`, the gateway.
28
29
  - `library/app/api/usps.php` — `App_Api_USPS::validateAddress()`, the fallback carrier.
29
30
 
30
- 1. **Endpoint selection.** Calls the ODP **sandbox** endpoint when `App_Registry::inTestMode()` is true (`inTestMode` = `config['internal']['test_mode'] > 0`), and the **live** ODP endpoint otherwise. Production is effectively hard-pinned to live (comment "2024-01-22 JC - Always using production...").
31
- 2. **On a real ODP answer** (HTTP 200, accept or reject) returns ODP's standardized result.
32
- 3. **On any non-200 ODP response** (sandbox down/expired, etc.) falls back to `App_Api_USPS::validateAddress()` and returns the USPS result — a genuine standardization **or** a genuine bad-address rejection both count as a real answer and are returned.
33
- 4. **Only when BOTH ODP and USPS are unavailable** returns `array('success' => true)` with no standardized fields (fail-open, so associates are never hard-blocked by an outage).
31
+ 1. **Endpoint selection.** Calls the ODP **sandbox** endpoint when `App_Registry::inTestMode()` is true (`inTestMode` = `config['internal']['test_mode'] > 0`), and the **live** ODP endpoint otherwise. Production is effectively hard-pinned to live (comment "2024-01-22 JC - Always using production..."). ODP is called directly (no proxy) and only answers allowlisted source IPs — see [Office Depot profile](../../../../clients/office-depot/profile.md) gotchas.
32
+ 2. **On a real ODP answer** (HTTP 200 with `validationResult`, accept or reject) returns ODP's standardized result.
33
+ 3. **On any ODP failure** — non-200, or `$api->execute()` throwing (it throws on a non-JSON body, e.g. a 403 HTML page) — logs `ODP call failed (HTTP <code>)` and falls back to `App_Api_USPS::validateAddress()`. A USPS standardization **or** bad-address rejection is a real answer and is returned.
34
+ 4. **USPS down** = result has `serviceUnavailable => true` (USPS 401/403) or one of the two service-down error strings. Then the gateway fails open: `success => true` + a "could not be validated... click OK to continue" message, so associates are never hard-blocked.
34
35
 
35
36
  ## Gotchas
36
37
 
37
- - **Beta-bypass class of bug (fixed 2026-08-13).** Previously the ODP-failure `else` branch returned `array('success' => true)` with **no** standardized address fields and the USPS fallback was commented out. Beta/test uses the ODP **sandbox**; when the sandbox was down/expired, callers saw `success:true` and saved the **raw typed address** (wrong city, wrong casing) with no validation. Production was unaffected (working live ODP endpoint). The fix restores the USPS fallback so validation only truly fails open when **both** carriers are down.
38
- - **"Both unavailable" must check BOTH USPS service-down strings.** USPS signals unavailability two ways: `'Address validation service is temporarily unavailable'` **and** `'USPS OAuth authentication failed'`. The fail-open condition must match either. Checking only the first wrongly blocks associates when ODP is down *and* USPS OAuth also fails (a php-reviewer pass caught this).
39
- - **Front-end write-back anti-pattern (widespread, mostly UNFIXED).** Many 1.0 front-end callbacks use the gateway response only as a pass/fail **gate**, then rebuild the save payload from the raw DOM inputs — discarding the standardized `address1`/`city`/`state`/`zipCode` the gateway returned. So even a successful validation never persists the corrected address. Known flows still with this anti-pattern: `toga/app/togarefresh2026/servicerequests/finalizequote.php`, `toga/app/togarefresh2026/appointment/select.php`, `toga/app/servicerequests/builder.php`, `toga/app/loaners/checkout.php`, `toga/app/jobjackets/view.php`, and the `togarefresh2023` variants. The 2026-08-13 gateway fix protects all from the beta bypass, but the write-back was only corrected in the `togarefresh2026` customersearch flow (see related doc). When touching any of these, write the standardized address back into the inputs before building the save payload.
38
+ - **`usps.php` is shared — changes must be backward compatible.** Callers: `officedepot.php`, togadesk `desk/includes/classes/class.repair.php` (~L648/711, reads only `success` + address fields), store `mvc/account/address/shipping/verify/post.php` (echoes the JSON). Add new keys; never change existing values or meanings.
39
+ - **USPS fallback cannot validate since 2026-08-01 (OPEN).** USPS added Addresses API access controls: our account gets 401/403 "The requested information is not authorized for access to Addresses API". Fix = an Addresses API license in the USPS Business Portal (cop.usps.com, My Account > API Licenses). Until then, when ODP fails every address fails open unvalidated.
40
+ - **ODP failure used to hang the page (fixed 2026-10-29).** An ODP 403 HTML body made `App_ApiTransaction::execute()` throw → `ajax.php` 500 → browser `Unexpected token '<'` in `jsonDecode` → endless spinner (seen on the `togarefresh2026_customersearch` Next button). Keep `execute()` inside the try/catch and keep the 200-path guards (`isset($responseBody->validationResult)`, `(string)($errorDesc ?? '')`).
41
+ - **"Both unavailable" must check every USPS down signal.** `serviceUnavailable`, `'Address validation service is temporarily unavailable'`, and `'USPS OAuth authentication failed'`. Missing one wrongly blocks associates when ODP and USPS are both down.
42
+ - **Front-end write-back anti-pattern (widespread, mostly UNFIXED).** Many 1.0 front-end callbacks use the gateway response only as a pass/fail **gate**, then rebuild the save payload from the raw DOM inputs — discarding the standardized `address1`/`city`/`state`/`zipCode`. Known flows still with it: `toga/app/togarefresh2026/servicerequests/finalizequote.php`, `toga/app/togarefresh2026/appointment/select.php`, `toga/app/servicerequests/builder.php`, `toga/app/loaners/checkout.php`, `toga/app/jobjackets/view.php`, and the `togarefresh2023` variants. Only the `togarefresh2026` customersearch flow is fixed (see related doc). When touching any of these, write the standardized address back into the inputs before building the save payload.
40
43
 
41
44
  ## Related
42
45
 
43
46
  - `../../toga/features/odp-customer-search-loyalty-persistence.md` — the customersearch save flow, where the write-back fix was applied.
47
+
48
+ ## Change history
49
+ - 2026-10-29 — Catch ODP `execute()` throw (403 HTML → endless spinner) and fall back to USPS; USPS 401/403 adds `serviceUnavailable` and the gateway fails open; USPS license still open (TRUE-83387, commit 0d7aaf08) (rgirish)
50
+ - 2026-08-13 — Restored USPS fallback so beta (ODP sandbox down) no longer saves the raw typed address; fail open only when both carriers are down (jcardinal)
@@ -6,7 +6,7 @@ project: Test
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-10-28
9
+ updated: 2026-10-29
10
10
  owners: ["rgirish"]
11
11
  files:
12
12
  - test/team/aws/create_elastic_beanstalk.php
@@ -60,6 +60,7 @@ Shared by both:
60
60
  - DNS: EB alias zone us-west-2 `Z38NKT9BP95V3O`.
61
61
  - `ebs/register_instance_to_shared_application_load_balancer.php` hardcodes us-west-2 but skips any env name containing `-prod` — it does nothing here.
62
62
  - The 2.0 DBs (`writer.core` / `writer.client.database.togahub.com`, us-east-1, 10.201.x) are now cross-region for these apps — a small part of their traffic.
63
+ - Outbound IP: all 3 private web subnets route through NAT `nat-0dc3b341a685b0bac` = `54.203.171.146` (VPC owner account 339712893798, not open to developers). This is the IP an IP-allowlisting partner (ODP) must allow.
63
64
 
64
65
  TOGa Retail: 2 instances. SNI: `togahub.com` / `*.togahub.com`, `*.toga.agilantsolutions.com` / `*.toga.togatechnology.com`. DNS: toga-prod `toga.togatech.com`, `odp.toga.togatechnology.com` (A alias), `chat.togahub.com` (CNAME, TTL 300); legacy `agilantsolutions.com` zone: `odp-toga`, `odp.toga`, `staples.toga`, `synnex.toga` (A alias). ODP hosts 302 to the Azure SAML login.
65
66
 
@@ -101,15 +102,16 @@ ODP calls it "sandbox"; internally it **is beta** (replaces the old shared `agil
101
102
  Done for TOGa Retail (TRUE-83361) and TOGa Desk (TRUE-83360), us-east-1 → us-west-2. EB app/env/pipeline names are per region, so the new region reuses them. New region = new Redis, so every user is logged out once.
102
103
 
103
104
  1. Create the env in the new region (steps above) on the target VPC; check it reaches the DB network and the DB SG allows 3306.
104
- 2. Request ACM certs in the new region. If the DNS validation CNAMEs already exist in the same account, they issue in minutes (validation records are per account + domain, shared across regions).
105
- 3. Point `SESSION_REDIS_SAVE_PATH` at the Redis cluster in the **same** region; add the env's instance SG to that Redis SG on 6379.
106
- 4. Create the pipeline in the new region with that region's artifact bucket.
107
- 5. Test each host against the new env CNAME (`curl --resolve` or Host header): same status and page size as the old env.
108
- 6. UPSERT every DNS record (both accounts) to the new env's alias zone.
109
- 7. Wait until the old ALB `RequestCount` is 0 (staff browsers keep old keep-alive connections ~10 min after the DNS change).
110
- 8. Save the old env config to a file (EB saved configs die with the EB app — see Gotchas).
111
- 9. Remove only the old env's instance SG rule from the old-region Redis SG ingress (us-east-1 `sg-03e79bcb9c105e100`). Never delete the us-east-1 `togadesk-sessions` cluster — Vision, View, Store, Walmart and Tools still use it (TRUE-83179).
112
- 10. Delete the old pipeline, terminate the old env, delete the old EB app.
105
+ 2. Find the new outbound NAT IP. If the app calls a partner that allowlists IPs (ODP for TOGa Retail), get that IP allowlisted **before** the DNS cutover. TRUE-83361 missed this: ODP returned 403 from the cutover on (TRUE-83387).
106
+ 3. Request ACM certs in the new region. If the DNS validation CNAMEs already exist in the same account, they issue in minutes (validation records are per account + domain, shared across regions).
107
+ 4. Point `SESSION_REDIS_SAVE_PATH` at the Redis cluster in the **same** region; add the env's instance SG to that Redis SG on 6379.
108
+ 5. Create the pipeline in the new region with that region's artifact bucket.
109
+ 6. Test each host against the new env CNAME (`curl --resolve` or Host header): same status and page size as the old env.
110
+ 7. UPSERT every DNS record (both accounts) to the new env's alias zone.
111
+ 8. Wait until the old ALB `RequestCount` is 0 (staff browsers keep old keep-alive connections ~10 min after the DNS change).
112
+ 9. Save the old env config to a file (EB saved configs die with the EB app — see Gotchas).
113
+ 10. Remove only the old env's instance SG rule from the old-region Redis SG ingress (us-east-1 `sg-03e79bcb9c105e100`). Never delete the us-east-1 `togadesk-sessions` cluster — Vision, View, Store, Walmart and Tools still use it (TRUE-83179).
114
+ 11. Delete the old pipeline, terminate the old env, delete the old EB app.
113
115
 
114
116
  ### Where DNS lives
115
117
 
@@ -141,6 +143,7 @@ SAML ACS URLs are hard-coded (store: `prudential.togahub.com/?acs`, then redirec
141
143
  - **Old env names in other docs are history.** `prudential-store-prod`, `true-togacommerce-prod`, `spglobal-store-prod`, `*-togadesk-prod` and pipeline `Store-Production` no longer exist.
142
144
 
143
145
  ## Change history
146
+ - 2026-10-29 — us-west-2 outbound NAT IP; region move must get partner IP allowlists (ODP) before DNS cutover (TRUE-83387) (rgirish)
144
147
  - 2026-10-28 — TOGa Desk prod moved to us-west-2 (3 x t4g.large, 4 certs, 7 DNS records), wait-for-drain step, keep us-east-1 Redis (TRUE-83360) (rgirish)
145
148
  - 2026-10-28 — TOGa Retail prod moved to us-west-2 (next to the 1.0 DBs), region-move steps, EB app/SG/cert gotchas (TRUE-83361) (rgirish)
146
149
  - 2026-10-27 — TOGa Retail sandbox for ODP (= beta internally), `-sandbox` host strip, nonprod S3 grants, `_beta` drift (TRUE-83279) (rgirish)
@@ -6,11 +6,12 @@ project: TOGa
6
6
  client: office-depot
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-09-16
10
- owners: [jcardinal]
9
+ updated: 2026-10-29
10
+ owners: [jcardinal, rgirish]
11
11
  files:
12
12
  - toga/app/togarefresh2026/customersearch/view.php
13
13
  - library/app/model/customer.php
14
+ - library/app/model/servicerequest.php
14
15
  related:
15
16
  - ./bundleconfirmation-cart-preservation.md
16
17
  - ../../library/features/address-validation-gateway.md
@@ -43,5 +44,11 @@ The "Existing PC Services" flow — `content=togarefresh2026_customersearch`, Ne
43
44
  - **Backfill deferred.** Records that went null between 2023-11-28 and this fix are NOT backfilled — a separate one-time backfill is still owed.
44
45
  - **CRITICAL pre-existing SQL-injection risk (unfixed — needs its own ticket).** The address-lookup query in `saveNewCustomer` (`library/app/model/customer.php`, ~lines 538–543) interpolates `streetAddress`/`line2`/`city`/`state`/`zip` into `$sqlCheckAddress` with **no `App_Database::sqlEscape()` and no casting**. This was not introduced by this session and was left unfixed per scope; it must be escaped per the SQL-injection rules before this method is touched again.
45
46
 
47
+ - **Loyalty lookup survives an ODP outage, but only partly.** `library/app/model/servicerequest.php` `ajaxRefreshCustomerSearch` / `ajaxCustomerSearch` wrap ODP `execute()` in try/catch and fall through to a local TOGa `Customers` search (phone/email/name/stored `merchantId`). During an ODP block (e.g. IP not allowlisted, TRUE-83387) associates see only customers already in TOGa. The catch blocks are empty — an ODP failure leaves no log line.
48
+
46
49
  ## Related
47
50
  - `./bundleconfirmation-cart-preservation.md`
51
+
52
+ ## Change history
53
+ - 2026-10-29 — Noted: loyalty lookup falls back to local TOGa Customers search when ODP fails (TRUE-83387) (rgirish)
54
+ - 2026-09-16 — merchantId persisted through togarefresh2026 save; standardized address written back (jcardinal)
@@ -6,8 +6,8 @@ project: Vision
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-10-24
10
- owners: ["jcardinal"]
9
+ updated: 2026-10-29
10
+ owners: ["jcardinal", "rgirish"]
11
11
  files:
12
12
  - vision/.ebextensions/009_setup_phpini.config
13
13
  - vision/.ebextensions/020_setup_git_libraries.config
@@ -74,6 +74,8 @@ Vision serves every `.php` file directly, so deny rules are the only guard:
74
74
  5. ~100 `mysql_*` calls (dead since PHP 7.0).
75
75
  6. php-fpm warns "Module redis/igbinary/msgpack already loaded" — `02-install-php-redis.sh` loads the extension ini twice (same in tools).
76
76
  7. `config.prod.ini` still has old us-west-2 RDS hosts + unmounted catalog paths.
77
+ 8. **Copyright watchdog is off since the AL2023 port (fe8940ce, 2026-10-23).** `cr.php` (called from the shared library JS on every 1.0 page) sends `Access-Control-Allow-Origin` twice: `.htaccess` `Header set` + PHP `header()` no longer merge under php-fpm, so the browser gets `*, *`, rejects it, and the check fails quietly (console CORS error only). Fix = drop one of the two headers. Before that, check `Vision_Log.CopyrightHosts`: once the check works, any calling host not listed there is redirected to `unauthorized.html` (`toga.togatech.com` etc. are listed with `redirectTo` NULL). Left as is on purpose in TRUE-83387.
77
78
 
78
79
  ## Change history
80
+ - 2026-10-29 — Found: double CORS header disables the `cr.php` copyright watchdog since the AL2023 port; not changed (TRUE-83387) (rgirish)
79
81
  - 2026-10-23 — Created: Vision revived on PHP 8.5 / AL2023 in the prod account; folder hook, Redis sessions, s3fs `asifiles`, deny rules, open security list (TRUE-83056) (jcardinal)
@@ -18,7 +18,7 @@ project: TOGa Voice
18
18
  client: office-depot
19
19
  type: profile
20
20
  status: active
21
- updated: 2026-10-27
21
+ updated: 2026-10-29
22
22
  owners: [akhokhani, mhammontree, jcardinal, snaredla, rgirish]
23
23
  files: []
24
24
  related:
@@ -29,6 +29,7 @@ related:
29
29
  - ../../1.0/apps/togadesk/architecture.md
30
30
  - ../../1.0/apps/toga/features/odp-esd-mcafee-descriptions.md
31
31
  - ../../1.0/apps/toga/features/odp-tech-services-job-jacket-answers.md
32
+ - ../../1.0/apps/library/features/address-validation-gateway.md
32
33
  ---
33
34
 
34
35
  Office Depot uses TOGa Voice for inbound store-associate tech support and Desk ticketing; open for client scope and links to its voice configuration.
@@ -51,3 +52,7 @@ Office Depot is the production support client of the standalone Python/LiveKit T
51
52
  ## Gotchas
52
53
 
53
54
  The audience is store staff. Preserve the retail-misroute branch and verify store/work order before linking a repeat caller from a shared phone. Call artifacts contain personal information; retain only aggregate diagnostics in shared knowledge. A created ticket does not prove post-call attachments succeeded.
55
+
56
+ - **ODP APIs accept only allowlisted source IPs.** `inpr.odazcloud.com` (Akamai) returns 403 HTML to any other IP. There is no proxy any more: `proxy.agilantsolutions.com` (EB `agilant-proxy-prod`) was terminated 2026-10-23 and library calls ODP directly (commit 75aac84a). So the IP ODP must allowlist is the outbound NAT IP of the env that calls it. TOGa Retail prod (us-west-2) = `54.203.171.146` (NAT `nat-0dc3b341a685b0bac`, shared VPC `true-production-vpc-2`, owner account 339712893798); allowlist request sent 2026-10-29, pending (TRUE-83387). Any region/VPC move: get the new NAT IP allowlisted before the DNS cutover — see [legacy 1.0 web apps in toga-prod](../../1.0/apps/test/workflows/legacy-1-0-web-apps-in-toga-prod.md).
57
+ - **While ODP is blocked:** address validation falls back to USPS ([gateway](../../1.0/apps/library/features/address-validation-gateway.md)); loyalty lookup shows only customers already in TOGa ([customer search](../../1.0/apps/toga/features/odp-customer-search-loyalty-persistence.md)).
58
+ - **Dead proxy still called:** `toga/_/browser/form/input/autocomplete/customerlookup.php` (~L35) still calls `proxy.agilantsolutions.com`; only used by the DOA page `toga/app/jobjackets/viewDOA.php`. Unfixed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.1189",
3
+ "version": "1.0.1190",
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",