toga-ai 1.0.444 → 1.0.445

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,74 @@
1
+ ---
2
+ type: session
3
+ slug: true-79191-fulfill-ship
4
+ title: TRUE-79191 Fulfill & Ship — package Units of Measure full-stack (shipped, verified) + local-env fixes
5
+ author: mhammontree
6
+ repos: [dbchanges2, _underscore, api2, toga2-supply]
7
+ framework: "2.0"
8
+ client: shared
9
+ status: active
10
+ created: 2026-07-27
11
+ updated: 2026-07-27
12
+ ---
13
+
14
+ # Session: true-79191-fulfill-ship
15
+ **Date:** 2026-07-27
16
+ **Project/Repo:** toga2-supply (2.0) + _underscore / api2 / dbchanges2
17
+ **Task:** Implement the package Units-of-Measure feature full-stack (dbchanges2 → _underscore models → api2 metadata → toga2-supply UI), verify save/retrieve locally, and prep deployment.
18
+
19
+ ---
20
+
21
+ ## What WORKED
22
+ <!-- All verified this session -->
23
+ - **dbchanges2 UoM migrations (4 files, committed d52f811 / 3d18971 / ceeb95d), applied + verified idempotent across all 11 local client DBs + Core_2:**
24
+ - `Client/2026-07-22a - MeasureUomColumns.sql` — `Measures.measureType ENUM('LENGTH','WEIGHT')` + `conversionFactorToBase DECIMAL(18,9)`; idempotent seed of **Inch** (LENGTH, factor 1) + **Pound** (WEIGHT, factor 1) via fixed v4-literal uuids + `NOT EXISTS` on slug.
25
+ - `Client/2026-07-22b - TrackingNumberMeasureIds.sql` — `lengthMeasureId` (governs length/width/height) + `weightMeasureId`; backfill existing rows → Inch/Pound (9605 rows on Growrk); then **hard FK constraints** (order: cols → backfill → FK). `ORDER BY id ASC` on backfill subqueries (defensive vs junk `slug='Text'` rows that exist in Compass/CompassCanada).
26
+ - `Core/2026-07-22a - MeasureUomRecordFields.sql` — RecordFields **2436** lengthMeasureId + **2437** weightMeasureId on tracking-numbers (MATCH/NULL, like shippingMethodId), **2438** measureType on measures (STRING, like signatureType).
27
+ - `Client/2026-07-22c - TrackingNumberMeasureIdsFieldPermission.sql` — write grants for the 2 FK fields (mirror **shippingMethodId**) + **read** grant for `measures.measureType` (mirror sibling **slug**).
28
+ - **_underscore models (committed 77fae34c), linted + php-reviewer clean:** `Measure.php` (+`measureType` FIELD_CHAR, +`conversionFactorToBase` FIELD_DECIMAL); `TrackingNumber.php` (+`lengthMeasureId`/`weightMeasureId` `[FIELD_FOREIGNKEY, FIELDOPT_FOREIGNKEY_MODEL => '\_Model_Client_Measure']`).
29
+ - **api2 — NO code change needed** (metadata-driven; confirmed zero refs to needsReturnLabel/signatureType). Fields exposed purely via Core RecordFields + Client ACL.
30
+ - **toga2-supply frontend (commit e91f7029d, 10 files, `tsc --noEmit` clean):** two RHF selects fed by `GET /v2/measures` split client-side by `measureType`; FK write as **nested relation `{uuid}`** (`lengthMeasure`/`weightMeasure`, mirrors shippingCarrier — NOT scalar id); read field-selectors request `lengthMeasure`/`weightMeasure`; default Inch/Pound; Pending Shipments dimension unit from length symbol.
31
+ - **End-to-end save/retrieve VERIFIED in DB:** new tracking number **9676** (SO 7221433 / NetSuite, created 2026-07-27 12:06) has `lengthMeasureId=1` (inch) / `weightMeasureId=2` (pound) — populated by the **frontend create payload** (proves real persistence: column defaults NULL, and the 07-22 backfill predates this row).
32
+ - **Local-env fixes** (see failures section for why these were NOT our code): `pcre.jit=0` in php.ini; `Items.isFulfillable` column added to all 11 local client DBs.
33
+ - Deployment doc `C:\WWW\TRUE-79191_deployment_order.md` updated with all 4 UoM migrations + notes.
34
+
35
+ ## What did NOT work — DO NOT RETRY THESE
36
+ - **First `22c` mirrored `needsReturnLabel` for the measure-field WRITE grants** → on `Client_True` it granted **nothing** (needsReturnLabel's own grants never propagated to every client). Writing lengthMeasureId/weightMeasureId there would return **EV-9**. FIX: mirror **`shippingMethodId`** (stable, universal, MATCH FK, same form) — it correctly copies each client's own writer roles (Growrk=1, Compass=1,3,4).
37
+ - **Skipped granting `measures.measureType`** (assumed read-only fields need no ACL) → **WRONG.** The V2 engine builds each GET's **output** field list from `AclFieldPermissions` (`getAclFieldPermissions` + `foreach ($aclFieldPermissions as $field)` output loops in `V2.php`), so a field with no grant row for the caller's role is **silently omitted**. `GET /v2/measures` returned rows without `measureType` → the frontend LENGTH/WEIGHT split matched nothing → **both unit dropdowns rendered empty**. FIX: grant `measureType` read by mirroring sibling `slug` per-client (in `22c`).
38
+ - **`sales-orders` 500 was NOT our UoM code** — two stacked local-env issues: (a) `preg_split(): Allocation of JIT memory failed` at `Sentry\init()` bootstrap (PCRE JIT can't allocate executable memory on this Win Enterprise machine); the framework escalates the warning to a fatal, 500-ing **every** request → FIX `pcre.jit=0` + restart Apache. Then (b) DB error **1054 Unknown column 'isFulfillable'** — the prod sync brought the updated `_Model_Client_Item` but not the `2026-07-17` migration → FIX add the column locally.
39
+ - **Do NOT re-run `Client/2026-07-22b` after go-live** — the backfill is one-time; a re-run coerces later legitimately-NULL rows to Inch/Pound (column + FK adds are guarded, the backfill is not re-run-safe).
40
+ - `FIELD_LIST` for measureType — rejected; `FIELD_CHAR` is correct (FIELD_LIST is reserved for fields with explicit class constants; signatureType precedent).
41
+
42
+ ## Not tried yet (candidates for next session)
43
+ - **Deploy** TRUE-79191 (deployment order doc is ready) — the actual go-live.
44
+ - Seed **cm/kg** into a client (metric, per-client opt-in) to exercise multi-option dropdown — offered, not done (Growrk currently US-only, so saved value == default value visually).
45
+ - **Full local DB migration catch-up / reset from beta** — drift scan found **16 genuine missing columns** in unrelated tables (TransferOrders, Cases, Questions, TicketSlas, Persona/Language/User RecordFieldSettings, Integrations, Bills, SectionRecordFields) — none in the fulfill/ship path. (5 more "missing" were FIELD_STORAGE false positives: labelPdfFile, imageFile, invoicePdfFile, Files.data, fileData.)
46
+ - Make the **weight symbol on Pending Shipments cards dynamic** (currently static `"lbs."`; TODO in ShipmentsCardTableForm; weightMeasure.symbol already fetched).
47
+ - Register `isFulfillable` RecordField/interceptors locally (skipped — id-2434 collision).
48
+
49
+ ## Current file state
50
+ | File | Status | Notes |
51
+ |------|--------|-------|
52
+ | dbchanges2 Client 2026-07-22a/b/c, Core 2026-07-22a | committed (d52f811/3d18971/ceeb95d), applied to all 11 local clients + Core_2 | UoM schema + seed + backfill + FK + RecordFields + ACL |
53
+ | _underscore Model/Client/Measure.php, TrackingNumber.php | committed (77fae34c) | UoM model fields; survived prod merges |
54
+ | toga2-supply (10 src files) | committed (e91f7029d) | UoM UI; .env/.env.development/package-lock intentionally uncommitted |
55
+ | api2 | no changes | metadata-driven; only untracked local Config/dev-*.ini |
56
+ | C:\WWW\TRUE-79191_deployment_order.md | updated (external) | UoM migrations + notes added |
57
+ | Local: php.ini pcre.jit=0; all client Items.isFulfillable column | applied (local env only) | NOT repo changes; beta/prod unaffected |
58
+
59
+ ## Decisions made
60
+ - **Columns `lengthMeasureId` + `weightMeasureId`** (not dimension/volume); **volume dropped entirely** (Jeff). One length unit governs length/width/height.
61
+ - **Reuse Measures + `measureType` enum, zero new tables**; base units Inch/Pound; `conversionFactorToBase` convention `value_in_base = value × factor`. (Two-table alternative rejected by Jeff.)
62
+ - **Hard FK constraints** (Jeff "+ FKs"; values fully controlled via seed+backfill; matches `UnitTypes.sql` precedent) — rejected logical-only (returnAddressId precedent).
63
+ - **Mirror `shippingMethodId`** for write grants (universal/stable), **mirror `slug`** for measureType read grant — rejected needsReturnLabel (incomplete propagation).
64
+ - **`pcre.jit=0` is a local-only env fix**, not code — beta/prod PHP permits JIT, so nothing to ship.
65
+ - **isFulfillable: add column only locally**, skip RecordField (its migration hardcodes `id=2434`, colliding with returnAddressId `2026-07-10a` also 2434 — flag to that feature's owner).
66
+
67
+ ## Blockers
68
+ None blocking. Ready to deploy. **Before running `Core/2026-07-22a` in production, verify `SELECT MAX(id) FROM Core.RecordFields = 2435`** — the migration hardcodes ids 2436-2438; if the sequence advanced, bump them.
69
+
70
+ ## Exact next step
71
+ > Deploy TRUE-79191 per `C:\WWW\TRUE-79191_deployment_order.md` (order: DB → backend → frontend). First confirm prod `MAX(Core.RecordFields.id) = 2435` (else bump the 2436-2438 ids in `Core/2026-07-22a`), run `composer install` on api2 (FPDF) + apply production carrier config, and separately flag the unrelated `isFulfillable` migration's `id=2434` collision to its owner.
72
+
73
+ ---
74
+ _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.444",
3
+ "version": "1.0.445",
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",