toga-ai 1.0.1132 → 1.0.1133
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/1.0/apps/library/features/db-connection-lifecycle-and-reconnect.md +6 -3
- package/knowledge/1.0/apps/library/features/php-8-5-runtime-compatibility.md +8 -2
- package/knowledge/1.0/apps/worker/INDEX.md +1 -1
- package/knowledge/1.0/apps/worker/architecture.md +3 -3
- package/knowledge/1.0/apps/worker1.5/INDEX.md +1 -1
- package/knowledge/1.0/apps/worker1.5/architecture.md +2 -2
- package/knowledge/1.0/apps/worker1.5/workflows/diagnosing-crons-not-running.md +17 -3
- package/knowledge/1.0/standards/backend-php.md +1 -1
- package/knowledge/INDEX.md +2 -2
- package/knowledge/clients/aig/profile.md +2 -1
- package/knowledge/clients/compass-canada/profile.md +2 -1
- package/knowledge/clients/nycdoe/features/servicenow-integration.md +3 -2
- package/knowledge/clients/pcmaticb2b/profile.md +2 -2
- package/knowledge/clients/prudential/features/device-information-import-and-contact-linking.md +8 -6
- package/knowledge/clients/staples/profile.md +2 -1
- package/knowledge/clients/walmart/profile.md +3 -2
- package/package.json +1 -1
|
@@ -50,7 +50,7 @@ $lastErrorNumber = mysqli_errno($link); // query.php:173 <- the 2006 ch
|
|
|
50
50
|
|
|
51
51
|
`mysqli_report(MYSQLI_REPORT_STRICT)` is set in `App_Error::initialize()` (`library/app/error.php:252`). **STRICT raises mysqli exceptions only for CONNECTION errors** — everything else stays a warning. That single line is why a dead connection is a warning and not something you can catch.
|
|
52
52
|
|
|
53
|
-
### The built-in 2006 reconnect (fixed 2026-10-23, TRUE-82812)
|
|
53
|
+
### The built-in 2006 reconnect (fixed 2026-10-23, TRUE-82812 + TRUE-83163)
|
|
54
54
|
|
|
55
55
|
`App_Query::execute()` retries MySQL error **2006** at `query.php:~176`, capped by `$connectionAttemptsToMake = 2` (`query.php:85`). Until `library` `96cffb23` it was broken:
|
|
56
56
|
|
|
@@ -58,7 +58,9 @@ $lastErrorNumber = mysqli_errno($link); // query.php:173 <- the 2006 ch
|
|
|
58
58
|
2. **Cache handed back the same handle (fixed).** Retry called `databaseClose()` + `registerDatabaseConnect()`; `databaseClose()` never clears `App_Database::$connections[$hostname]`, so it got the same **closed** mysqli → PHP Error `mysqli object is already closed` at `query.php:168` (1.8K events, worker1.5 `startech/import_supporting_records_pcmaticb2b.php` → `cronFinished()`). Now calls `App_Database::reconnect()`.
|
|
59
59
|
3. **Stale `$link` (fixed).** `$link` is now re-read after the reconnect.
|
|
60
60
|
|
|
61
|
-
**
|
|
61
|
+
4. **PHP 8 exception path (TRUE-83163).** `app/error.php` sets `MYSQLI_REPORT_STRICT`, so on PHP 8.1+ a 2006 can arrive as `mysqli_sql_exception`, which skipped the errno branch. The loop now also catches `mysqli_sql_exception` code 2006 and retries (private `reconnectAfterServerGone()`, main or `_read`). Other codes rethrow. Same "already closed" error also hit Compass OD sales orders and Prudential FedEx delivered crons.
|
|
62
|
+
|
|
63
|
+
**Open limit (not fixed):** links on the same DB host share one mysqli object, so after `reconnect()` other registry links on that host (e.g. `db_toga` vs `db_common`) still hold the closed object. On a `_read` retry, TRUE-83163 drops the `_read` cache slot that held the stale link.
|
|
62
64
|
|
|
63
65
|
### A `try`/`catch` around a DB call cannot help
|
|
64
66
|
|
|
@@ -76,7 +78,7 @@ Canonical mechanics and the two sanctioned local opt-outs are in the [back-end s
|
|
|
76
78
|
| `App_Database::reconnect($link)` | `database.php:446` | The connection is already presumed dead and you must get a working one (e.g. inside a shutdown handler). |
|
|
77
79
|
| `App_Database::queryForked($sql, $link)` | `database.php:465` | A write you want to run in a **separate PHP process**, so the parent cron never holds its connection open across it. Falls back to a direct `query()` on Windows. |
|
|
78
80
|
|
|
79
|
-
**`reconnect()` is the
|
|
81
|
+
**`reconnect()` is the only thing that forces a new handle** (cache bug #2 above):
|
|
80
82
|
|
|
81
83
|
```php
|
|
82
84
|
self::databaseClose($linkRegistryString);
|
|
@@ -117,6 +119,7 @@ The alert pattern as first shipped in Compass ODP cron 5 (`worker` commit `afce3
|
|
|
117
119
|
- `library/app/error.php:5-8` has `IMMEDIATELY_TERMINATE_INSTANCE_IF_ERROR_STRING_CONTAINS`, matched **on substring before severity**. A recoverable warning could terminate an EC2 instance.
|
|
118
120
|
|
|
119
121
|
## Change history
|
|
122
|
+
- 2026-10-23 — TRUE-83163: 2006 retry also catches `mysqli_sql_exception` 2006 (PHP 8.1+ STRICT path) and drops the stale `_read` cache slot. (jcardinal)
|
|
120
123
|
- 2026-10-23 — TRUE-82812: 2006 retry now uses `reconnect()` and re-reads `$link` (`96cffb23`); noted shared-handle and `_read` limits, `cronFinished()` catch gap. (snaredla)
|
|
121
124
|
- 2026-10-08 — Shutdown alarm can fire falsely (warning inside the SOAP call exits too); Compass cron 5 replaced it with an externalId lookup (`fd426d9b`). Added the remote-id rule to the Summary. (bala)
|
|
122
125
|
- 2026-09-25 — Corrected the cron 5 pattern: it stamps via `queryForked` with no `keepAlive`, and disarms the alarm before the forked write is confirmed; noted the monitor backstop. (rgirish)
|
|
@@ -66,6 +66,10 @@ Fix patterns (TRUE-83059 `d28f9b23`, TRUE-83055 PR #905):
|
|
|
66
66
|
| 8.2 | dynamic property | declare it on the class (TRUE-83055: `App_Api_Carrier_ShipmentRequest::$shipToAddressLine2`, `App_Email_AigTemplates_SendAigSupportTicketReplyNotification::$contact`) |
|
|
67
67
|
| 8.1 | `SoapClient` overrides without return types (`app/soaptransaction.php`) | return-type attributes + 6th `$uriParserClass` arg |
|
|
68
68
|
| 8.5 | `case 'x';` (semicolon after `case`) — vendored `php-imap2/src/Roundcube/ImapClient.php:2580` flooded worker1.5's `worker-cron.log` | `case 'x':` (library `53ea57f8`) |
|
|
69
|
+
| 8.5 | warning "The float ... is not representable as an int, cast occurred" — `App_String::encrypt/decrypt`, `underscoreEncrypt/decryptUnderscoreEncrypt` key loop `$i += $i` turns float at ~56+ chars, then `$i % 255` (killed Walmart order reminder/processor crons) | private `keyByteModulo()`: wrap the float to signed 64-bit (`fmod` 2^64) before `% 255` — exact PHP 7/8.2 result, old links still decrypt (0 mismatches / 30,720 cases) (TRUE-83163) |
|
|
70
|
+
| 8.x | `socket_set_timeout()` (`phpmailer/class.smtp.php`) | `stream_set_timeout()` (TRUE-83163) |
|
|
71
|
+
| 8.1 | `null` into `strlen`/`trim`, null array key (`hjson/HJSONParser.php`, `catalog/content.php`, `App_Database::buildDoubleFieldArray` / `buildAssocArray`) | `(string)` cast / `?? ''`; `buildAssocArray` casts the key, so a wrong column index stays loud (TRUE-83163) |
|
|
72
|
+
| 8.2 | dynamic property | declared: `App_ASNRaw` props, `App_ApiResult::$success`, NetSuite `SearchTextNumberField` / `SearchEnumMultiSelectField::$operatorSpecified` (TRUE-83163) |
|
|
69
73
|
|
|
70
74
|
State on `_production`: `php -l` on PHP 8.4 shows no deprecations in `app/` or `browser/`.
|
|
71
75
|
|
|
@@ -91,20 +95,22 @@ ext-imap is gone from PHP 8.4+, so on 8.5 hosts (worker1.5) every `imap2_*` call
|
|
|
91
95
|
|
|
92
96
|
## Gotchas
|
|
93
97
|
|
|
94
|
-
- **Still open
|
|
98
|
+
- **Still open:** vendored `phpseclib/`, `LightSaml/`, `simplesaml/`, `jpgraph/`, `mobile_detect/`, `invoiceprocessor/` raise 8.4 compile-time deprecations (implicit nullable, optional-before-required, `${var}`); `app/string.php:277` `mb_convert_encoding(..., 'HTML-ENTITIES')` (8.2) — no byte-identical 7.2-safe replacement found. Logged, not fatal.
|
|
99
|
+
- **⚠ Never "fix" the `App_String` encrypt key loop with real modular math.** It changes the key for long strings, so every issued link (Walmart unsubscribe/register) stops decrypting. `keyByteModulo()` must reproduce the old 64-bit wraparound exactly.
|
|
95
100
|
- **Warnings and notices still exit.** Only deprecations were softened — an "offset on null" or "undefined index" still kills the request on any PHP version.
|
|
96
101
|
- **`file_get_contents` to IMDS or any optional endpoint:** the `ignore_errors` stream option covers only HTTP status — a refused or slow connection still warns, and App_Error exits. Wrap the call in a local `set_error_handler` / `restore_error_handler` and check for a `200` status line (togaview TRUE-83060).
|
|
97
102
|
- **The `instance-data` hostname does not resolve on AL2023.** Use `169.254.169.254` only in new code. Old uses: `App_Error_Capture::AWS_METADATA_HOSTS` falls back to it when `169.254.169.254` returns non-200 (e.g. the tags path with instance metadata tags off), so the cron log shows `getaddrinfo for instance-data failed` warnings (noise, capture still works). `App_Cloud::getInstanceId()` (`app/cloud.php` ~L711) calls only `instance-data`, so it returns `''`, and `App_Framework::getInstanceId()` caches that `''` in `instanceId.txt`. That is why worker1.5 writes `instanceId = ''` to `Common.CronJobExecutions` and `Logs.Emails`. `app/error.php` ~L642 (Sentry AWS context) also uses it.
|
|
98
103
|
- **PHP 7.2 safety was checked by reading, not running** (no 7.2 binary locally on PR #905). The guards use `PHP_VERSION_ID` (5.2.7+) and `?type` (7.1+). Run a 7.2 smoke test if a 7.2 app breaks after this change.
|
|
99
104
|
- **Do not "repair" the removed `App_Edi::cleanText` block.** A working version would replace every character 0-127 (all letters and digits) with spaces and blank every EDI file.
|
|
100
105
|
- Local dev on PHP 8.2 does not show 8.4/8.5 deprecations — test with an 8.5 binary.
|
|
101
|
-
- **PHP 8 string-to-number comparison
|
|
106
|
+
- **PHP 8 string-to-number comparison changed (`"abc" != 0` is now true).** `api` subscription: a non-numeric quantity compared to an int looped forever. worker1.5 `get_new_detectprotectservices.php`: HTML junk rows passed a `$cols[0] != 0` check and inserted duplicate recNo 0. Cast `(int)` before comparing.
|
|
102
107
|
- **OAuth IMAP (`imap2_open(..., OP_XOAUTH2)`) always uses the Roundcube path — even on PHP 7.2 with ext-imap.** So `HeaderInfo::fromMessage` runs on the 7.2 worker too. Gate every php-imap2 behavior change on `!IMAP2_RETROFIT_MODE` (= `function_exists('imap_open')` at bootstrap load) so 7.2 stays byte-identical.
|
|
103
108
|
- **php-imap2 gaps still open (no-ext-imap only):** group end writes `grp: ` not `grp: ;`; `mutf7ToUtf8` / `utf8ToMutf7` / `utf8` return input; `mailCompose` returns `false`; `rfc822ParseHeaders` still needs zbateson (not on importer paths). `imap_binary` line width (60, CRLF) not compared to real ext-imap. 7.2 safety checked by reading only.
|
|
104
109
|
- **Needs `mbstring` + `iconv`** on the 8.5 host for the Polyfill decode paths — check `php -m`.
|
|
105
110
|
- **A library fix reaches worker1.5 only after a worker1.5 redeploy** ([worker1.5 architecture](../../worker1.5/architecture.md)).
|
|
106
111
|
|
|
107
112
|
## Change history
|
|
113
|
+
- 2026-10-23 — `App_String` key float overflow fixed with exact old result; more deprecations cleared; open list updated (TRUE-83163) (jcardinal)
|
|
108
114
|
- 2026-10-23 — php-imap2 works without ext-imap: Polyfill loaded + filled in; OAuth/Roundcube gotcha; open gaps (TRUE-83148, `4a2c363f`) (jcardinal)
|
|
109
115
|
- 2026-10-23 — `instance-data` does not resolve on AL2023 (empty instance id, log noise); deprecations-continue behavior confirmed in the worker1.5 cron log (TRUE-83107). (bala)
|
|
110
116
|
- 2026-10-23 — `case 'x';` 8.5 deprecation fixed in vendored php-imap2 (TRUE-83116). (bala)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Summary |
|
|
4
4
|
|-----|---------|
|
|
5
|
-
| [Worker (1.0 Framework) Architecture](architecture.md) |
|
|
5
|
+
| [Worker (1.0 Framework) Architecture](architecture.md) | RETIRED 1.0 background-job tier (history only); every cron now runs on worker1.5. |
|
|
6
6
|
| [Canon received-PO → NetSuite sales order sync](features/canon-received-po-netsuite-order-sync.md) | Canon-only worker cron that reads Vision customer POs and creates the matching NetSuite sales order (bundles exploded, prefixes stripped); open before touching |
|
|
7
7
|
| [Compass MA Sales Order Exception Report](features/compass-ma-sales-order-exception-report.md) | Worker cron emailing ops the "Compass Refresh Exception Report" of `MA%` Compass sales orders whose ODP NetSuite order isn't keyed yet; open for the two-hop bri |
|
|
8
8
|
| [Compass Approval Reminder & 30-Day Auto-Cancel Crons (1.0 worker)](features/compass-manager-approval-reminder-emails.md) | Per-tenant Compass worker crons that remind approvers daily (8:00) and auto-cancel orders still unapproved after 30 days (8:30); open before touching approval a |
|
|
@@ -5,7 +5,7 @@ repo: worker
|
|
|
5
5
|
project: Worker
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
|
-
status:
|
|
8
|
+
status: deprecated
|
|
9
9
|
updated: 2026-10-23
|
|
10
10
|
owners: [jcardinal, sking, bala]
|
|
11
11
|
files:
|
|
@@ -26,9 +26,9 @@ related:
|
|
|
26
26
|
- ../worker1.5/architecture.md
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
RETIRED 1.0 background-job tier (history only); every cron now runs on worker1.5.
|
|
30
30
|
|
|
31
|
-
> **
|
|
31
|
+
> **Retired (2026-10-22):** the `worker` repo runs nothing. Every cron moved to worker1.5 (TRUE-83078). Make code fixes in worker1.5; read this doc for history only. See [../worker1.5/architecture.md](../worker1.5/architecture.md).
|
|
32
32
|
|
|
33
33
|
## Summary
|
|
34
34
|
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
| [Worker 1.5 (1.0 Framework) Architecture](architecture.md) | The single-server 1.0 cron tier that replaced worker's 7-role fleet; open for how the crontab is built from schedules/*.json, the overlap check, and what the cr |
|
|
6
6
|
| [worker1.5 — Committed Secrets & Credentials in Logs (debt register)](features/committed-secrets-and-log-exposure.md) | Open security debt in worker1.5: a GitHub token committed in `ebs/git.json` and a cron that writes OptimumDesk passwords into the cron log; open before touching |
|
|
7
7
|
| [worker1.5 PHP ini overrides on EB AL2023 (memory_limit 11G, zz-application.ini)](features/php-ini-memory-limit-eb.md) | How worker1.5 sets PHP ini values (memory_limit 11G) on EB `worker15-production`; open before changing any PHP ini setting or wondering why the EB console value |
|
|
8
|
-
| [Diagnosing worker1.5 crons that stopped running (single-server 1.0 tier)](workflows/diagnosing-crons-not-running.md) | Steps to tell "worker1.5 crons are not running" from "a downstream system is failing", find the cause on the box, and recover
|
|
8
|
+
| [Diagnosing worker1.5 crons that stopped running (single-server 1.0 tier)](workflows/diagnosing-crons-not-running.md) | Steps to tell "worker1.5 crons are not running" from "a downstream system is failing", find the cause on the box, and recover, plus how to triage failing crons |
|
|
@@ -30,7 +30,7 @@ The single-server 1.0 cron tier that replaced worker's 7-role fleet; open for ho
|
|
|
30
30
|
|
|
31
31
|
## Summary
|
|
32
32
|
|
|
33
|
-
`worker1.5` is the 1.0 `App_` background-job tier on **one Elastic Beanstalk server, PHP 8.5, AL2023** (TRUE-83078). It
|
|
33
|
+
`worker1.5` is the 1.0 `App_` background-job tier on **one Elastic Beanstalk server, PHP 8.5, AL2023** (TRUE-83078). It replaced the legacy `worker` repo, which is retired: every worker 1.0 cron moved here on 2026-10-22. Same folder layout (`crons/<area>/...`, `crons/toga2/<client>/`). The 7 role schedules were merged into one file. Role election, heartbeat, stale-worker termination and per-role Elastic IPs are gone: one server runs every active job. Builds on `library`.
|
|
34
34
|
|
|
35
35
|
**Critical rules:** the schedule `schedules/cron.<ENVIRONMENT>.json` is the source of truth. Production is `cron.worker.json`. A script not in it never runs. A bad `schedule` (not 5 fields) or a `%` in a command aborts the whole crontab install on deploy. Keep cron's default `/bin/sh`: `App_Framework::isProcessRunning()` only ignores `/bin/sh` parent rows, so another shell breaks the already-running check (TRUE-83121). Any PHP warning exits the request or cron (see the backend-php standard, *Any PHP warning terminates the request*). Host setup uses `.platform/hooks` (prebuild/predeploy/postdeploy), not `.ebextensions`; never apply `worker` (AL1) EB fixes here blindly. PHP ini values come from the repo (`ebs/setup_phpini.php` → `/etc/php.d/zz-application.ini`), not the EB console; memory_limit is 11G per process on a 16 GiB m7g.xlarge, so watch crons running at the same time. See [[php-ini-memory-limit-eb]].
|
|
36
36
|
|
|
@@ -59,4 +59,4 @@ No CloudWatch log group: every job's output is `/var/log/worker-cron.log` on the
|
|
|
59
59
|
- **IMAP crons depend on library's php-imap2 patches.** No ext-imap on PHP 8.5; php-imap2 comes from the cloned `library`, not composer. Desk, Quad and ~20 Compass / Compass Canada IMAP crons crashed until library `4a2c363f` (see [PHP 8.5 § IMAP without ext-imap](../library/features/php-8-5-runtime-compatibility.md#imap-without-ext-imap-php-imap2)). togadesk is cloned as `ontrack` → `/var/www/html/ontrack`; the prod Desk importer is `php /var/www/html/crons/../ontrack/crons/tickets_prod.php` (TRUE-83148).
|
|
60
60
|
- **Do not read files that are not in this repo on a 1.0 page.** The page read `schedules/cron.worker.catalog.json` (old worker only). The warning became `exit` and the page died about 474 times an hour (9,387 total), probably from a health check on `/`. Fixed in TRUE-83107 (c903b20).
|
|
61
61
|
- **`App_Misc::getFileList` uses `readdir` and does not sort.** Any loop that keeps state per file must set that state on every file. The old loop set `$jsonContents` only for a non-empty category, so `cron.worker.json` reused the previous file's rows, or warned (and exited) if it came first.
|
|
62
|
-
- **Cron log noise you can ignore:** `PHP Deprecated: ...` lines (the job continues) and `getaddrinfo for instance-data failed` warnings (see [PHP 8.5 compatibility § Gotchas](../library/features/php-8-5-runtime-compatibility.md)). Two open faults show up in the log: the OptimumDesk sync prints a credential row into its exception ([Error capture 1.0 § Gotchas](../library/features/error-capture-1-0.md)), and empty-From emails fail at SES every run ([Email queue § Gotchas](../library/features/email-queue-attachments.md)).
|
|
62
|
+
- **Cron log noise you can ignore:** `PHP Deprecated: ...` lines (the job continues) and `getaddrinfo for instance-data failed` warnings (see [PHP 8.5 compatibility § Gotchas](../library/features/php-8-5-runtime-compatibility.md)). Two open faults show up in the log: the OptimumDesk sync prints a credential row into its exception ([Error capture 1.0 § Gotchas](../library/features/error-capture-1-0.md)), and empty-From emails fail at SES every run ([Email queue § Gotchas](../library/features/email-queue-attachments.md)). Triage steps and known entries: [diagnosing workflow § Triage](workflows/diagnosing-crons-not-running.md#triage-failing-crons-in-worker-cronlog).
|
|
@@ -7,7 +7,7 @@ client: shared
|
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
9
|
updated: 2026-10-23
|
|
10
|
-
owners: [bala]
|
|
10
|
+
owners: [bala, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker1.5/ebs/cron.worker.php
|
|
13
13
|
- worker1.5/ebs/setup_git_libraries.php
|
|
@@ -22,7 +22,7 @@ related:
|
|
|
22
22
|
- ../../../clients/compass-usa/features/mits-sales-order-transmission-alerting.md
|
|
23
23
|
---
|
|
24
24
|
|
|
25
|
-
Steps to tell "worker1.5 crons are not running" from "a downstream system is failing", find the cause on the box, and recover
|
|
25
|
+
Steps to tell "worker1.5 crons are not running" from "a downstream system is failing", find the cause on the box, and recover, plus how to triage failing crons in `worker-cron.log`; open when several 1.0 jobs or backlogs stall at once, or a cron errors.
|
|
26
26
|
|
|
27
27
|
## Summary
|
|
28
28
|
|
|
@@ -45,14 +45,28 @@ Since TRUE-83078 (2026-10-22) every 1.0 cron runs on ONE server: EB env `worker1
|
|
|
45
45
|
- Backlog crons (MITS transmit, SR→SO, email send, NetSuite syncs) drain by themselves. Watch the backlog fall; do not assume.
|
|
46
46
|
- Fixed-time daily/weekly jobs do NOT catch up. Re-run each missed one by hand (`php /var/www/html/crons/<path>` on the box) and tell the people who expect it.
|
|
47
47
|
|
|
48
|
+
## Triage failing crons in `worker-cron.log`
|
|
49
|
+
|
|
50
|
+
1. Group the `PHP Warning` and `ERROR [..]` lines by message. `App_Error_Capture` adds the cron name to each.
|
|
51
|
+
2. Skip known noise: `ImapClient.php:2580`, `instance-data` lookups, `PHP Deprecated` (job continues).
|
|
52
|
+
3. For the caller, open prod `Logs.Issue` / `Event` by reference (e.g. `8F`); the Event trace gives script:line.
|
|
53
|
+
|
|
54
|
+
Known entries (TRUE-83163, 2026-10-23):
|
|
55
|
+
- `Allowed memory size of 268435456` = the HTTP 500 body from `api.beta.togahub.com` (php-fpm 256 MB on `GET /contacts/{uuid}/contact-addresses` depth=3, Issue `8F`), not a worker1.5 crash. That cron, "Generates sales and purchase orders for Prudential (Beta)", is set `active: 0`.
|
|
56
|
+
- `togadesk desk/includes/functions.php:1017` `$ticket['clientid']` offset on false (~1,500 per log): Desk crons do not use App_Error, so noise, not a crash.
|
|
57
|
+
- `get_new_detectprotectservices.php`: the remote feed returns HTML, not CSV, since ~2025-09-11. Remote issue; the cron now skips those rows.
|
|
58
|
+
- `addDeviceTickets.php` (ManageEngine): fails loud with curl error / HTTP code / body snippet, 5 s timeout (`MANAGEENGINE_TIMEOUT_SECONDS`). ManageEngine host reachability from worker1.5 is still open.
|
|
59
|
+
- NYC DOE `send_request_item_updates.php` is broken in git (undefined vars); see [ServiceNow integration § Gotchas](../../../../clients/nycdoe/features/servicenow-integration.md).
|
|
60
|
+
|
|
48
61
|
## Gotchas
|
|
49
62
|
|
|
50
63
|
- **A script that calls `isProcessRunning()` itself fails half-way.** `crons/notifications/infrastructure/send_emails.php` passes `false` to `cronInitialization()` (so it checks in every run) but calls `isProcessRunning()` again at line 14 and exits. Under the bash shell it checked in every 3 minutes and sent nothing. A fresh `CronJobExecutions` row is not proof the job did its work.
|
|
51
|
-
- **Schedules come from `worker1.5/schedules/cron.worker.json` only.** The old `worker` repo
|
|
64
|
+
- **Schedules come from `worker1.5/schedules/cron.worker.json` only.** The old `worker` repo is retired (all its crons moved here 2026-10-22).
|
|
52
65
|
|
|
53
66
|
## Worked example (2026-10-22 16:56 → 2026-10-23 ~10:30 CT, TRUE-83121)
|
|
54
67
|
|
|
55
68
|
TRUE-83078's installer wrote `SHELL=/bin/bash`. 222 of 234 active crons never checked in. Seen: 16 Compass SOs not sent to MITS (oldest 836 min, MITS returned 200 on every call it got, last call 16:10), all Compass 1.0 workflow crons, NetSuite toga2 syncs, Prudential SR→SO (12 REQs), 141 unsent `Common.EmailsQueued` ticket emails (RUMC, TOW, PC Matic, Apiary, Craftex), and NYC DOE "Send DOE Courier Installation Schedule (7:00 AM)" and "Daily Open and Closed DOE Report (1:00 AM)" missed on 2026-10-23. Fixed by dropping the `SHELL` line (worker1.5 PR #8, `ce0f0b2`). Backlogs drained by ~10:52 CT; missed DOE reports needed a manual re-run.
|
|
56
69
|
|
|
57
70
|
## Change history
|
|
71
|
+
- 2026-10-23 — Added log triage steps and known log entries after the PHP 8.5 stabilization (TRUE-83163). (jcardinal)
|
|
58
72
|
- 2026-10-23 — Created from the worker1.5 cron outage after the single-server move (TRUE-83121, TRUE-83116, TRUE-83138). (bala)
|
|
@@ -527,7 +527,7 @@ Rules:
|
|
|
527
527
|
|
|
528
528
|
### Asynchronous / background processing
|
|
529
529
|
|
|
530
|
-
- For long-running or background work, hand off to the legacy worker tier (the `worker`
|
|
530
|
+
- For long-running or background work, hand off to the legacy worker tier (`worker1.5`; the old `worker` repo is retired) rather than running long tasks inline in a web request. Use AJAX for front-end interactions.
|
|
531
531
|
|
|
532
532
|
## API design
|
|
533
533
|
|
package/knowledge/INDEX.md
CHANGED
|
@@ -5,9 +5,9 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
5
5
|
## 1.0 framework
|
|
6
6
|
|
|
7
7
|
- **library** (Library) _(framework core)_ — 41 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
|
|
8
|
-
- **worker** (Worker) —
|
|
8
|
+
- **worker** (Worker) — 42 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
|
|
9
9
|
- **dbchanges** (Database Changes) _(framework core)_ — 1 doc(s) → [1.0/apps/dbchanges/INDEX.md](1.0/apps/dbchanges/INDEX.md)
|
|
10
|
-
- **worker1.5** (Worker 1.5) —
|
|
10
|
+
- **worker1.5** (Worker 1.5) — 7 doc(s) → [1.0/apps/worker1.5/INDEX.md](1.0/apps/worker1.5/INDEX.md)
|
|
11
11
|
- **togadesk** (TOGa Desk) — 18 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
|
|
12
12
|
- **togaview** (TOGa View) — 9 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
|
|
13
13
|
- **webhook** (Webhook) — 1 doc(s) → [1.0/apps/webhook/INDEX.md](1.0/apps/webhook/INDEX.md)
|
|
@@ -15,11 +15,12 @@ apps:
|
|
|
15
15
|
- dbchanges2
|
|
16
16
|
- library
|
|
17
17
|
- tools
|
|
18
|
+
- worker1.5
|
|
18
19
|
project: _Underscore
|
|
19
20
|
client: compass-canada
|
|
20
21
|
type: profile
|
|
21
22
|
status: active
|
|
22
|
-
updated: 2026-10-
|
|
23
|
+
updated: 2026-10-23
|
|
23
24
|
owners: [jcardinal, bala, tcox, apeterson, ajean, mhammontree, rgirish]
|
|
24
25
|
files: []
|
|
25
26
|
related:
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: nycdoe
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-23
|
|
10
10
|
owners: [mhammontree, sking, bala, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/sync/nycdoe/import_asn.php
|
|
@@ -179,7 +179,7 @@ All **live** DOE business logic is 1.0. The only other 2.0 code is the in-progre
|
|
|
179
179
|
## Gotchas
|
|
180
180
|
- **OPEN (seen 2026-10-22, not investigated) — `Undefined offset` at `library/app/asnprocessor/apple.php:74`** from `legacy_import_asn.php`; prod `Logs.Issue` #1115, new that day.
|
|
181
181
|
- **⚠ OPEN BUG (found 2026-10-19, not fixed) — 1.0 Lexmark/CDW dates save as `1969-12-31`.** CDW sends `YYYY/MM/DD`, but `library/app/asnprocessor/lexmark.php` reads dates with `App_Date::convertMDYYYYtoSQLDate`. All 5,244 of 5,244 `App_ASNProcessor_Lexmark` queue rows since 2026-09-01 have `dateShipped` (and other dates) = 1969-12-31. 2.0 Parse reads `YYYY/MM/DD` correctly. Never trust a 1.0 Lexmark/Acer ship date.
|
|
182
|
-
- **⚠ OUTAGE (fixed TRUE-82735, not deployed 2026-10-14) — the RITM and POD crons never finished for months.** Signs: `Common.CronJobExecutions.dtCheckOut` NULL on every run, zero ServiceNow calls. RITM cron: Feb–Mar 2026 refactors dropped the per-MSO inner loop → `Logs.Issue` 1R *"Undefined variable: row2"* every run since at least 2026-05-07 (last sane version `ce98cd2b5`). POD cron: the file query had **no `ORDER BY`**, so every run hit the same poison file (RITM1387575, already closed → empty-body 200 on `-7` → `Logs.Issue` 1S *"Empty response body from API (HTTP 200)"*), died, never set `dtTransmitted`, and re-uploaded that POD every 5 min; 6,718 files stuck, last sent 2026-09-04. Lesson: an unordered work queue + one poison row + a run-killing throw = a silent total outage; order the queue, isolate per row, and capture.
|
|
182
|
+
- **⚠ OUTAGE (fixed TRUE-82735, not deployed 2026-10-14) — the RITM and POD crons never finished for months.** Signs: `Common.CronJobExecutions.dtCheckOut` NULL on every run, zero ServiceNow calls. RITM cron: Feb–Mar 2026 refactors dropped the per-MSO inner loop → `Logs.Issue` 1R *"Undefined variable: row2"* every run since at least 2026-05-07 (last sane version `ce98cd2b5`). POD cron: the file query had **no `ORDER BY`**, so every run hit the same poison file (RITM1387575, already closed → empty-body 200 on `-7` → `Logs.Issue` 1S *"Empty response body from API (HTTP 200)"*), died, never set `dtTransmitted`, and re-uploaded that POD every 5 min; 6,718 files stuck, last sent 2026-09-04. Lesson: an unordered work queue + one poison row + a run-killing throw = a silent total outage; order the queue, isolate per row, and capture. **Still broken on worker1.5 (2026-10-23, TRUE-83163):** its `crons/sync/nycdoe/send_request_item_updates.php` (copied in `58dab19`) has undefined `$row2` / `$locationSysId` / `$sendLocationUpdate` / `$ritmNumber` — a git bug, not PHP 8; left unfixed by choice.
|
|
183
183
|
|
|
184
184
|
- **⚠ OPEN (parked, handed to Skyler 2026-10-16) — Stage 5 repair-order loop on RITM1320328.** MSO 317327 / PO `WR260255743` / ASN 24959 item 51224 (qty 282, 283 units, `installTicketStatus` `pending`, 18 units with no `togadeskRepairOrderId`). Since June `3_create_installation_ticket.php` creates a new INSTALLATION repair order every few hours: 637 ROs (1 COMPLETE, 23 ORDER_ASSIGNED_AWAITING_SCHEDULING, 613 ORDER_CREATED_AWAITING_REVIEW), all on serial `NXJJEAA0015281EAF72P00`. Each pass also PATCHes the RITM to `-12`, which ServiceNow refuses (empty-body 200). Stop lever: cancel `AdvanceShippingNoticeItems.installTicketStatus` with a reason, per `dbchanges/Core/SK/2026-07-30-dedupe-asn-install-units.sql` — a business call. Root cause in code not confirmed.
|
|
185
185
|
- **OPEN (parked 2026-10-16) — POD cron PATCHes a BLANK ticket number.** `send_nycdoe_proof_of_delivery.php` with an empty `ritmRequestId` calls `/api/nycd3/data/tasks/` → HTTP 400, ~8 per 8h, in pairs every ~2h.
|
|
@@ -295,6 +295,7 @@ All **live** DOE business logic is 1.0. The only other 2.0 code is the in-progre
|
|
|
295
295
|
- Local deep-dive (full WR260236464 case study with record IDs): `doe_service_now_integration.md` in Mark's WWW workspace root; also the `service-now-integration` skill (`asn-import-flow.md`)
|
|
296
296
|
|
|
297
297
|
## Change history
|
|
298
|
+
- 2026-10-23 — RITM cron still broken in the worker1.5 copy (undefined vars); not fixed on TRUE-83163 (jcardinal)
|
|
298
299
|
- 2026-10-22 — Open: new `apple.php:74` Undefined offset (`Logs.Issue` #1115), seen during TRUE-82983 (jcardinal)
|
|
299
300
|
- 2026-10-22 — TRUE-83064: repaired customer PO `WR270089579` (two ASNs, Acer + Lexmark vendor) via dbchanges#270; open-bug gotcha — Stage 5 takes the manufacturer from the ASN vendor and keys the MSO on the ASN; cross-received serials dead-end silently; 152 mislabeled assets on Lexmark model `23782` remain. No code change. (sking)
|
|
300
301
|
- 2026-10-19 — Gated delete live since ~10-11 and its re-release login storm (worker2 SFTP IP ban); file names matter to 1.0 only for FileLog/dedupeKey (rename = re-email); open Lexmark 1969-12-31 date bug. (bala)
|
|
@@ -5,9 +5,9 @@ project: TOGa
|
|
|
5
5
|
client: pcmaticb2b
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
8
|
+
updated: 2026-10-23
|
|
9
9
|
owners: [snaredla, jcardinal]
|
|
10
|
-
apps: [_underscore, api2, worker, worker2, togadesk, library, dbchanges2]
|
|
10
|
+
apps: [_underscore, api2, worker, worker2, togadesk, library, dbchanges2, worker1.5]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/toga2/startech/sync_togadesk_startech_pcmaticb2b.php
|
|
13
13
|
- togadesk/desk/includes/functions.php
|
package/knowledge/clients/prudential/features/device-information-import-and-contact-linking.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Prudential: Device information import + unit→contact linking (import_device_information.php)"
|
|
3
3
|
framework: "1.0"
|
|
4
|
-
repo:
|
|
5
|
-
project: Worker
|
|
4
|
+
repo: worker1.5
|
|
5
|
+
project: Worker 1.5
|
|
6
6
|
client: prudential
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["bala"]
|
|
9
|
+
updated: 2026-10-23
|
|
10
|
+
owners: ["bala", "jcardinal"]
|
|
11
11
|
files:
|
|
12
|
-
-
|
|
12
|
+
- worker1.5/crons/toga2/prudential/import_device_information.php
|
|
13
13
|
- worker/crons/toga2/prudential/backfill_unit_contacts.php
|
|
14
14
|
- dbchanges2/Client_Prudential/2026-07-07 - Contact Dedup Merge.sql
|
|
15
15
|
related:
|
|
@@ -19,7 +19,7 @@ related:
|
|
|
19
19
|
How the device-sync cron links a Unit to its employee Contact, the "null null" duplicate-contact incident, and the dedup+backfill remediation; open before touching device import or contact linking.
|
|
20
20
|
|
|
21
21
|
## Summary
|
|
22
|
-
Prudential's **device-sync** cron (`
|
|
22
|
+
Prudential's **device-sync** cron (`worker1.5/crons/toga2/prudential/import_device_information.php`; was `worker`, retired) pulls device/asset records (from ServiceNow / the Dell CMDB feed) and PUTs them into the 2.0 platform (`Client_Prudential` via the TOGa2 API), writing `Units` and their owning `Contact`. Each unit's contact is keyed to an employee by the custom field **`c_employeeXid`**. This records how the cron links a unit to its real employee contact, the production incident from getting that wrong, and the two remediation pieces (a dedup migration and a null-contact backfill cron).
|
|
23
23
|
|
|
24
24
|
## How it works
|
|
25
25
|
- The cron loads **all** existing Prudential contacts once into an in-memory **`c_employeeXid` → keeper uuid** map (`loadPrudentialContactMap`).
|
|
@@ -51,6 +51,7 @@ Uses inline keeper subqueries — **no work table, no backup table** (the DBA ta
|
|
|
51
51
|
Resumable, unit-driven. For each `Unit` with `contactId` NULL it queries ServiceNow by serial (`/gbts/v1/sn_pcaas/asset?serial_number=`) for the owner XID, resolves the contact uuid (reusing the map logic), and PUTs `/units {serialNumber, contact:{uuid}}`. Resumable via a `Parameters` pointer key **`SERVICE_NOW_UNIT_CONTACT_BACKFILL`** (upserts; generates a uuid4 on insert), time-bounded per run.
|
|
52
52
|
|
|
53
53
|
## Gotchas
|
|
54
|
+
- **The unit lookup returns `units` as a list** (since the explicit serialNumber where/and filter, `worker` `e7926dd2`). Read `units[0]`; an empty list means no unit → create branch.
|
|
54
55
|
- **⚠ Never link a related record by a non-identifier custom field.** `c_employeeXid` is not a match key; only `uuid` (or the numeric internal id) links an existing child. Passing the custom field alone silently creates a new empty record on every write.
|
|
55
56
|
- **Ordering is load-bearing:** deploy the cron fix → run the dedup migration → run the backfill. Running dedup before the cron fix regenerates duplicates.
|
|
56
57
|
- **⚠ Do NOT force NULL contactId to zero.** Null-contact units are frequently **legitimate**: Dell has not yet assigned the laptop to an employee, so the device is unassigned inventory (ServiceNow returns no `ownedBy`). Verified read-only on the prod copy: of **22,960** null-contact units, only **26** shipped on a Toga ASN (all 26 trace to a named order contact); the other ~22,934 never went through a Toga order and cluster in inventory dispositions/stages (only 15 "In Use"). **Caveat:** the device-import cron also pulls CMDB assets never ordered through Toga, so "not on an ASN" does not by itself prove unassigned — definitive ownership is **ServiceNow-by-serial only**. A residual of no-owner units after backfill is **expected and correct**.
|
|
@@ -60,6 +61,7 @@ Resumable, unit-driven. For each `Unit` with `contactId` NULL it queries Service
|
|
|
60
61
|
- **⚠ `Client_Prudential.Users.contactId` is now NOT NULL — and do NOT blind email-create contacts for Prudential.** The `Client_Prudential/2026-09-21a` backfill linked ~14,298 null-contactId users to their `c_employeeXid` **keeper** contact (ROW_NUMBER: primary-email, then name, then oldest id), created a Contact+email for only ~20 residual, then set `contactId` NOT NULL. Prudential's Contacts were just deduped 893k→19.7k, so a blanket email-create backfill would **re-pollute** them — link by XID keeper, never create-per-user. Part of the platform-wide contactId initiative: [client-user-contactid-required](../../../2.0/apps/_underscore/features/client-user-contactid-required.md).
|
|
61
62
|
|
|
62
63
|
## Change history
|
|
64
|
+
- 2026-10-23 — Unit lookup returns `units` as a list since the explicit serialNumber where/and filter (`worker` `e7926dd2`); cron reads `units[0]`, empty list → create. Moved to worker1.5 (TRUE-83163) (jcardinal)
|
|
63
65
|
- 2026-09-21 — `Client_Prudential.Users.contactId` made NOT NULL after the `2026-09-21a` backfill linked null-contactId users to their XID keeper contact. Noted the "never blind email-create for Prudential" rule (would re-pollute the just-deduped Contacts) and cross-linked the platform contactId initiative. (jcardinal)
|
|
64
66
|
|
|
65
67
|
## Related
|
|
@@ -7,11 +7,12 @@ apps:
|
|
|
7
7
|
- worker2
|
|
8
8
|
- dbchanges
|
|
9
9
|
- dbchanges2
|
|
10
|
+
- worker1.5
|
|
10
11
|
project: Worker
|
|
11
12
|
client: staples
|
|
12
13
|
type: profile
|
|
13
14
|
status: active
|
|
14
|
-
updated: 2026-10-
|
|
15
|
+
updated: 2026-10-23
|
|
15
16
|
owners: [jcardinal, bala, mhammontree]
|
|
16
17
|
files:
|
|
17
18
|
- worker/crons/sync/staples/sync_staples_cxml.php
|
|
@@ -8,11 +8,12 @@ apps:
|
|
|
8
8
|
- library
|
|
9
9
|
- walmarttechservices
|
|
10
10
|
- walmart-activate
|
|
11
|
+
- worker1.5
|
|
11
12
|
client: walmart
|
|
12
13
|
type: client-feature
|
|
13
14
|
status: active
|
|
14
|
-
updated: 2026-10-
|
|
15
|
-
owners: ["mhammontree", "rgirish"]
|
|
15
|
+
updated: 2026-10-23
|
|
16
|
+
owners: ["mhammontree", "rgirish", "jcardinal"]
|
|
16
17
|
files:
|
|
17
18
|
- library/app/client/walmart.php
|
|
18
19
|
- library/app/email/walmartb2c.php
|
package/package.json
CHANGED