toga-ai 1.0.493 → 1.0.494
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/toga2-api-client-and-bridge.md +117 -8
- package/knowledge/2.0/apps/api2/INDEX.md +1 -1
- package/knowledge/2.0/apps/api2/features/nested-relationship-writes.md +36 -1
- package/knowledge/2.0/apps/worker2/features/teams-transcript-export.md +17 -2
- package/knowledge/clients/aig/features/entitlement-intake.md +41 -1
- package/package.json +1 -1
|
@@ -6,13 +6,15 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-03
|
|
10
10
|
owners: [jcardinal, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
13
13
|
- worker/crons/toga2/aig/sync_togasupply_aig.php
|
|
14
14
|
- worker/crons/toga2/wje/sync_togasupply_wje.php
|
|
15
15
|
related:
|
|
16
|
+
- ../../../2.0/apps/api2/features/nested-relationship-writes.md
|
|
17
|
+
- ../../../clients/aig/features/entitlement-intake.md
|
|
16
18
|
- netsuite-suiteql-api-reference.md
|
|
17
19
|
- netsuite-suiteql-rest-shim.md
|
|
18
20
|
- ../../worker/features/netsuite-togasupply-per-client-sync.md
|
|
@@ -106,6 +108,80 @@ bool $enabled2to1, bool $enabled1to2)`. When `enabled2to1`:
|
|
|
106
108
|
- The 1→2 direction (`enabled1to2`) is a stub (not yet implemented).
|
|
107
109
|
- DB target is selected with `App_Model_Client::changeDbLinkToClientDatabase(getClientDatabaseNames()[$togaClientId])`.
|
|
108
110
|
|
|
111
|
+
### TRUE-79401 payload shape — RESOLVED 2026-08-03 (nested; NO api2 work needed)
|
|
112
|
+
|
|
113
|
+
**The multi-email payload shape is settled, and the 2026-07-28 library change is the COMPLETE
|
|
114
|
+
implementation — zero api2/`_underscore` work is required.** (This supersedes the previous "open
|
|
115
|
+
question, owner Paulina".) Sources: AIG's post-entitlements API field-mapping spreadsheet, the
|
|
116
|
+
2026-05-01 Teams meeting, and the 2026-05-04 AIG email exchange.
|
|
117
|
+
|
|
118
|
+
AIG's mapping document maps **both** `UserDefined3` (secondary email) **and** `UserDefined4` (third
|
|
119
|
+
email) to the **same** nested collection — `Entitlements.contact.contactEmailAddresses[].emailAddress`.
|
|
120
|
+
In the meeting the team looked up our own record/field names specifically to write them into that
|
|
121
|
+
document ("it is called contact email addresses and the field is called email address").
|
|
122
|
+
|
|
123
|
+
> **`UserDefined3` / `UserDefined4` are AIG's SOURCE-SIDE column labels only.** They are not, and
|
|
124
|
+
> never were, fields to implement on our side. Verified: `UserDefined3` appears **nowhere** in
|
|
125
|
+
> `api2` or `_underscore` — its only occurrences in the tree are a comment in
|
|
126
|
+
> `library/app/api/toga2.php` and this doc.
|
|
127
|
+
|
|
128
|
+
Correct inbound shape:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
"contact": {
|
|
132
|
+
"primaryContactEmailAddress": { "emailAddress": "primary@example.com" },
|
|
133
|
+
"contactEmailAddresses": [
|
|
134
|
+
{ "emailAddress": "primary@example.com" },
|
|
135
|
+
{ "emailAddress": "second@example.com" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The primary **may** be repeated inside the collection — `syncContactToToga1Customer` de-dupes with
|
|
141
|
+
`array_unique`.
|
|
142
|
+
|
|
143
|
+
**Three distinct layers — only the last one is delimited.** Conflating them is the easy mistake:
|
|
144
|
+
|
|
145
|
+
| Layer | Shape |
|
|
146
|
+
|---|---|
|
|
147
|
+
| 1. AIG → us (V2 payload) | nested `contactEmailAddresses[]` **array of objects** |
|
|
148
|
+
| 2. `Client_Aig.ContactEmailAddresses` (2.0) | one **ROW** per address |
|
|
149
|
+
| 3. `TOGA_AIG.Customers.emailAddress` (1.0) | a single `'; '`-joined **STRING** |
|
|
150
|
+
|
|
151
|
+
The 2026-05-01 meeting's comma-vs-semicolon debate was entirely about **layer 3** (a legacy
|
|
152
|
+
`varchar(255)` can only hold a string) and says nothing about the inbound payload.
|
|
153
|
+
|
|
154
|
+
**Structural trap:** `contactEmailAddresses` is a **direct child collection**, so it is a **flat**
|
|
155
|
+
array of objects — *not* the double-wrapped bridge-table form used by its siblings in the very same
|
|
156
|
+
AIG payload (`entitlementEntitlementCoverageTypes: [{ entitlementCoverageType: {…} }]`). And a
|
|
157
|
+
wrong shape does **not** error: V2 returns **201** and drops the data — see
|
|
158
|
+
[api2 nested-relationship writes](../../../2.0/apps/api2/features/nested-relationship-writes.md).
|
|
159
|
+
|
|
160
|
+
**Purpose is verification/lookup ONLY.** Settled 2026-05-01: "it's just for visual, this is for
|
|
161
|
+
verification, it's a string." The use case is a support agent looking up a caller by **any** of
|
|
162
|
+
their addresses after pulling the contract number. Multi-recipient notification (CC/BCC on TogaDesk
|
|
163
|
+
tickets) was **explicitly rejected** — see the gotcha about the single-recipient registration email.
|
|
164
|
+
|
|
165
|
+
### Open items (TRUE-79401)
|
|
166
|
+
|
|
167
|
+
1. Confirm the library change is **merged and deployed to the prod worker** (git/deploy state not
|
|
168
|
+
verified as of 2026-08-03).
|
|
169
|
+
2. **Beta verification of the nested write is outstanding.** Assert real
|
|
170
|
+
`Client_Aig.ContactEmailAddresses` **row counts** (never the HTTP status), covering **both** a
|
|
171
|
+
brand-new contact (CREATE) **and** a second entitlement for an **existing** contact (UPDATE).
|
|
172
|
+
The UPDATE path is the suspect one: the reverse back-reference injection at `V2.php:4985–4996`
|
|
173
|
+
breaks single-key forced-MATCH and explicitly names `primaryContactEmailAddress` as likely
|
|
174
|
+
affected (see the api2 doc, 2026-07-20).
|
|
175
|
+
3. Give AIG's dev team a **concrete JSON example** (above), not just the
|
|
176
|
+
`contactEmailAddresses[].emailAddress` path expression, so they don't emit an array of bare
|
|
177
|
+
strings.
|
|
178
|
+
4. Confirm with **Paulina** that the mapping document actually reached AIG. Risk: our 2026-05-04
|
|
179
|
+
email told AIG to send the email fields "only if they are necessary for the process" — ambiguous
|
|
180
|
+
phrasing, sent in the same breath as dropping Business Unit, which may have read as a de-scope.
|
|
181
|
+
No AIG reply. Meanwhile AIG won't enable the extra fields until we tell them the feature is live
|
|
182
|
+
→ **both sides are waiting on each other.** The feature has been functionally complete since
|
|
183
|
+
2026-07-28 and is gated on **communication + verification, not development.**
|
|
184
|
+
|
|
109
185
|
### `syncWithTogadesk()` — 2.0 ⇄ 1.0 (TOGaDesk)
|
|
110
186
|
|
|
111
187
|
A large bi-directional sync gated by **two checkpoint parameters** stored in 2.0 via
|
|
@@ -176,18 +252,51 @@ enable flags** and an optional `$monitorTogadeskDepartmentIds[]`:
|
|
|
176
252
|
enough to hold several `'; '`-joined emails, so TRUE-79401 needed **no** schema/dbchanges migration
|
|
177
253
|
(the earlier fear that it was ~VARCHAR(55) was wrong). `TOGA_AIG` is the 1.0 legacy DB;
|
|
178
254
|
`Client_Aig` is the 2.0 prod tenant.
|
|
179
|
-
- **The multi-email path is not yet exercised (
|
|
180
|
-
`Client_Aig.ContactEmailAddresses` rows are one-per-contact
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
255
|
+
- **The multi-email path is not yet exercised in prod data (TRUE-79401)** — as of 2026-07-28 all 20
|
|
256
|
+
`Client_Aig.ContactEmailAddresses` rows are one-per-contact. The *payload-shape* question that
|
|
257
|
+
used to sit here is **RESOLVED** — see *TRUE-79401 payload shape* above. Verification (real row
|
|
258
|
+
counts on beta, CREATE **and** UPDATE paths) is the remaining open item.
|
|
259
|
+
- **Do not "fix" the single-recipient registration email.**
|
|
260
|
+
`_Model_Aig_Entitlement::postPost` sends the Staples Protection Plan registration email to
|
|
261
|
+
`addTo($payload->contact->primaryContactEmailAddress->emailAddress)` only. That is **correct by
|
|
262
|
+
design**: TRUE-79401's scope is *verification/lookup*, and the team explicitly rejected
|
|
263
|
+
CC/BCC-ing every address onto TogaDesk tickets. Multi-recipient notification is out of scope.
|
|
264
|
+
- **The dedupe `LIKE` is an equality match a stored list can no longer satisfy (latent).**
|
|
265
|
+
`syncContactToToga1Customer`'s fallback lookup is
|
|
266
|
+
`WHERE Customers.emailAddress LIKE '$primaryEmailAddress'` — **no wildcards**, so it is an
|
|
267
|
+
equality match. Now that the column can hold a `'; '`-joined list, a 1.0 row that already holds a
|
|
268
|
+
list will **not** match a primary-only lookup. Shielded in practice because dedupe is
|
|
269
|
+
authoritatively keyed on `c_togaCustomerId` first and a successful sync writes that id back to
|
|
270
|
+
2.0, dropping the contact out of the `c_togaCustomerId IS NULL` query forever. It only bites if
|
|
271
|
+
the **write-back fails after the insert succeeds** — a retry then inserts a **duplicate**
|
|
272
|
+
customer instead of matching. Hardening if it ever fires: `LIKE '%$primaryEmailAddress%'`. Note
|
|
273
|
+
this is a consequence of a deliberate deviation: the 2026-05-01 meeting said to drop the primary
|
|
274
|
+
email entirely, but the implementation retained a separately-escaped primary for this lookup (the
|
|
275
|
+
right call — dropping it would have broken legacy record matching outright).
|
|
276
|
+
- **No `emailAddress2`/`emailAddress3` columns — deliberate.** The 2026-05-01 meeting explicitly
|
|
277
|
+
rejected adding extra email columns across the ~15 Toga Classic client DBs and chose the
|
|
278
|
+
delimited single field instead. That is **why TRUE-79401 has no `dbchanges` migration** — an
|
|
279
|
+
intentional decision, not an oversight. (The doc's earlier "fear that `Customers.emailAddress`
|
|
280
|
+
was ~VARCHAR(55)" traces to a garbled transcript line — "needs to fifty-five… maybe we should
|
|
281
|
+
change it to a long"; the column is `varchar(255)`. Closed.)
|
|
186
282
|
- **PHP 7.2** target (prod worker/library) — no arrow functions, typed properties, `??=`, `match`.
|
|
187
283
|
Lint with `C:\xampp7\php\php.exe -l`.
|
|
188
284
|
|
|
189
285
|
## Change history
|
|
190
286
|
|
|
287
|
+
- 2026-08-03 — TRUE-79401 **payload shape RESOLVED** (review/no-code session; sources: AIG's
|
|
288
|
+
field-mapping spreadsheet, the 2026-05-01 Teams meeting, the 2026-05-04 AIG email). Both
|
|
289
|
+
`UserDefined3` and `UserDefined4` map to the **same** nested
|
|
290
|
+
`contact.contactEmailAddresses[].emailAddress` collection; `UserDefined3/4` are **AIG's
|
|
291
|
+
source-side labels only** and exist nowhere in `api2`/`_underscore` — so **no api2 intake mapping
|
|
292
|
+
is needed** and the 2026-07-28 library change is the complete implementation. Replaced the
|
|
293
|
+
"open question, owner Paulina" gotcha with the resolved shape + a concrete JSON example. Also
|
|
294
|
+
recorded: purpose is **verification/lookup only** (multi-recipient notification explicitly
|
|
295
|
+
rejected, so `postPost`'s single-recipient email is correct by design); the three-layer
|
|
296
|
+
payload/row/delimited-string distinction; the extra-email-columns rejection that explains the
|
|
297
|
+
absent `dbchanges` migration; and the latent wildcard-less dedupe `LIKE` vs. a stored list.
|
|
298
|
+
Remaining: deploy confirmation, beta row-count verification (CREATE **and** UPDATE), sending AIG
|
|
299
|
+
a concrete JSON example, and confirming the mapping doc reached AIG. (mhammontree)
|
|
191
300
|
- 2026-07-28 — TRUE-79401: `syncContactToToga1Customer` now stores a contact's **full** email list in
|
|
192
301
|
1.0 `TOGA_AIG.Customers.emailAddress` as a `CUSTOMER_EMAIL_DELIMITER` (`'; '`)-joined, deduped,
|
|
193
302
|
escaped string (was primary-only) for AIG/Staples support lookup. Added
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
| [Health-check endpoint (/health liveness short-circuit)](features/health-check-endpoint.md) | `_Controller_Index::api()` short-circuits **liveness/health-probe** requests to an HTTP 200 **before** any routing, DB bootstrap, or V2 engine work runs. | api2/Controller/Index.php |
|
|
10
10
|
| [Language Translation Layer (audience.language + sidecar tables)](features/language-translation-layer.md) | Serves the same TOGa data (Item title/description/longDescription, plus item **feature** text — `Features.name`, `ItemCategoryFeatureGroups.name`, `ItemFeatures | api2/Component/Api/V2/V2.php, api2/Component/Api/V2/Response/Response.php, _underscore/Model/Core/Setting.php, _underscore/Model/Core/RecordField.php, _underscore/Model/Core/DefaultGlobalSetting.php, _underscore/Model/Client/ItemTranslation.php, _underscore/Model/Client/FeatureTranslation.php, _underscore/Model/Client/ItemCategoryFeatureGroupTranslation.php, _underscore/Model/Client/ItemFeatureTranslation.php, dbchanges2/Client/2026-06-23a - ItemTranslations.sql, dbchanges2/Client/2026-06-23b - ItemTranslationsAcl.sql, dbchanges2/Client/2026-07-13a - FeatureTranslations.sql, dbchanges2/Client/2026-07-13b - FeatureTranslationsAcl.sql, dbchanges2/Core/2026-06-23a - RecordFieldsTranslationColumn.sql, dbchanges2/Core/2026-06-23b - ItemTranslationsRecord.sql, dbchanges2/Core/2026-07-13 - FeatureTranslationsRecord.sql |
|
|
11
11
|
| [Nested FK object embedding is gated by the CHILD record's own ACL](features/nested-fk-acl-embedding.md) | When the V2 JSON engine serializes a foreign-key field into a **nested object** (in `getFullModelData()`, ~V2.php L6016-6060), it re-checks the **child** record | api2/Component/Api/V2/V2.php, dbchanges2/Client_Compass/2026-07-23b - PurchaseOrdersRecordReadAcl.sql |
|
|
12
|
-
| [Nested-relationship writes & child matching (link vs. create)](features/nested-relationship-writes.md) | When a 2.0 API write payload (`POST`/`PUT`) contains a **nested related object** (e.g. | api2/Component/Api/V2/V2.php |
|
|
12
|
+
| [Nested-relationship writes & child matching (link vs. create)](features/nested-relationship-writes.md) | When a 2.0 API write payload (`POST`/`PUT`) contains a **nested related object** (e.g. | api2/Component/Api/V2/V2.php, _underscore/Model/Client/ContactEmailAddress.php |
|
|
13
13
|
| [Record Scripts (computed/aggregate /v2 endpoints — the authoring contract)](features/record-scripts.md) | In api2 you almost never write a controller. | api2/Component/Api/V2/V2.php, _underscore/Model/Team/Sprint.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Query.php |
|
|
14
14
|
| [V2 Request Logging & Where Requests Land (client vs core log DB)](features/request-logging.md) | The V2 engine logs **every inbound request** — success *and* failure, with response code and payload — and routes each log entry to the **client** log or the ** | api2/Component/Api/V2/V2.php, _underscore/Model/Client/Logs/Api.php, _underscore/Model/Core/Logs/Api.php |
|
|
15
15
|
| [POST + JSON-body args for scripted APIs](features/scripted-api-post-body-args.md) | The V2 engine can run a Record Script (scripted API) for a **POST** request, and a scripted API can receive its arguments from the **JSON request body** instead | api2/Component/Api/V2/V2.php |
|
|
@@ -6,10 +6,11 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-03
|
|
10
10
|
owners: ["bala", "mhammontree", "tcox"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
13
|
+
- _underscore/Model/Client/ContactEmailAddress.php
|
|
13
14
|
related:
|
|
14
15
|
- ../architecture.md
|
|
15
16
|
- ../../../clients/aig/features/entitlement-intake.md
|
|
@@ -124,6 +125,33 @@ build — audit such rows before/with the deploy. First seen in AIG (TRUE-79978)
|
|
|
124
125
|
beta/QA threw EV-12 on the injected `entitlementFulfillmentType` — see
|
|
125
126
|
[AIG entitlement intake](../../../clients/aig/features/entitlement-intake.md).
|
|
126
127
|
|
|
128
|
+
## A wrong-shaped nested collection returns 201 and silently drops the data
|
|
129
|
+
|
|
130
|
+
V2 **ignores payload keys it does not recognize** and does not validate the *element shape* of a
|
|
131
|
+
nested collection. Two distinct failure modes both return **HTTP 201 Created** with no error
|
|
132
|
+
anywhere — no `messages[]`, nothing in the client `Logs` or the core/writer `Logs`:
|
|
133
|
+
|
|
134
|
+
1. **Unknown / flat key.** A partner sends a source-side column label (e.g. a literal
|
|
135
|
+
`UserDefined3`) instead of the nested collection. The key is dropped; no child row is created.
|
|
136
|
+
2. **Array of bare strings instead of array of objects.** `"contactEmailAddresses":
|
|
137
|
+
["email2@domain.com"]` instead of `[{"emailAddress": "email2@domain.com"}]`. Nothing is
|
|
138
|
+
created, and any consumer doing `array_column($list, 'emailAddress')` gets an **empty** list —
|
|
139
|
+
so a downstream sync silently falls back to the primary email and the extra addresses vanish
|
|
140
|
+
with no trace.
|
|
141
|
+
|
|
142
|
+
**Operational rules that follow:**
|
|
143
|
+
|
|
144
|
+
- **When verifying a nested-collection integration, assert actual child ROW COUNTS** (e.g.
|
|
145
|
+
`SELECT COUNT(*) FROM Client_Aig.ContactEmailAddresses …`). **Never** treat the HTTP status as
|
|
146
|
+
evidence — a 201 proves only that the parent was written.
|
|
147
|
+
- **Never hand a partner a path expression as the spec.** `contactEmailAddresses[].emailAddress`
|
|
148
|
+
does not pin element structure and reads as "an array of emails" to many developers. Always give
|
|
149
|
+
a **concrete JSON example** of the collection, objects included.
|
|
150
|
+
- **Know whether the collection is direct or bridged.** A direct child collection is a **flat
|
|
151
|
+
array of objects** (`contactEmailAddresses: [{emailAddress}]`); a bridge/join collection is
|
|
152
|
+
**double-wrapped** (`entitlementEntitlementCoverageTypes: [{entitlementCoverageType: {…}}]`).
|
|
153
|
+
Both shapes routinely appear in the *same* payload, so the sibling collections actively mislead.
|
|
154
|
+
|
|
127
155
|
## Gotcha
|
|
128
156
|
|
|
129
157
|
- **Nested write with only a non-identifier field silently creates duplicates.** This is a
|
|
@@ -135,6 +163,13 @@ beta/QA threw EV-12 on the injected `entitlementFulfillmentType` — see
|
|
|
135
163
|
|
|
136
164
|
## Change history
|
|
137
165
|
|
|
166
|
+
- 2026-08-03 — Documented that V2 **silently ignores unrecognized payload keys and does not
|
|
167
|
+
validate nested-collection element shape**: both a flat/unknown key and an array of bare strings
|
|
168
|
+
(instead of an array of objects) return **HTTP 201** with the data dropped and no error logged
|
|
169
|
+
anywhere. Added the verification rule (assert child row counts, never the HTTP status), the rule
|
|
170
|
+
to give partners a concrete JSON example rather than a `collection[].field` path expression, and
|
|
171
|
+
the direct-vs-bridged collection shape distinction. Surfaced while resolving the AIG TRUE-79401
|
|
172
|
+
multi-email payload shape (`Client_Aig.ContactEmailAddresses`); no code changed. (mhammontree)
|
|
138
173
|
- 2026-07-20 — Root-caused the **UPDATE-path** failure of the single-key-uuid forced-MATCH: the
|
|
139
174
|
update path injects a reverse back-reference (`contactId = parent id`) into nested child objects
|
|
140
175
|
at `V2.php:4985–4996` (commit `91d803f`) when the child has a back-FK to the parent, turning the
|
|
@@ -6,8 +6,8 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["ajean", "jcardinal"]
|
|
9
|
+
updated: 2026-08-03
|
|
10
|
+
owners: ["ajean", "jcardinal", "mhammontree"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Team/Transcripts.php
|
|
13
13
|
- worker2/Config/production.ini
|
|
@@ -140,6 +140,15 @@ process is identical for all.
|
|
|
140
140
|
those files. Acceptable as a one-off — the ledger `s3Key` is updated to match — but if you
|
|
141
141
|
ever need a minimal re-file, change the move test to compare only the `YYYY-MM-DD` folder
|
|
142
142
|
segment and relocate while preserving the original filename.
|
|
143
|
+
- **There is NO transcript coverage before ~2026-06-12 — pre-June meetings are not in our store.**
|
|
144
|
+
`Team/Transcripts/Export` first ran successfully in production on **2026-06-12** (PR #78), and it
|
|
145
|
+
works off an **incremental watermark** with a bounded `lookbackDays`; nothing in its history shows
|
|
146
|
+
a multi-week backfill ever running. So searching `Team.TranscriptExports` /
|
|
147
|
+
`Team.TranscriptProcessing` / the `togaiq` KBs for a meeting older than ~2026-06-12 comes back
|
|
148
|
+
**empty even though the meeting WAS recorded**. Do not conclude "it wasn't recorded."
|
|
149
|
+
**Recovery path for an older meeting: Microsoft Graph** (which still retains it) or a manually
|
|
150
|
+
exported `.docx`/`.vtt` — not our own store. Hit in practice on 2026-08-03: a 2026-05-01 meeting
|
|
151
|
+
had to be supplied as a downloaded `.docx`.
|
|
143
152
|
- **Cron rows cannot be `DELETE`d once they have fired.** `Core.WorkerJobs.cronJobId` has a FK
|
|
144
153
|
(`WorkerJobs_CronJobs_FK`) to `CronJobs.id`, so a schedule change must `UPDATE` an existing
|
|
145
154
|
row (and set `isActive = 0` on the ones being retired), not delete-and-reinsert. The historical
|
|
@@ -181,6 +190,12 @@ Policy gap; a `400` only on a UPN means the id was never resolved to a GUID. (A
|
|
|
181
190
|
|
|
182
191
|
## Change history
|
|
183
192
|
|
|
193
|
+
- 2026-08-03 — Documented the **pre-2026-06-12 coverage gap**: `Team/Transcripts/Export` first ran in prod on
|
|
194
|
+
2026-06-12 (PR #78) and is watermark-incremental with a bounded `lookbackDays`, with no backfill
|
|
195
|
+
ever run — so any meeting older than ~2026-06-12 is absent from `Team.TranscriptExports` /
|
|
196
|
+
`Team.TranscriptProcessing` / `togaiq` despite having been recorded. Recovery is via Microsoft
|
|
197
|
+
Graph or a manual `.docx`/`.vtt` export. Found while recovering a 2026-05-01 meeting. No code
|
|
198
|
+
changed. (mhammontree)
|
|
184
199
|
- 2026-07-09 — **`Export` re-architected to GRAPH-DIRECT** (merged into the ingestion pipeline).
|
|
185
200
|
The `toga-private` staging bucket is dropped: `Export` is now a thin poller that records a
|
|
186
201
|
discovery row and enqueues one `Process` job per transcript; `Process` downloads the VTT
|
|
@@ -5,13 +5,14 @@ project: API
|
|
|
5
5
|
client: aig
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
8
|
+
updated: 2026-08-03
|
|
9
9
|
owners: ["mhammontree"]
|
|
10
10
|
files:
|
|
11
11
|
- _underscore/Model/Aig/Entitlement.php
|
|
12
12
|
- dbchanges2/Client_Aig/2026-06-18a - TRUE-79534 AIG SaleItem codes.sql
|
|
13
13
|
related:
|
|
14
14
|
- clients/aig/features/contract-reconciliation.md
|
|
15
|
+
- 1.0/apps/library/features/toga2-api-client-and-bridge.md
|
|
15
16
|
- 2.0/apps/api2/features/request-logging.md
|
|
16
17
|
- 2.0/apps/api2/features/nested-relationship-writes.md
|
|
17
18
|
- 2.0/apps/api2/architecture.md
|
|
@@ -121,6 +122,23 @@ Loading the catalog fixed intake, and because the feed re-sends outstanding cont
|
|
|
121
122
|
**backlog of dropped contracts self-healed on the next scheduled run** — no manual back-fill was
|
|
122
123
|
needed (see [contract reconciliation](contract-reconciliation.md)).
|
|
123
124
|
|
|
125
|
+
## Business Unit (`UserDefined2`) — answered and DROPPED (nothing to build)
|
|
126
|
+
|
|
127
|
+
AIG (Alissa, 2026-05-04) confirmed the **Business Unit / Value** field needs **nothing returned to
|
|
128
|
+
AIG**: they already store it from the sale, and it is used only for **sales invoicing**, which is a
|
|
129
|
+
**different process from claims invoicing** (the one we participate in). We replied asking them to
|
|
130
|
+
**omit it from the API call entirely**. **No invoice-file template was ever needed.**
|
|
131
|
+
|
|
132
|
+
Verified there is no dead custom field to clean up: `c_businessUnit` exists **nowhere** in
|
|
133
|
+
`_underscore`, `api2`, or `dbchanges2` (the only `BusinessUnits` hits in the tree are unrelated, in
|
|
134
|
+
the `forecast` repo). Corroborated by AIG's field-mapping spreadsheet, where the `UserDefined2`
|
|
135
|
+
row's target column is **empty** while every other row is mapped.
|
|
136
|
+
|
|
137
|
+
**This — not the extra emails — was the item actually blocking Paulina from sending the mapping
|
|
138
|
+
document.** The two were conflated previously. See
|
|
139
|
+
[the library bridge doc](../../../1.0/apps/library/features/toga2-api-client-and-bridge.md) for the
|
|
140
|
+
resolved email-payload shape.
|
|
141
|
+
|
|
124
142
|
## Key files / entry points
|
|
125
143
|
|
|
126
144
|
- **`_underscore/Model/Aig/Entitlement.php`** — `prePost(&$api, &$payload)` is the PRE-POST
|
|
@@ -223,6 +241,19 @@ this interceptor or use this dual-purpose Items pattern.
|
|
|
223
241
|
|
|
224
242
|
## Gotchas / known issues
|
|
225
243
|
|
|
244
|
+
- **`postPost`'s SINGLE-recipient registration email is correct by design — do not "fix" it.**
|
|
245
|
+
It sends the Staples Protection Plan registration email to
|
|
246
|
+
`addTo($payload->contact->primaryContactEmailAddress->emailAddress)` only. TRUE-79401's multi-email
|
|
247
|
+
work is for **verification/lookup only** (a support agent finding a caller by any of their
|
|
248
|
+
addresses); the team **explicitly rejected** CC/BCC-ing every address onto TogaDesk tickets, so
|
|
249
|
+
multi-recipient notification is out of scope. Settled in the 2026-05-01 meeting: "it's just for
|
|
250
|
+
visual, this is for verification, it's a string."
|
|
251
|
+
- **A wrong-shaped nested collection on intake returns HTTP 201 and silently drops the data.**
|
|
252
|
+
Both a flat/unknown key (a literal `UserDefined3`) and an array of bare strings
|
|
253
|
+
(`"contactEmailAddresses": ["a@b.com"]` instead of `[{"emailAddress": "a@b.com"}]`) are accepted
|
|
254
|
+
with **no error anywhere**. When verifying AIG's multi-email feed, assert **actual
|
|
255
|
+
`Client_Aig.ContactEmailAddresses` row counts** — never the HTTP status. See
|
|
256
|
+
[api2 nested-relationship writes](../../../2.0/apps/api2/features/nested-relationship-writes.md).
|
|
226
257
|
- **A sale-item code missing from `Client_Aig.Items` currently produces an HTTP 500, not a clean
|
|
227
258
|
"Missing AIG item ID."** In prod (TRUE-79534) a valid-but-unknown code made `$itemId` empty and
|
|
228
259
|
the downstream fulfillment query threw MySQL ERROR 1064 — so the request 500'd and the contract
|
|
@@ -275,6 +306,15 @@ this interceptor or use this dual-purpose Items pattern.
|
|
|
275
306
|
|
|
276
307
|
## Change history
|
|
277
308
|
|
|
309
|
+
- 2026-08-03 — Review-only session (no code changed). **Business Unit (`UserDefined2`) is answered
|
|
310
|
+
and dropped**: AIG (2026-05-04) needs nothing returned — it is theirs, from the sale, for **sales**
|
|
311
|
+
invoicing (not claims invoicing); we asked them to omit it from the API call and **no invoice-file
|
|
312
|
+
template was needed**. Verified `c_businessUnit` exists nowhere in `_underscore`/`api2`/`dbchanges2`,
|
|
313
|
+
so there is no dead field to clean up; the mapping spreadsheet's `UserDefined2` target column is
|
|
314
|
+
empty. This — not the extra emails — was what blocked Paulina from sending the mapping document.
|
|
315
|
+
Also recorded that `postPost`'s **single-recipient** registration email is **correct by design**
|
|
316
|
+
(TRUE-79401 is verification/lookup only; multi-recipient CC/BCC was explicitly rejected), and the
|
|
317
|
+
201-with-dropped-data trap when verifying the nested `contactEmailAddresses` feed. (mhammontree)
|
|
278
318
|
- 2026-07-29 — TRUE-80179 / TRUE-79534 (**RESOLVED + correction**): the `STS_001` intake gap is
|
|
279
319
|
**closed and it self-healed.** After the catalog load + collation fix shipped, AIG's next
|
|
280
320
|
scheduled batch delivered all **156** outstanding `STS_001` contracts and every one succeeded
|
package/package.json
CHANGED