toga-ai 1.0.438 → 1.0.439

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.
@@ -0,0 +1,73 @@
1
+ ---
2
+ type: session
3
+ slug: true-79533-pin-depth
4
+ title: TRUE-79533 WH service-address pin blocked by depth=-1 starving postPost
5
+ author: mhammontree
6
+ repos: [_underscore, dbchanges2, toga2-view]
7
+ framework: "2.0"
8
+ client: rate
9
+ status: active
10
+ created: 2026-07-27
11
+ updated: 2026-07-27
12
+ ---
13
+
14
+ # Session: true-79533-pin-depth
15
+ **Date:** 2026-07-27
16
+ **Project/Repo:** _underscore / dbchanges2 / toga2-view (2.0, client Rate)
17
+ **Task:** Get the validated Whole Home Warranty service address to pin (`Entitlements.serviceAddressId`) and render on beta. Backend is done and correct; the live blocker is that the beta purchase POST sends `depth=-1`, which starves the `postPost` interceptor of the just-created address id.
18
+
19
+ ---
20
+
21
+ ## What WORKED
22
+ <!-- Include specific file paths and evidence -->
23
+ - **Backend pin implemented** in `_underscore/Model/Rate/Entitlement.php` `postPost` → `persistWarrantyServiceAddress`: reads the created address id from the in-memory hydrated payload (`$payload->contact->primaryContactAddress->address->id`), then `UPDATE Addresses SET isValidated=1` + `UPDATE Entitlements SET serviceAddressId=… WHERE uuid=…`. Committed `dff1fb5e`. **Proven working**: a no-`depth` (deep-payload) purchase — ET100057, made from the local FE against the beta backend — pinned `serviceAddressId=56` with `Addresses.id 56` (STE 400) `isValidated=1`.
24
+ - **`serviceAddressId` is a STANDARD field** on base `_underscore/Model/Client/Entitlement.php:20` (`FIELD_FOREIGNKEY` → `_Model_Client_Address`), inherited by the Rate model. Verified in the model file. So V2 persists/reads it; the postPost UPDATE and the FE read both rely on this.
25
+ - **Display EZ-2 fixed**: `dbchanges2/Client_Rate/2026-07-24a - AddressIdFieldPermission.sql` grants READ on `Addresses.id` for roles 1/2/3 (isWritable 0). Committed `ea1e0fb`. Applied on beta manually and verified (`AclFieldPermissions` field 41 → roles 1,2,3). This fixes the FE `GET /v2/addresses?fields=id,…` 403.
26
+ - **Hard-blocks now return HTTP 400** via `_Exception_Validation` (the 3 prePost throws: missing address, unverifiable address, duplicate WH). Committed `41a5dc1a`. Verified against `api2/Controller/Index.php:204-222` — it catches `_Exception_Validation` → `DEFINED_MESSAGE_ERROR_BAD_REQUEST` (400 + clean message); all other exceptions → 500 + stack trace. This is what makes the FE blocked-purchase toaster work.
27
+ - **Dedup** `hasActiveWarrantyAtAddress` forces the WRITE host + disables the query cache for a fresh read (committed). `COALESCE(serviceAddressId, ContactAddresses.addressId)` fallback so unpinned WHs are still deduped.
28
+ - **Root cause proven via `Logs_Rate.Api`** (beta, MCP env `dev-sandbox`): the local-FE and beta-FE purchases hit the SAME beta backend instance (`i-0ce5624547cfecc93`, `api.beta.togahub.com`). The ONLY difference is the request query string — local: `transactionId=…` (no depth) → 64,650-byte deep response → **pinned**; beta: `depth=-1&transactionId=…` → 644-byte shallow response → **starved** (no address node in the payload postPost receives).
29
+ - **`-1` source confirmed = front-end**: `toga2-view/src/pages/CheckOut/api/checkoutApi.ts:21` (`createService` passes `{ 'depth': -1 }`), serialized to `?depth=-1`. Confirmed in the logged query string.
30
+ - **Beta interceptor config read** (`ApiPayloadInterceptors` join `Core.Records`): entitlements `postPost` = id **2**, active, **minDepth 5**; entitlements `prePost` = id 4, active, minDepth **null → default 3**; sales-orders `postPost` = id 3, null→3.
31
+
32
+ ## What did NOT work — DO NOT RETRY THESE
33
+ <!-- Exact failure reasons — do not vague-ify -->
34
+ - **Forcing the write host inside `persistWarrantyServiceAddress`** (commit `7b63955d`, later removed in `dff1fb5e`): NO EFFECT. V2 already globally disables the read host for the entire create block (`api2/.../V2.php:5024`), so the lookup was ALREADY on the write connection — forcing it again was a no-op. Confirmed by a genuinely post-deploy purchase (ET100051/52/53) still returning null.
35
+ - **The original 3-table JOIN lookup** in postPost (`Entitlement → Contacts.primaryContactAddressId → ContactAddresses → addressId`): returned 0 rows *in-request* on beta even on the write connection. The identical query resolves correctly *post-commit*. Superseded by the payload approach.
36
+ - **The payload-based pin (`$address->id`) still returned null on beta** — NOT because the code is wrong, but because with `depth=-1` the payload built for `postPost` is shallow (`meta.calcDepth: 1`) and does **not contain the `contact.primaryContactAddress.address` node at all**, so `$address->id` (and the `uuid` fallback) find nothing. Root cause is the depth, not the pin code.
37
+ - **Merging `_underscore` to `_beta` did NOT deploy to beta.** The beta EB env **API-Sandbox-Dev** pulls `_underscore` from branch **`_sandbox-dev`** via `api2/.platform/hooks/prebuild/git.sh` + `api2/.ebextensions/git.sandbox-dev.json` (the prebuild hook `rm -Rf`s and re-clones that branch, overwriting the pipeline copy). Deploying anything not on `_sandbox-dev` runs old code.
38
+ - **"Removing `depth=-1` from the beta curl" (Fri/earlier)**: the edited curl STILL had `depth=-1` in the query string (it wasn't actually removed) — confirmed via the logged `queryString`. So that test never proved anything.
39
+ - **The single-host-vs-multi-host connection theory** (an earlier hypothesis): DISPROVEN. Both local-FE and beta-FE requests run on the SAME beta backend instance. The difference is purely the request's `depth` param, not connection topology.
40
+
41
+ ## Not tried yet (candidates for next session)
42
+ - **THE decisive test (do this first):** on beta `Client_Rate`, `UPDATE ApiPayloadInterceptors SET minDepth = 10 WHERE id = 2;` then fire one fresh WH purchase (FE still sends `depth=-1`) and check `meta.calcDepth` + whether the new entitlement pins (`serviceAddressId` set, its address `isValidated=1`). Open question for Rohan: does the `minDepth` clamp engage for the `-1` sentinel at all? It's already 5 yet the build came through at `calcDepth 1` — so either it needs > 5 or `-1` bypasses the clamp.
43
+ - **FE fix (alternative/parallel):** drop `{ 'depth': -1 }` at `toga2-view/src/pages/CheckOut/api/checkoutApi.ts:21` (or set ≥ minDepth), deploy `toga2-view`. This is the FE-side lever; it's owned by Tanner/Alex and the repo is currently on `_beta`.
44
+ - Merge `_underscore` `TRUE-79533` → `_sandbox-dev` + deploy (ships the pin + 400 fix; backend is NOT yet on beta).
45
+ - Merge `dbchanges2` `TRUE-79533` to its deploy branch for prod parity (beta already has the migrations, applied manually).
46
+ - Run `/capture`.
47
+ - File side tickets: (1) framework `minDepth`-clamp appears to be a no-op for `depth=-1` (starves ALL payload interceptors); (2) AIG contract block (`postPost`) has the SAME `depth` starvation — silently skipped for beta-FE purchases; (3) SECURITY: plaintext GitHub PAT committed in `api2/.ebextensions/git.*.json` — rotate + move to env config.
48
+
49
+ ## Current file state
50
+ | File | Status | Notes |
51
+ |------|--------|-------|
52
+ | `_underscore/Model/Rate/Entitlement.php` | Committed on `TRUE-79533` (HEAD `41a5dc1a`) | payload-based pin (`dff1fb5e`), dedup write-host, `_Exception_Validation` hard-blocks (`41a5dc1a`). NOT merged to `_sandbox-dev`. |
53
+ | `_underscore/Model/Client/Entitlement.php` | Committed | `serviceAddressId` standard FK field (line 20). |
54
+ | `dbchanges2/Client_Rate/2026-07-24a - AddressIdFieldPermission.sql` | Committed (`ea1e0fb`) | `Addresses.id` read ACL. Applied on beta manually. |
55
+ | `dbchanges2` serviceAddressId col + `Core` field + ACL migrations | Committed on `TRUE-79533` | Applied on beta manually; branch needs merge for prod. |
56
+ | `toga2-view/src/pages/CheckOut/api/checkoutApi.ts` | UNCHANGED (on `_beta`) | Line 21 still `'depth': -1` — the FE fix is pending. |
57
+ | `test/@Mark/Rate/verify_wholehome_per_address_guard.php` | Updated (personal, not in a repo) | `c_serviceAddressId` → `serviceAddressId`; documents the multi-host/depth caveat. |
58
+
59
+ ## Decisions made
60
+ - **Pin from the in-memory hydrated payload, not a DB read-back.** Rationale: in-request DB SELECTs can't reliably see the transaction's own uncommitted nested writes; V2's `getFullModelData` hydrates the payload before `postPost`. Rejected: the JOIN lookup (0 rows in-request) and an async post-commit worker (more surface; unnecessary once depth is fixed).
61
+ - **`serviceAddressId` = STANDARD field** (per Jeff), not the old Rate-custom `c_serviceAddressId`.
62
+ - **Hard-blocks throw `_Exception_Validation`** so they map to HTTP 400 with the message surfaced (the FE checkout expects 400). Rejected: plain `_Exception` → 500 + stack trace.
63
+ - **Preferred depth fix = raise the `postPost` interceptor `minDepth`** (Rohan's lever) over the FE change: config-only, FE-independent, and also un-breaks the AIG block. Pending the beta test to confirm the clamp actually deepens the build for a `depth=-1` request.
64
+
65
+ ## Blockers
66
+ - Pin cannot fire on beta until the `depth` collision is resolved: FE sends `depth=-1`, and the `postPost` interceptor `minDepth 5` floor is NOT producing a deep-enough payload (`calcDepth 1`). Open question to Rohan: does the `minDepth` clamp engage for `depth=-1`, and what target value? Needs the beta `UPDATE` test to settle.
67
+ - Backend (`TRUE-79533`) is not yet on beta — it lives on `TRUE-79533`, but beta deploys `_underscore` from `_sandbox-dev`.
68
+
69
+ ## Exact next step
70
+ > On beta (`dev-sandbox`, `Client_Rate`) run `UPDATE ApiPayloadInterceptors SET minDepth = 10 WHERE id = 2;`, then make one fresh WH purchase (FE unchanged, still sends `depth=-1`) and check the create response `meta.calcDepth` plus the new entitlement's `serviceAddressId` and its `Addresses.isValidated`. If it pins → the clamp works and 5 was too low; dial in the minimal value and ship it as a `dbchanges2 Client_Rate` migration (no FE change). If still null / `calcDepth` stays 1 → the clamp doesn't engage for `-1`; fix the FE (`checkoutApi.ts:21` drop `depth:-1`) instead. Confirm the intended target value with Rohan first.
71
+
72
+ ---
73
+ _Saved by /session-save on 2026-07-27_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.438",
3
+ "version": "1.0.439",
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",