toga-ai 1.0.424 → 1.0.425

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.
@@ -8,5 +8,5 @@
8
8
  | [Rate SAML SSO](features/saml-sso.md) | 2.0 | Rate uses Azure AD as its IdP (`login.rate.com`). | _underscore/Model/Rate/ClientAuthentication.php, saml/Controller/Index.php, toga2-view/src/hooks/useAuthenticationFlow.ts |
9
9
  | [Service Card Entitlement Display](features/service-card-entitlements.md) | 2.0 | Rate's home and services pages display one service card per purchased entitlement. | src/components/ServiceCard/ServiceCard.tsx, src/components/ServiceCard/index.ts, src/hooks/useBundleServices.ts, src/hooks/useActiveServices.ts, src/pages/Home/api/homeApi.ts, src/pages/Home/view/HomePage.tsx, src/pages/Home/viewModels/useHomePageViewModel.ts, src/pages/Services/view/ServicesPage.tsx, src/pages/Services/viewModels/useServicePageViewModel.ts, src/api/serviceAddressApi.ts, src/api/apiErrors.ts |
10
10
  | [Rate Service-Purchase Confirmation Emails (Tech / Warranty)](features/service-purchase-emails.md) | 2.0 | When a Rate customer purchases a service, a confirmation email is sent. | _underscore/Model/Rate/Entitlement.php, worker2/Worker/Notification/EmailTemplate.php, dbchanges2/Client_Rate/2026-06-30a - Rate purchase email templates.sql |
11
- | [Rate Whole Home Warranty Per-Address Purchase Guard](features/whole-home-warranty-purchase-guard.md) | 2.0 | > **⚠ DEPLOYING beta→production, NOT YET PROD-VERIFIED (as of 2026-07-23).** TRUE-79533 is > beta-verified (PM Paulina tested the WH purchase flow on beta) and | _underscore/Model/Rate/Entitlement.php, _underscore/Model/Client/Entitlement.php, _underscore/Model/Client/Address.php, dbchanges2/Client/2026-07-22a - EntitlementServiceAddressId.sql, dbchanges2/Core/2026-07-22a - EntitlementServiceAddressIdField.sql, dbchanges2/Client/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql, test/@Mark/Rate/verify_wholehome_per_address_guard.php |
11
+ | [Rate Whole Home Warranty Per-Address Purchase Guard](features/whole-home-warranty-purchase-guard.md) | 2.0 | > **⚠ DEPLOYING beta→production, NOT YET PROD-VERIFIED (as of 2026-07-23).** TRUE-79533 is > beta-verified (PM Paulina tested the WH purchase flow on beta) and | _underscore/Model/Rate/Entitlement.php, _underscore/Model/Client/Entitlement.php, _underscore/Model/Client/Address.php, dbchanges2/Client/2026-07-22a - EntitlementServiceAddressId.sql, dbchanges2/Core/2026-07-22a - EntitlementServiceAddressIdField.sql, dbchanges2/Client_Rate/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql, test/@Mark/Rate/verify_wholehome_per_address_guard.php |
12
12
  | [Rate](profile.md) | 2.0 | Rate is a mortgage/lending client. | |
@@ -14,7 +14,7 @@ files:
14
14
  - _underscore/Model/Client/Address.php
15
15
  - dbchanges2/Client/2026-07-22a - EntitlementServiceAddressId.sql
16
16
  - dbchanges2/Core/2026-07-22a - EntitlementServiceAddressIdField.sql
17
- - dbchanges2/Client/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql
17
+ - dbchanges2/Client_Rate/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql
18
18
  - test/@Mark/Rate/verify_wholehome_per_address_guard.php
19
19
  related:
20
20
  - clients/rate/profile.md
@@ -32,11 +32,12 @@ related:
32
32
  > **shared-interceptor merge hazard** gotcha and the **dangling-pin** / **unguarded prod
33
33
  > interceptor** gotchas before merging to `_production`.
34
34
  >
35
- > **🚨 LAUNCH BLOCKER (added 2026-07-23, tcox) the standard `serviceAddressId` field has NO
36
- > READ ACL grant.** The custom→standard redesign shipped the column + `Core.RecordFields` row
37
- > but **not** the `AclFieldPermissions` read grant, so `GET /v2/entitlements?fields=serviceAddressId`
38
- > returns **403 `EZ-2`** for roles 1/2/4. Prod does **not** launch clean without the new
39
- > `Client/2026-07-23a` field-permission migration below — see
35
+ > **🚨 LAUNCH BLOCKER — RESOLVED (migration built 2026-07-23, mhammontree; found 2026-07-23,
36
+ > tcox) — the standard `serviceAddressId` field had NO READ ACL grant.** The custom→standard
37
+ > redesign shipped the column + `Core.RecordFields` row but **not** the `AclFieldPermissions`
38
+ > read grant, so `GET /v2/entitlements?fields=serviceAddressId` returned **403 `EZ-2`**. The fix
39
+ > now ships as **`dbchanges2/Client_Rate/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql`**
40
+ > (committed `b74f42a` on TRUE-79533) — see
40
41
  > **[Launch blocker: standard field needs a READ ACL grant](#launch-blocker-true-79533--the-standard-field-needs-a-read-acl-grant)**.
41
42
 
42
43
  ## Summary
@@ -144,7 +145,7 @@ during backend review (Jeff). This is the single biggest change since the beta b
144
145
 
145
146
  **The custom→standard redesign shipped the column and the `Core.RecordFields` row but NOT the
146
147
  `AclFieldPermissions` READ grant.** Probe-verified on **beta 2026-07-23**:
147
- `GET /v2/entitlements?fields=serviceAddressId` returns **403 `EZ-2`** for roles 1, 2, and 4
148
+ `GET /v2/entitlements?fields=serviceAddressId` returns **403 `EZ-2`** (no read ACL row)
148
149
  (Entitlements has `Core.Records.aclDatabase = 'CLIENT'`, so the grant lives in each client DB).
149
150
  Without the grant, **production launches with the exact `EZ-2` failure beta hit** — the field is
150
151
  registered but unreadable, so the service cards cannot fetch the pin.
@@ -157,16 +158,20 @@ field is authorized through **`AclFieldPermissions`**, keyed by the **`Core.Reco
157
158
  because the `CustomRecordFields` row for `c_serviceAddressId` was deleted in the standard-field
158
159
  redesign. The read grant now has to be an **`AclFieldPermissions`** row instead.
159
160
 
160
- **Needed migration (proposed, on the TRUE-79533 branch):**
161
- `dbchanges2/Client/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql`, following the
162
- existing `Client/2026-07-02c - TrackingNumberNeedsReturnLabelFieldPermission.sql` precedent:
163
- - Resolve field ids via `Core.RecordFields` / `Core.Records` subselects (route `'entitlements'`,
164
- fields `'serviceAddressId'` **and** its sibling `'number'`) — never hardcode ids.
165
- - **Mirror the sibling `number` field's per-role grants**, but force **`isWritable = 0`**:
166
- `serviceAddressId` is written **server-side** by the guard's `postPost`, never by the API caller,
167
- so it must be read-only.
168
- - Guard each INSERT with `NOT EXISTS` (verified against the Client blank DDL:
169
- `AclFieldPermissions` is `UNIQUE (recordFieldId, roleId)`).
161
+ **The migration that ships (built + committed `b74f42a` on TRUE-79533):**
162
+ `dbchanges2/Client_Rate/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql`:
163
+ - Resolves field ids via cross-schema `Core.RecordFields` / `Core.Records` subselects (route
164
+ `'entitlements'`, field `'serviceAddressId'`) never hardcodes ids.
165
+ - **Grants READ only (`isWritable = 0`)**`serviceAddressId` is written **server-side** by
166
+ `_Model_Rate_Entitlement::postPost` via raw SQL, never by the API caller.
167
+ - **Mirrors the reader ROLE(s) of the sibling standard FK `saleItemId` which is `role 1` in
168
+ `Client_Rate`** (the same role the earlier `c_serviceAddressId` custom grant used). Although
169
+ the cross-role probe showed roles 1/2/4 all lacked a grant, **role 1 is the only actual reader**
170
+ of this field (Rate's toga2-view service cards), so the grant is role-1 only.
171
+ - Guards each INSERT with `NOT EXISTS` — safe no-op until the field is registered
172
+ (`AclFieldPermissions` is `UNIQUE (recordFieldId, roleId)`).
173
+ - **Scoped to `Client_Rate`, not a `Client/` fan-out** — Rate is the only client whose UI reads
174
+ the field today. A `Client/` fan-out grant is the follow-up if another client's UI needs it.
170
175
 
171
176
  ## Beta data migration — old-column pins are orphaned by the redesign (beta only, NO prod impact)
172
177
 
@@ -289,6 +294,17 @@ live carrier waterfall is opt-in via `RUN_LIVE=1` (defaults off). Verified **18/
289
294
  the deleted `2026-07-07a` interceptor registration was redundant — and why keeping it would have
290
295
  been a latent hazard: a fresh environment running both would **double-register**, firing
291
296
  `prePost` twice. **Recommend adding a `NOT EXISTS` guard to the `2026-07-15` file.**
297
+ - **`AclFieldPermissions` has NO `isReadable` column — a ROW grants READ; `isWritable`
298
+ *additionally* grants WRITE** (reusable platform fact, verified against the api2 enforcement
299
+ code, not just schema). In `api2/Component/Api/V2/V2.php`, `getAclFieldPermissions()` (~line
300
+ 5731) sets the field key regardless of `isWritable`, and the read-authorization checks test key
301
+ **presence** (`isset`/`array_key_exists`). Consequences: to make a field API-**readable** you
302
+ insert an `AclFieldPermissions` row (`isWritable = 0` for read-only); to make it **writable** set
303
+ `isWritable = 1`. And promoting a custom (`c_`) field to a **standard** field means its read grant
304
+ must be **re-created** as a standard `AclFieldPermissions` row (keyed by the `Core.RecordFields`
305
+ id) — the old `AclCustomFieldPermissions` grant (keyed by `CustomRecordFields` id) does **not**
306
+ carry over. This is exactly why the `c_serviceAddressId` grant did not cover the standard field
307
+ and the launch-blocker migration above was needed.
292
308
  - **`Core.RecordFields` has no `foreignRecordId` column** — FK resolution for `serviceAddressId`
293
309
  is entirely **model-side** (the `_Model_Client_Entitlement` FK declaration). The RecordField row
294
310
  only carries type/identifier/childPolicy/precision, mirroring sibling FKs `saleItemId`/`vendorId`.
@@ -297,6 +313,17 @@ live carrier waterfall is opt-in via `RUN_LIVE=1` (defaults off). Verified **18/
297
313
 
298
314
  ## Change history
299
315
 
316
+ - 2026-07-23 — **READ-ACL grant BUILT — launch blocker resolved** (TRUE-79533, committed `b74f42a`).
317
+ Added `dbchanges2/Client_Rate/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql`: a
318
+ READ-only (`isWritable = 0`) `AclFieldPermissions` grant for the standard `serviceAddressId`,
319
+ resolving field ids cross-schema (route `entitlements`), `NOT EXISTS`-guarded. Final scope
320
+ corrected vs. the earlier proposal: **`Client_Rate` (not a `Client/` fan-out), role 1 only** —
321
+ mirroring the sibling standard FK `saleItemId`'s reader role (role 1 is the sole actual reader,
322
+ the toga2-view service cards). Recorded the reusable platform fact that **`AclFieldPermissions`
323
+ has no `isReadable` column — row presence grants READ, `isWritable` additionally grants WRITE**
324
+ (verified in `api2/Component/Api/V2/V2.php` `getAclFieldPermissions()` ~line 5731), which is why
325
+ a custom→standard promotion must re-create the read grant as an `AclFieldPermissions` row.
326
+ (mhammontree)
300
327
  - 2026-07-23 — **LAUNCH BLOCKER found: the standard `serviceAddressId` field has NO read ACL grant**
301
328
  (tcox). Probe-verified on beta: `GET /v2/entitlements?fields=serviceAddressId` → 403 `EZ-2` for
302
329
  roles 1/2/4 (`aclDatabase=CLIENT`). Standard fields are authorized via **`AclFieldPermissions`**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.424",
3
+ "version": "1.0.425",
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",