toga-ai 1.0.1035 → 1.0.1037
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/app-cloud-aws-credentials.md +6 -1
- package/knowledge/1.0/apps/library/features/error-capture-1-0.md +4 -0
- package/knowledge/1.0/apps/worker/features/compass-partial-in-transit-delivered-emails.md +25 -3
- package/knowledge/2.0/apps/_underscore/features/email-template-sending.md +8 -1
- package/knowledge/clients/canon/profile.md +4 -1
- package/knowledge/clients/spglobal/profile.md +5 -2
- package/knowledge/standalone/standards/communication-clarity.md +2 -1
- package/package.json +1 -1
|
@@ -19,6 +19,8 @@ files:
|
|
|
19
19
|
- togadesk/ebs/register_instance_to_shared_application_load_balancer.php
|
|
20
20
|
- togadesk/.ebextensions/020_setup_git_libraries.config
|
|
21
21
|
- toga/composer.json
|
|
22
|
+
- vision/composer.json
|
|
23
|
+
- library/app/database.php
|
|
22
24
|
related:
|
|
23
25
|
- ../../../../2.0/apps/worker2/features/cross-account-aws-access.md
|
|
24
26
|
- ../../../../2.0/apps/_underscore/features/cloud-s3-helpers.md
|
|
@@ -70,12 +72,15 @@ CodePipeline `Worker` (502, us-west-2) sources `_production` of **both** `agilan
|
|
|
70
72
|
- **`getS3Objects()` returns `null` for both a failed CLI call and an empty prefix.** A caller that loops `['Contents']` without a null check raises a warning, which 1.0 `App_Error` turns into `exit`. When the prefix should be non-empty, throw on `null` (the ODP AS2 move cron does).
|
|
71
73
|
- **`copyFileToS3()` (and the other CLI-based calls) never report failure.** `shell_exec('aws s3 cp ...')` then `return true` — bad credentials = silent lost upload. Uploads made while the old key was dead (repair photos, KB files, `BLOBSTORAGE` saves via `App_Model::save()`, email-intake attachments) may be missing from S3. Open, not fixed.
|
|
72
74
|
- `togadesk` `ebs/check_s3fs.php` + `ebs/register_instance_to_shared_application_load_balancer.php` now assume `WorkloadsRuntime` (TRUE-82718, PR #622, merged 2026-10-13); togaview fix is PR #307 (opened). The s3fs **mount** cannot assume a role — it uses the instance role directly: see [asifiles s3fs mount](../../togadesk/features/s3fs-asifiles-file-storage.md).
|
|
73
|
-
- **
|
|
75
|
+
- **Every 1.0 app that bootstraps `library` needs `aws/aws-sdk-php` in its own `composer.json`** (since TRUE-82705). Without it any App_Cloud call fatals `Class 'Aws\Credentials\CredentialProvider' not found`. Hidden trigger: on prod/worker/bigworker, `App_Database::getDatabaseReaderHost()` (`library/app/database.php` ~156) calls `App_Cloud::getParameter('database_reader_endpoint')` whenever its cache file `databaseReadHost.txt` is older than 30-60 s. The fatal hits before the cache is rewritten, so the cache stays stale and **every** reader-DB request fails — the app looks fully down (TRUE-82793: Vision PunchOut for Canon / S&P Global via SAP Ariba returned HTTP 500). Fix = add `"aws/aws-sdk-php": "^3.328"` and pin `composer.lock` to worker's prod versions (aws-sdk-php 3.328.1, guzzle 7.9.2, promises 2.0.4, psr7 2.7.0, jmespath 2.8.0 — PHP 7.2 safe); EB runs `composer install --no-dev` on deploy (`vendor/` is gitignored).
|
|
76
|
+
- Has it (`_production`, checked 2026-10-15): worker, togadesk (`^3.337`), togaview, tools, toga (TRUE-82714, 63c5ed0), vision (TRUE-82793, 2edd5877, branch — needs merge + deploy).
|
|
77
|
+
- **Missing — same crash risk, not fixed, flagged to Jeff:** store, walmarttechservices, webhook.
|
|
74
78
|
- Verify assume-role in CloudTrail (`AssumeRole` on `WorkloadsRuntime`, by session name: `library-app-cloud`, `setup-git-libraries`, `register-instance`, `worker-deploy`).
|
|
75
79
|
- Open, not fixed (developer owns): remove `AWS_ACCESS_KEY_ID`/`AWS_SECRET_KEY` from `agilant-worker` EB env properties.
|
|
76
80
|
- Every long-lived key still in code across repos (SES SMTP, s3fs, ALB scripts, other accounts): [audit 2026-10-13](../../../../2.0/apps/worker2/features/cross-account-aws-access.md#long-lived-keys-still-in-code-audit-2026-10-13).
|
|
77
81
|
|
|
78
82
|
## Change history
|
|
83
|
+
- 2026-10-15 — aws-sdk-php requirement: reader-host crash path, per-app audit, vision fixed (TRUE-82793) (mhammontree)
|
|
79
84
|
- 2026-10-13 — togadesk ebs scripts converted (TRUE-82718); aws-sdk-php composer requirement (TRUE-82714) (mhammontree)
|
|
80
85
|
- 2026-10-12 — Created: removed the static IAM key from all 1.0 code/config; App_Cloud, deploy scripts and `setup_git_libraries.php` assume `WorkloadsRuntime` (TRUE-82705) (jcardinal)
|
|
81
86
|
- 2026-10-13 — S3 403 triage, silent `copyFileToS3` failure, unconverted togadesk/togaview ebs scripts (TRUE-82712) (mhammontree)
|
|
@@ -17,6 +17,7 @@ files:
|
|
|
17
17
|
- library/app/api/toga2.php
|
|
18
18
|
- library/app/cloud.php
|
|
19
19
|
- worker/config.worker.ini
|
|
20
|
+
- vision/config.prod.ini
|
|
20
21
|
- worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php
|
|
21
22
|
related:
|
|
22
23
|
- ../architecture.md
|
|
@@ -117,6 +118,8 @@ Two things are specifically 1.0:
|
|
|
117
118
|
## Gotchas
|
|
118
119
|
|
|
119
120
|
- **Looking for a 1.0 cron fatal? Query `Logs.Issue` on environment `prod`, ordered by `dtLastOccurred`.** The legacy `Logs.Errors` and `Vision_Log.ErrorLog` tables do **not** carry 1.0 cron fatals — searching them wastes time (2026-09-18, DOE outbound-cron outage). Uncaught exceptions reach here via `App_Error::handleException` → `captureException()`, which returns the short quotable reference. **A `catch` BYPASSES capture** — if you catch per record, call `App_Error_Capture::captureException($e)` yourself, and put only the returned reference (never `$e->getMessage()`, which can carry a raw vendor response body) into an email or log.
|
|
121
|
+
- **Vision does not capture — use Sentry.** `vision/config.prod.ini` has no `[database_toga2logs]` section, so vision errors never reach `Logs.Issue`; only Sentry project `vision` has them. Do not search `Logs.Issue` for vision errors.
|
|
122
|
+
- **Open, not fixed (TRUE-82793): a PHP `\Error` (e.g. class not found) breaks the Sentry path and prints the stack trace on the prod page.** `library/app/error.php` ~646 builds `ReflectionProperty('\ErrorException' or '\Exception', 'message')`; `setValue` on an `\Error` throws `Cannot access protected property Error::$message`, which hides the real error, and the catch (~653) calls `debugVar($sentryException, false)`, which prints the full trace to the client (info leak). Fix idea: use `'\Error'` when `$exception instanceof \Error`.
|
|
120
123
|
- **A `*/` inside PHP docblock prose silently closes the comment early.** Writing something like `list*/fetch*` in a `/** … */` block ends the docblock at the `*/`, and `php -l` then reports a confusing `syntax error, unexpected token` on a later line. Hit while documenting the NetSuite throw sites. Avoid the literal `*/` in docblock text.
|
|
121
124
|
- **⚠ A grouped `Logs.Issue` row can show a STALE error id — read the live api2 transaction log for the current error.** Two facts combine to mislead: (1) `Event.errorMessage` is **`varchar(255)`**, so a long API response is **truncated** — often before the trailing error id; and (2) `Logs.Issue.subject` / `Issue.errorMessage` hold the **first-occurrence** text, so a long-lived issue keeps showing the error id from whenever it was first seen even after the per-call error has changed. Observed 2026-08-17: issue `3H` still displayed `EO-1` / `3G-6` (the original undefined-method fatal) while the current per-call failure was actually `EV-11` (an FK 1451 on a DELETE). **To see what is failing *now*, read the live api2 transaction log (`Logs_<client>.Api.responsePayload`) for the specific call — not the grouped `Issue` row.**
|
|
122
125
|
- **Any PHP warning inside capture code would kill the request.** 1.0's `App_Error::handleError()` routes **every** PHP warning into `handleException()`, which renders the red box and calls `exit()` — so a single warning raised while capturing (an unreachable IMDS endpoint is the everyday case: `file_get_contents` warns before returning `false`) terminates the request or cron mid-run while reporting an unrelated error. 2.0's equivalent handler *throws*, which a `catch` absorbs. `captureException()` therefore sets `App_Error::setThrowExceptionsEnabled(false)` for its own duration and restores it in `finally`. This disables the **handler**, not real throws — `App_Query` raises a plain `throw new Exception` on DB errors, so genuine failures still reach the catch. **This is a general 1.0 trap for any code that must not escalate**, not just error capture.
|
|
@@ -127,6 +130,7 @@ Two things are specifically 1.0:
|
|
|
127
130
|
- **⚠ The legacy pre-Sentry handler dumps the entire config on any error it renders.** `library/app/DELETE_DOA_PRE-SENTRY_error.php` is a dead-named but still-reachable handler whose `htmlBacktrace()` does `print_r($entry['args'])` for **every** frame. On 2026-08-05 a real error page on **https://towfoundation.togaview.com** rendered ~26 pages containing the whole config array — production DB host/user/password, SES SMTP credentials, NetSuite credentials, Payeezy API key/secret/merchant token, SSL Store token — plus ticket PII. The current `library/app/error.php` is safe (it dumps only under `App_Registry::inDevMode()`), but **both** handlers gate on `inDevMode()`, so the affected host was **evaluating as dev mode in production**. Two actions: (1) verify `inDevMode()` per host in production before trusting any error page not to leak, and (2) delete the legacy handler. Rotation of every exposed credential **was escalated to Jeff on 2026-08-05; confirmation that it was completed is still owed** — treat those credentials as compromised until confirmed. (No values are recorded here or anywhere in the KB.)
|
|
128
131
|
|
|
129
132
|
## Change history
|
|
133
|
+
- 2026-10-15 — Vision has no Logs capture (Sentry only); open `\Error` reflection leak in `error.php` (TRUE-82793) (mhammontree)
|
|
130
134
|
- 2026-10-13 — Open issue: handler crash `App_Error_Capture not found` on tools prod hides the real error (TRUE-82662) (bala)
|
|
131
135
|
- 2026-10-12 — Removed the committed-AWS-key gotcha: key deleted, `App_Cloud` assumes `WorkloadsRuntime` (TRUE-82705) (jcardinal)
|
|
132
136
|
|
|
@@ -6,8 +6,8 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
10
|
-
owners: ["bala"]
|
|
9
|
+
updated: 2026-10-15
|
|
10
|
+
owners: ["bala", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/toga2/compass/update_salesorder_status_from_odp.php
|
|
13
13
|
- worker/crons/toga2/compass/send_delivered_email.php
|
|
@@ -177,6 +177,14 @@ IFNULL(Users.email, ContactEmailAddresses.emailAddress) AS contactEmail
|
|
|
177
177
|
The same `IFNULL` resolves the display name, and the `WHERE` requires the resolved address to be
|
|
178
178
|
non-empty.
|
|
179
179
|
|
|
180
|
+
**Delivered crons (USA + Canada `send_delivered_email.php`, 2026-10-15, TRUE-82847)** use the same
|
|
181
|
+
precedence, but pick **one** `Users` row: `LEFT JOIN Users ON Users.id = (SELECT MAX(id) FROM Users
|
|
182
|
+
WHERE contactId = … AND isActive = 1 AND email <> '')` (newest active user with email), fallback
|
|
183
|
+
`Contacts.primaryContactEmailAddressId` → `ContactEmailAddresses`. Rows with no usable address are
|
|
184
|
+
excluded in SQL (no UPS call, no email, status stays non-`DELIVERED`, so it sends later if an
|
|
185
|
+
address appears). `Notifications` rows are written only when `userId > 0`. Never go back to a plain
|
|
186
|
+
`INNER JOIN Users ON contactId` — it yields one email per `Users` row (see gotcha).
|
|
187
|
+
|
|
180
188
|
**The `ContactEmailAddresses.contactId = Contacts.id` predicate is not optional.** About 14% of
|
|
181
189
|
Compass contacts point at an email row owned by a *different* contact, so without it the cron mails
|
|
182
190
|
the wrong person — see
|
|
@@ -239,7 +247,8 @@ the tracking number as emailed (it retries next run).
|
|
|
239
247
|
`ORDER BY MAX(TrackingNumbers.dtCreated) DESC`, `isTrackingStatusFinal()`) but **still resolves
|
|
240
248
|
the recipient with `INNER JOIN Users … Users.email AS contactEmail`** — no `Contacts` /
|
|
241
249
|
`ContactEmailAddresses` fallback and no ownership guard. Canada therefore still cannot email a
|
|
242
|
-
contact that has no `Users` row.
|
|
250
|
+
contact that has no `Users` row. (Canada's **delivered** cron has the fallback as of 2026-10-15;
|
|
251
|
+
the in-transit cron does not.) Port the recipient block if Canada ever gets EDI-created MR
|
|
243
252
|
orders.
|
|
244
253
|
- **Canada keeps its own retry behaviour on a carrier throw.** It leaves the row **unstamped** when
|
|
245
254
|
the carrier lookup throws, so the next run retries it; only rows confirmed *final* are stamped.
|
|
@@ -355,7 +364,20 @@ the tracking number as emailed (it retries next run).
|
|
|
355
364
|
tracking-numbers today; the plan (not built) is to move this email logic into a Compass `postPut`
|
|
356
365
|
interceptor and retire this 1.0 cron.
|
|
357
366
|
|
|
367
|
+
- **A contact can have 2 active `Users` rows** (old one with NULL email + new one with a new HR
|
|
368
|
+
username, e.g. `thompj53` → `thompj55`). Prod 2026-10-15: 2 such contacts (326758, 283892). Any
|
|
369
|
+
join `Users ON contactId` fans out per user. Before the 2026-10-15 fix the delivered cron sent
|
|
370
|
+
twice: SA138904 → `Logs_Compass.Email` 146380 FAILED (`to=''`, "You must provide at least one
|
|
371
|
+
recipient email address.") + 146381 SENT. Likely the [PEOPLE-file duplicate-account
|
|
372
|
+
behavior](../../../clients/compass-usa/features/people-file-user-lifecycle.md); the new-username
|
|
373
|
+
case is not proven in importer code.
|
|
374
|
+
- **One tracking number on several sales orders sends one delivered email per SO.** Known, left
|
|
375
|
+
as is (decided 2026-10-15, TRUE-82847).
|
|
376
|
+
- Delivered template = prod `emailTemplateId` 26 in `Logs_Compass.Email`; baseline 2026-09-15..10-15:
|
|
377
|
+
959 SENT, 1 FAILED.
|
|
378
|
+
|
|
358
379
|
## Change history
|
|
380
|
+
- 2026-10-15 — Delivered crons (USA + Canada) pick one active `Users` row with email + Contacts fallback; documented the 2-active-users duplicate. (jcardinal)
|
|
359
381
|
- 2026-10-13 — Recorded that the USA delivered cron has no date window, so bulk ASN loads must suppress first. (bala)
|
|
360
382
|
|
|
361
383
|
## Related
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-15
|
|
10
10
|
owners: ["jcardinal", "bala", "mhammontree", "apeterson"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Client/EmailTemplate.php
|
|
@@ -178,6 +178,12 @@ can keep using `sendEmail($api, ...)`.
|
|
|
178
178
|
`EmailTemplateOutgoingEmailAddress` rows, if `$to` is **still empty** it throws `_Exception_Validation`
|
|
179
179
|
(→ **HTTP 400**) rather than a TypeError-500 or sending a recipientless email. A missing recipient is
|
|
180
180
|
client input error, not a server fault.
|
|
181
|
+
- **⚠ Gap (not fixed): `$to = ''` with a cc/bcc slips past the empty check.** `(array)''` → `['']`, so
|
|
182
|
+
`dispatch()`'s `empty($to)` passes; `_Email::addTo` (`_underscore/Email.php` ~L42-49) drops the
|
|
183
|
+
falsy value; `_Email` send (~L361) throws only when to+cc+bcc are **all** empty. Result: email queued
|
|
184
|
+
with `to=''`, fails later in worker2 `Worker/Infrastructure/Email/Send.php` (PHPMailer preSend,
|
|
185
|
+
"You must provide at least one recipient email address."). Callers must not pass an empty `to`.
|
|
186
|
+
Seen 2026-10-15 (TRUE-82847, Compass delivered email); core fix deferred.
|
|
181
187
|
- **`sendEmail()`'s signature is load-bearing for scripted APIs** — the Record Script engine
|
|
182
188
|
(`api2/Component/Api/V2/V2.php`, ~line 3594) calls the method with `api` as a named
|
|
183
189
|
argument, so the first param must stay `&$api`. Do not "clean it up" by removing it.
|
|
@@ -231,6 +237,7 @@ worker method) in-process instead.
|
|
|
231
237
|
|
|
232
238
|
## Change history
|
|
233
239
|
|
|
240
|
+
- 2026-10-15 — Recorded the empty-string `to` + cc gap (queued, fails in worker2). (jcardinal)
|
|
234
241
|
- 2026-10-06 — Noted the 1054 symptom when the email-review migrations are missing in an env (client-sandbox). (bala)
|
|
235
242
|
- 2026-09-23 — Added the review hold (`isReviewRequired` + `REVIEW` addresses → `HELD` real email +
|
|
236
243
|
`[REVIEW]` copy); fail-loud on missing REVIEW rows / unknown `toCcBcc`. (jcardinal)
|
|
@@ -4,16 +4,18 @@ framework: "2.0"
|
|
|
4
4
|
apps:
|
|
5
5
|
- library
|
|
6
6
|
- worker
|
|
7
|
+
- vision
|
|
7
8
|
project: Worker
|
|
8
9
|
client: canon
|
|
9
10
|
type: profile
|
|
10
11
|
status: active
|
|
11
|
-
updated: 2026-
|
|
12
|
+
updated: 2026-10-15
|
|
12
13
|
owners: ["jcardinal", "mhammontree"]
|
|
13
14
|
files: []
|
|
14
15
|
related:
|
|
15
16
|
- ../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md
|
|
16
17
|
- ../../1.0/apps/worker/features/canon-received-po-netsuite-order-sync.md
|
|
18
|
+
- ../../1.0/apps/library/features/app-cloud-aws-credentials.md
|
|
17
19
|
---
|
|
18
20
|
|
|
19
21
|
Canon TOGa-Supply NetSuite integration client on the shared 1.0 worker+library per-client sync; open for its sync wrapper and received-PO→NetSuite specifics.
|
|
@@ -29,3 +31,4 @@ Canon TOGa-Supply NetSuite integration client on the shared 1.0 worker+library p
|
|
|
29
31
|
|
|
30
32
|
- **`worker` / `library` (1.0)** — shared togasupply sync engine + `App_Api_Toga2` bridge.
|
|
31
33
|
- **`worker` (1.0) — received-PO → NetSuite sales orders.** `crons/sync/sync_received_po.php` reads Canon POs from the **Vision** databases (`companyId 13785`, catalog companies 13785 / 18610), explodes custom-catalog bundles into components, creates the NetSuite sales order. See [Canon received-PO → NetSuite sales order sync](../../1.0/apps/worker/features/canon-received-po-netsuite-order-sync.md).
|
|
34
|
+
- **`vision` (1.0) — PunchOut from SAP Ariba.** Canon buyers punch out into Vision. A Vision outage (HTTP 500) hits this flow first — e.g. TRUE-82793, missing `aws/aws-sdk-php` after the App_Cloud change: see [1.0 AWS credentials](../../1.0/apps/library/features/app-cloud-aws-credentials.md#gotchas). Vision errors are in Sentry only, not `Logs.Issue`.
|
|
@@ -4,15 +4,17 @@ framework: "2.0"
|
|
|
4
4
|
apps:
|
|
5
5
|
- library
|
|
6
6
|
- worker
|
|
7
|
+
- vision
|
|
7
8
|
project: Worker
|
|
8
9
|
client: spglobal
|
|
9
10
|
type: profile
|
|
10
11
|
status: active
|
|
11
|
-
updated: 2026-
|
|
12
|
-
owners: ["jcardinal"]
|
|
12
|
+
updated: 2026-10-15
|
|
13
|
+
owners: ["jcardinal", "mhammontree"]
|
|
13
14
|
files: []
|
|
14
15
|
related:
|
|
15
16
|
- ../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md
|
|
17
|
+
- ../../1.0/apps/library/features/app-cloud-aws-credentials.md
|
|
16
18
|
---
|
|
17
19
|
|
|
18
20
|
S&P Global TOGa-Supply NetSuite integration client on the shared 1.0 worker+library per-client sync; open for its sync specifics.
|
|
@@ -26,3 +28,4 @@ S&P Global TOGa-Supply NetSuite integration client on the shared 1.0 worker+libr
|
|
|
26
28
|
## Integration touchpoints
|
|
27
29
|
|
|
28
30
|
- **`worker` / `library` (1.0)** — shared togasupply sync engine + `App_Api_Toga2` bridge.
|
|
31
|
+
- **`vision` (1.0) — PunchOut from SAP Ariba** (same flow as Canon). Outage example + fix: TRUE-82793, see [1.0 AWS credentials](../../1.0/apps/library/features/app-cloud-aws-credentials.md#gotchas). Vision errors are in Sentry only, not `Logs.Issue`.
|
|
@@ -6,7 +6,7 @@ project: Claude Harness
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: standard
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-10-15
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files: []
|
|
12
12
|
related:
|
|
@@ -26,6 +26,7 @@ Enforced always-on: the short, hard version lives in `rules/common/communication
|
|
|
26
26
|
5. **Lead with the answer.** Put the point first. Add reasons after, only if needed. Do not build up to the answer with a long intro.
|
|
27
27
|
6. **Use lists and steps.** For anything with more than two parts, use a numbered or bulleted list, not a paragraph.
|
|
28
28
|
7. **Deploy steps follow their own rule.** See [[deploy-instruction-communication]] — lead with the exact SQL file(s) and order, then repos.
|
|
29
|
+
8. **No running commentary.** Work silently — no progress notes or per-agent summaries. Send one organized report at the end (answer first, findings per item, questions in a numbered list, then "What you need to do"). Only a blocker that stops all work gets an early, short message. See `rules/common/communication.md` § *No running commentary*.
|
|
29
30
|
|
|
30
31
|
## Quick check before sending
|
|
31
32
|
|
package/package.json
CHANGED