toga-ai 1.0.660 → 1.0.662

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.
@@ -190,6 +190,40 @@ See the [tracking-number bridge doc](../../../2.0/apps/_underscore/features/trac
190
190
  0 of their fulfillments resolve an upstream sales order). `/units` writes and all three
191
191
  tracking-bridge models have **no** interceptor, and **DELETE fires none**.
192
192
 
193
+ ### Single-tracking-number fan-out to items (318) and units (319) — corrected 2026-08-26
194
+
195
+ The earlier "maintains all three bridges" claim was **only true for 317 (header)**: in prod
196
+ `Client_Quad` had **0** rows in 318 and only partial 319. A NetSuite item fulfillment carries
197
+ **exactly one** tracking number, and item (318) + unit (319) tracking are meant to **mirror** that
198
+ single header number. Two bugs sat in the `if ($cntTrackingNumbers == 1)` branch of
199
+ `syncItemFulfillmentFromNetsuite`:
200
+
201
+ - **318 (item) was NEVER written** — the item-level bridge went unpopulated, so any fulfillment
202
+ TableView reading 318 showed a blank Tracking #/Carrier column (the Quad live bug).
203
+ - **319 (unit) received only the LAST item's units.**
204
+ `$lookupItemFulfillmentItemUnitsByNetsuite…AssignementId` (and the sibling existing-unit-tracking
205
+ lookup) was **rebuilt/reset per item inside the item loop** but **consumed once after the loop** —
206
+ so only the final processed item's units got tracking; serialized units on earlier items were
207
+ silently skipped (e.g. `Client_Quad` IF 1688 / SO 287406, whose one unit sat on the first item and
208
+ got none). Same lesson as the sparse-client `TypeError` above: a per-item structure consumed
209
+ outside its loop must not be reset inside it.
210
+
211
+ **Fix (2026-08-26, `library` only, `php -l` clean):** after confirming exactly one tracking number,
212
+ **re-GET the IF's items+units** with their current 318/319 tracking (each `App_Api_Toga2::send` is
213
+ its own committed api2 request, so the re-fetch sees everything just synced), then **fan the one
214
+ tracking number to every item and every serialized unit** — idempotent (skip if already linked) and
215
+ **pruning any stale tracking whose number ≠ the current NetSuite one**. Behavior change: the old
216
+ code deleted **all** unit tracking whenever NetSuite sent 0 or >1 numbers; the new code only prunes
217
+ non-matching tracking **within the single-number case** (single-number is the norm for NetSuite).
218
+ Leftover: the now-unused
219
+ `$lookupExistingItemFulfillmentItemUnitTrackingNumbersByItemFulfillmentItemUnitUuidAndTrackingNumberUuid`
220
+ lookup (~L4423, 4446-4453) is harmless dead code, left in place to avoid a whitespace-fragile edit
221
+ (minor future cleanup). Quad's historical rows were backfilled separately (single-TN IFs only) — see
222
+ the [Quad profile](../../../clients/quad/profile.md) and the
223
+ [tracking-number bridge doc](../../../2.0/apps/_underscore/features/tracking-number-bridges.md).
224
+ Design rationale for putting this at the source writer rather than a 317 interceptor lives on the
225
+ [recursive item fulfillments doc](../../../2.0/apps/_underscore/features/recursive-item-fulfillments.md).
226
+
193
227
  ### Custom field contract (hardcoded in `common_sync_togasupply.php`)
194
228
 
195
229
  The engine requests these `c_` fields by literal name per route — every synced client must have
@@ -431,6 +465,20 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
431
465
 
432
466
  ## Change history
433
467
 
468
+ - 2026-08-26 — Corrected the IF full-reconcile tracking claim and **fixed two bugs** in
469
+ `syncItemFulfillmentFromNetsuite`'s single-tracking-number branch: **318 (item) was never written**
470
+ (`Client_Quad` had 0 rows → blank Tracking # column) and **319 (unit) received only the last
471
+ processed item's units** (a per-item lookup was rebuilt inside the item loop but consumed once
472
+ after it — `Client_Quad` IF 1688/SO 287406). It now re-GETs the IF's items+units and **fans the one
473
+ NetSuite tracking number to every item + serialized unit**, idempotent + pruning non-matching
474
+ tracking; the old delete-all-on-0-or->1 behavior is narrowed to prune-non-matching within the
475
+ single-number case. `library`-only deploy. Quad's historical rows backfilled by two
476
+ `dbchanges2/Client_Quad/2026-08-26a/b` migrations (single-TN IFs only; ~120 multi-TN IFs excluded)
477
+ — see the [Quad profile](../../../clients/quad/profile.md) and
478
+ [tracking-number bridges](../../../2.0/apps/_underscore/features/tracking-number-bridges.md).
479
+ Design rationale (source writer vs. a 317 interceptor) on the
480
+ [recursive item fulfillments doc](../../../2.0/apps/_underscore/features/recursive-item-fulfillments.md).
481
+ (jcardinal)
434
482
  - 2026-08-18 — Recorded the **next poison-order variant** that re-froze Compass USA's SALES_ORDERS
435
483
  section on SO **14106** right after the 2026-08-17 fix let the sync advance past SO 7447. This one
436
484
  is 2.0-side: the api2 `PUT /sales-orders` → `postPut` → `renumberLineNumbers()` **single-UPDATE**
@@ -45,7 +45,7 @@
45
45
  | [Table-View Hyperlink Columns (meta → ACL → computed URL → render)](features/tableview-hyperlink-columns.md) | Any 2.0 table-view column can render its value as a clickable link instead of plain text. | _underscore/Model/Client/TableView.php, _underscore/Model/Client/TrackingNumber.php, api2/Component/Api/V2/V2.php, toga2-supply/src/api/toga.ts, toga2-supply/src/components/ui/Tables/PrimaryTable/helperFunctions/formatTableData.tsx, toga2-supply/src/components/ui/Tables/PrimaryTable/helperFunctions/convertData.tsx, toga2-supply/src/components/ui/Tables/hooks/useDataTableState.tsx, dbchanges2/Client/2026-07-20 - TrackingNumberHyperlinkAndFieldPermission.sql |
46
46
  | [TableView joins (TableViewJoins → SQL) — aliasing, chained multi-hop joins, ACL](features/tableview-joins.md) | `Client_*.TableViewJoins` rows are what let a table view show a column from a table other than its base record. | _underscore/Model/Client/TableView.php, dbchanges2/Client/2026-08-11a - ServiceRequestsInventoryUnitsTableViews.sql, dbchanges2/Client_Elite/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql |
47
47
  | [TogaIQ Gateway Client (_Component_Api_Togaiq) — AI generate/translate from 2.0](features/togaiq-gateway-client.md) | `_Component_Api_Togaiq` is the 2.0 framework's client for the **TogaIQ** (Talos) AI gateway. | _underscore/Component/Api/Togaiq/Togaiq.php, _underscore/ApiRequest.php |
48
- | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19b - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Growrk/2026-07-13a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Elite/2026-08-17 - FulfillmentTableViews.sql |
48
+ | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19b - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Growrk/2026-07-13a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Elite/2026-08-17 - FulfillmentTableViews.sql, dbchanges2/Client_Quad/2026-08-26a - BackfillQuadUnitLevelTrackingSingleTn.sql, dbchanges2/Client_Quad/2026-08-26b - BackfillQuadItemLevelTrackingSingleTn.sql |
49
49
  | [Units for Items for Purchase Orders — Data Structure](features/units-for-items-for-purchase-orders.md) | Describes how unit (serialized inventory) data is linked to sales-order and purchase-order line items behind the `units-for-items-for-purchase-orders` TableView | _underscore/Model/Client/ServiceRequest.php, dbchanges2/Client/2026-08-11a - ServiceRequestsInventoryUnitsTableViews.sql |
50
50
  | [USPS DPV Deliverability Verdict (is this address actually insurable/shippable?)](features/usps-dpv-deliverability.md) | **USPS returning HTTP 200 with a populated address is NOT evidence that the address is deliverable.** The authoritative signal is USPS's **DPV (Delivery Point V | _underscore/Component/Library/Carriers/Usps/Usps.php, _underscore/Model/Client/Address.php, _underscore/Model/Rate/Entitlement.php |
51
51
  | [Refreshing a Local Dev Database from Beta (dev-sandbox)](workflows/local-db-refresh-from-beta.md) | How to reset a local 2.0 dev database from the **beta / dev-sandbox** environment: dump each schema (`Core`, `Client_<Id>`, `Logs_<Id>`, …) from the beta host, | api2/Config/, _underscore/Loader.php, _underscore/Model/Client/BundleTranslation.php, api2/Component/Api/V2/V2.php, toga25-supply/sync_compasscanada_schema.sql |
@@ -155,12 +155,31 @@ inheritance. Verified against prod `Client_Compass` chains (≥3 levels deep).
155
155
  an IF while the item walk is empty/broken would **DELETE the adopted IF's legitimate line items**.
156
156
  Requires a CTO design review before implementing adoption semantics. The 2026-07-02 broken-bridge
157
157
  guard prevents the duplicate cascade for this failure mode without needing adoption.
158
+ - **Source-side IF tracking population belongs in the NetSuite sync writer, not a 317 interceptor
159
+ (design decision, CTO-reviewed 2026-08-26).** For NetSuite-integrated clients the fix that
160
+ populates item/unit tracking (318/319) from a fulfillment's single header tracking number lives in
161
+ the **source writer** `App_Api_Toga2::syncItemFulfillmentFromNetsuite` (see the
162
+ [per-client sync doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md)) —
163
+ **not** an `_underscore` payload interceptor on `ItemFulfillments_TrackingNumbers` (317). An
164
+ interceptor fires **per row** as tracking is attached, so it cannot know at insertion time whether
165
+ the shipment is single- or multi-package, and it would **fight this engine**:
166
+ `reconcileUpstreamUnits`/`reconcileUpstreamPackages` own upstream 319 with delete-stale logic. The
167
+ source writer knows NetSuite's one tracking number and the full item/unit set up front, and this
168
+ recursive engine then mirrors it upstream. A 317-interceptor approach was prototyped and
169
+ **abandoned**.
158
170
  - **Out of scope:** NetSuite sync of upstream IFs; transfer-order fulfillments
159
171
  (`transferOrderId`) are skipped.
160
172
  - **Performance:** every child write re-runs a full upstream walk (read-heavy, but each
161
173
  upstream record is written at most once — idempotent). Fine for normal fulfillment sizes.
162
174
 
163
175
  ## Change history
176
+ - 2026-08-26 — Recorded the **design decision** (CTO-reviewed) that source-side IF tracking
177
+ population for NetSuite clients belongs in `App_Api_Toga2::syncItemFulfillmentFromNetsuite`, not a
178
+ 317 (`ItemFulfillments_TrackingNumbers`) payload interceptor: a per-row interceptor cannot tell
179
+ single- vs multi-package at insert time and would fight this engine's upstream 319 delete-stale
180
+ reconcile. Prototyped interceptor abandoned. Source-side fix + Quad backfill live on the
181
+ [per-client sync doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md).
182
+ (jcardinal)
164
183
  - 2026-08-18 — **Bundle scaling now discriminates on distinct downstream items, not SOI lines
165
184
  (CTO-approved "Option B").** `reconcileUpstreamLevel`'s Recipe query takes one ordered-qty
166
185
  slot per distinct downstream `itemId` and scales only when `distinctDownstreamItemCount > 1`;
@@ -27,6 +27,8 @@ files:
27
27
  - dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql
28
28
  - dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql
29
29
  - dbchanges2/Client_Elite/2026-08-17 - FulfillmentTableViews.sql
30
+ - dbchanges2/Client_Quad/2026-08-26a - BackfillQuadUnitLevelTrackingSingleTn.sql
31
+ - dbchanges2/Client_Quad/2026-08-26b - BackfillQuadItemLevelTrackingSingleTn.sql
30
32
  related:
31
33
  - recursive-item-fulfillments.md
32
34
  - ../../api2/features/cxml-shipnotice-gateway.md
@@ -110,9 +112,11 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
110
112
  OUTER unit/tracking chain and tracking via the item-level bridge **318** (`dbchanges2/Client_Nychh/2026-06-19a`,
111
113
  `dbchanges2/Client_Quad/2026-06-19a`). NYCHH had no custom columns; Quad preserves three
112
114
  Units-level customs (`c_hostIdentifier`/CustomRecordFields 1, `c_pkIdentifier`/2, `macAddress`/rf 1432),
113
- re-attached to the Units join. **Caveat — record 318 is empty in both client DBs today** (see the
114
- portability gotcha below), so the re-root immediately fixes the "0 records" bug but the
115
- Outbound Tracking # / Carrier columns stay blank until item-level tracking is backfilled.
115
+ re-attached to the Units join. **Caveat — record 318 was empty in both client DBs when the views
116
+ were rebuilt** (see the portability gotcha below), so the re-root fixed the "0 records" bug but the
117
+ Outbound Tracking # / Carrier columns stayed blank until item-level tracking was backfilled.
118
+ **Quad's 318 is now populated (2026-08-26 writer fix + backfill — see the resolved gotcha below);
119
+ NYCHH's 318 is still empty.**
116
120
  - **Prudential (Client_Prudential):** Dell posts the legacy `advanceShippingNoticeUnits` key with flat
117
121
  tracking — a PRE/POST interceptor rewrites it (see the prudential client doc).
118
122
  - **NetSuite-integrated clients:** `Trait/Netsuite/ItemFulfillment.php` now reads
@@ -225,20 +229,34 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
225
229
  | CompassCanada | 169 | 138 | 318 |
226
230
  | Elite | **0** | 109 | **319** (fixed 2026-08-17) |
227
231
  | Prudential | **0** | 380 | **319** |
228
- | Quad | **0** | 633 | **319** - but its views read 318 (**live bug**, see below) |
232
+ | Quad | 0 → +5,129¹ | 633 → +779¹ | **318** — writer + backfill **fixed 2026-08-26** |
229
233
  | NYCHH | **0** | unit-level | **319** |
230
234
 
235
+ ¹ Quad's 2026-08-26 backfill (single-tracking-number IFs only) added **5,129** item-level (318)
236
+ rows (was 0) and **779** unit-level (319) rows; ~120 multi-tracking-number IFs were deliberately
237
+ excluded. `App_Api_Toga2::syncItemFulfillmentFromNetsuite` now populates both bridges going
238
+ forward by mirroring each NetSuite IF's single header (317) number down to every item + serialized
239
+ unit — so for NetSuite-integrated clients 318 and 319 are meant to **mirror** 317, not diverge.
240
+
231
241
  Worked example: copying Compass's **318** join into Elite yields 402 rows with **zero** tracking
232
242
  numbers; **319** yields 500 rows with 109 populated. `SELECT COUNT(*)` both bridges in the target
233
243
  client DB **before** choosing - the wrong choice produces a structurally-correct view with a
234
244
  permanently blank Tracking # / Carrier column, which reads as "the data is missing" rather than
235
245
  "the join is wrong."
236
246
 
237
- - **Live bug: Quad's Tracking # column is empty today.** Quad's two fulfillment views are a direct
238
- copy of Compass and read **318**, but Quad has **0** rows in 318 and **633** in 319. Same visible
239
- symptom as the Elite bug, different cause (Elite's was dead ids; Quad's is the wrong bridge
240
- level). **Not fixed** - out of scope when found 2026-08-17. This supersedes the earlier note that
241
- Quad's tracking "lives at 317 (~98%)"; the unit-level bridge is where it is now.
247
+ - **RESOLVED 2026-08-26 (was a live bug): Quad's Tracking # column — fixed by populating 318, not by
248
+ repointing views.** Quad's two fulfillment views read **318**, but Quad had **0** rows in 318 and
249
+ **633** in 319, so the column was blank. The right fix was **not** the interim "repoint to 319"
250
+ idea: Quad's IFs come from **NetSuite** (`syncItemFulfillmentFromNetsuite`), one tracking number
251
+ per IF, and 318/319 are meant to **mirror** the single 317 header number. The source writer was
252
+ fixed to fan that number to every item (318, previously never written) and every serialized unit
253
+ (319, previously only the last item's units), and Quad's historical rows were backfilled for
254
+ single-tracking-number IFs by `Client_Quad/2026-08-26a - BackfillQuadUnitLevelTrackingSingleTn.sql`
255
+ (319, 779 rows) and `Client_Quad/2026-08-26b - BackfillQuadItemLevelTrackingSingleTn.sql` (318,
256
+ 5,129 rows); ~120 multi-tracking-number IFs excluded. See the
257
+ [per-client sync doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md) for
258
+ the writer fix. This supersedes the older "Quad's tracking lives at 317 (~98%)" note. (Elite's bug
259
+ was a different cause — dead ids — and remains a 319 repoint.)
242
260
 
243
261
  - **The dead-id breakage spans TEN clients, not just Elite (measured 2026-08-17).**
244
262
  `item-fulfillments-for-sales-orders` / `item-fulfillments-for-sales-order-items` still carry
@@ -253,6 +271,17 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
253
271
  for record 41.
254
272
 
255
273
  ## Change history
274
+ - 2026-08-26 — **Quad's empty-Tracking-# live bug RESOLVED** — by populating **318**, not repointing
275
+ views to 319. Root: Quad's IFs come from NetSuite (one tracking number per IF) and 318/319 are
276
+ meant to **mirror** the single 317 header number; `App_Api_Toga2::syncItemFulfillmentFromNetsuite`
277
+ was fixed to fan that number to every item (318, previously never written) + serialized unit (319,
278
+ previously only the last item's units — see the
279
+ [per-client sync doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md)),
280
+ and Quad's history was backfilled for single-tracking-number IFs (`Client_Quad/2026-08-26a` 319
281
+ +779 rows, `2026-08-26b` 318 +5,129 rows; ~120 multi-TN IFs excluded). Updated the 318-vs-319
282
+ table and the NYCHH/Quad view caveat (Quad's 318 now populated; NYCHH's still empty). Design
283
+ rationale (source writer vs. 317 interceptor) captured on the recursive-item-fulfillments doc.
284
+ (jcardinal)
256
285
  - 2026-08-20 — Recorded the **`childPolicy` asymmetry across the three ASN tracking bridges** (1437
257
286
  `MATCH_CREATE`, 1441/1445 `MATCH`) and the resulting per-API `Apis_RecordFields` provisioning
258
287
  requirement for any client posting item- or header-level tracking. Compass Canada had zero override
@@ -13,3 +13,4 @@
13
13
  | [Talos AI-Assistant Component (launcher + slide-out chat panel)](features/talos-assistant.md) | `Talos` is a **shared, pure-UI** AI-assistant component in `@agilant/toga-blox`: a header launcher button (`TalosLauncher`) plus a slide-out chat panel (`TalosP | toga-blox/src/components/Talos/TalosLauncher.tsx, toga-blox/src/components/Talos/TalosPanel.tsx, toga-blox/src/components/Talos/TalosMessage.tsx, toga-blox/src/components/Talos/types.ts, toga-blox/src/components/Talos/theme.ts, toga-blox/src/components/Talos/helpers.tsx, toga-blox/src/components/Talos/stub.ts, toga-blox/src/components/Talos/Talos.module.css, toga-blox/src/components/Talos/index.ts, toga-blox/src/components/index.ts |
14
14
  | [Toaster — host-token styling contract](features/toaster.md) | The blox `Toaster` is a CSS-Module component whose every visual property reads a `--toaster-*` custom property supplied by the **host app**. | toga-blox/src/components/Toaster/Toaster.module.css, toga-blox/src/components/Toaster/Toaster.tsx, toga-blox/src/components/Toaster/ToasterContext.tsx, toga2-commerce/src/styles/index.css, toga2-commerce/src/components/Toasters/ToasterList.tsx |
15
15
  | [Dynamic npm Publish Pipeline (branch → channel)](workflows/dynamic-publish-pipeline.md) | How `@agilant/toga-blox` (checkout folder `toga-blox-npm`, registry repo key `toga-blox`) publishes a per-environment npm **channel** (dist-tag) from a `_<mode> | toga-blox/.github/workflows/publish.yml, toga-blox/package.json, toga-blox/src/utils/getFontAwesomeIcon.tsx |
16
+ | [Landing a Large Long-Lived Feature Branch (merge, never rebase)](workflows/landing-a-large-feature-branch.md) | The procedure for landing a long-lived, very large feature branch (months old, hundreds of commits) into `_production` in `toga-blox-npm`. | toga-blox/package.json, toga-blox/.github/workflows/publish.yml |
@@ -14,6 +14,7 @@ files:
14
14
  - toga-blox/src/utils/getFontAwesomeIcon.tsx
15
15
  related:
16
16
  - ../architecture.md
17
+ - ./landing-a-large-feature-branch.md
17
18
  - ../../toga2-commerce/workflows/amplify-build-and-deploy.md
18
19
  ---
19
20
 
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: Landing a Large Long-Lived Feature Branch (merge, never rebase)
3
+ framework: "2.0"
4
+ repo: toga-blox
5
+ project: TOGa Blox
6
+ client: shared
7
+ type: workflow
8
+ status: active
9
+ updated: 2026-08-26
10
+ owners: [apeterson]
11
+ files:
12
+ - toga-blox/package.json
13
+ - toga-blox/.github/workflows/publish.yml
14
+ related:
15
+ - ./dynamic-publish-pipeline.md
16
+ - ../architecture.md
17
+ ---
18
+
19
+ ## What it is
20
+
21
+ The procedure for landing a long-lived, very large feature branch (months old, hundreds of
22
+ commits) into `_production` in `toga-blox-npm`. The decision recorded here is **merge
23
+ `_production` into the feature branch — do not rebase.** Use this doc before starting any
24
+ big-branch integration; the recon step is the transferable part.
25
+
26
+ ## How it works
27
+
28
+ ### 1. Measure the divergence first
29
+
30
+ ```
31
+ git rev-list --left-right --count origin/_production...<branch>
32
+ ```
33
+
34
+ Concrete case (2026-08-26): `feature-new-table` was **281 commits ahead / 8 behind**
35
+ `origin/_production`, forked ~3 months earlier, 530 files changed (+46,795 / −14,209).
36
+
37
+ ### 2. Preview the conflict surface BEFORE merging
38
+
39
+ ```
40
+ git merge-tree $(git merge-base origin/_production <branch>) <branch> origin/_production
41
+ ```
42
+
43
+ This shows the exact conflicting files without touching the working tree, which turns
44
+ "530 changed files" into an actionable list. In the case above it was only **4 files**:
45
+
46
+ | File | Resolution |
47
+ |---|---|
48
+ | `package-lock.json` | **regenerate**, never hand-merge |
49
+ | a component `index.ts` | added independently on both sides — union the exports |
50
+ | a `utils` file | `_production` held two type fixes — keep them |
51
+ | `.github/workflows/publish.yml` | both sides added publish channels — **keep both sets** |
52
+
53
+ ### 3. Merge `_production` into the feature branch, then PR
54
+
55
+ 1. `git merge origin/_production` **on the feature branch**.
56
+ 2. Resolve the (small, known) conflict set **once**.
57
+ 3. Open a normal PR: feature → `_production`.
58
+
59
+ ### Why not rebase
60
+
61
+ Rebasing 281 commits onto `_production` replays every commit, forces the **same conflicts to
62
+ be re-resolved repeatedly**, and **force-rewrites an already-pushed shared branch** — which
63
+ the git-workflow rules forbid on a branch others may have pulled. Merging resolves the
64
+ conflict set exactly once and preserves the pushed history.
65
+
66
+ ## Gotchas
67
+
68
+ - **Confirm the release version before merging.** The `version` in `package.json` drives what
69
+ the publish workflow releases. The feature branch carried `1.0.322` while `_production` was
70
+ at `1.0.314`, so the merge publishes the *branch's* number. See
71
+ [dynamic-publish-pipeline](./dynamic-publish-pipeline.md) for how CI derives the published
72
+ version from that base.
73
+ - **Do not read a behind-count as staleness** in this repo — much of it is release plumbing
74
+ generated on the release branch and never merged back (see the publish-pipeline doc).
75
+
76
+ ## Change history
77
+ - 2026-08-26 — Created. Recorded the merge-not-rebase decision for landing `feature-new-table`
78
+ (281 ahead / 8 behind, 530 files) into `_production`, and the
79
+ `rev-list --left-right --count` → `merge-tree` recon pair that narrowed the conflict surface
80
+ to 4 files before any merge was started. (apeterson)
@@ -31,7 +31,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
31
31
  - **ai-bdr** (AI-BDR) — 13 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
32
32
  - **toga2-commerce** (TOGa Commerce) — 19 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
33
33
  - **toga25-supply** (TOGa 2.5 Supply) — 12 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
34
- - **toga-blox** (TOGa Blox) — 11 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
34
+ - **toga-blox** (TOGa Blox) — 12 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
35
35
  - **bdr** (BDR) — 0 doc(s) → [2.0/apps/bdr/INDEX.md](2.0/apps/bdr/INDEX.md)
36
36
 
37
37
  ## standalone framework
@@ -11,6 +11,7 @@ apps:
11
11
  - worker2
12
12
  - worker
13
13
  - dbchanges2
14
+ - library
14
15
  project: _Underscore
15
16
  client: quad
16
17
  type: profile
@@ -75,14 +76,23 @@ Client-specific DB change-sets live in `dbchanges2/Client_Quad/`.
75
76
  client role 1, id-agnostic + NOT-EXISTS-guarded, beta only). Quad exposes surfaces to role 1 only,
76
77
  vs Compass's 1/3/4 — revisit if broader roles are needed. See
77
78
  [Surface Resolver](../../2.0/apps/_underscore/features/surface-resolver.md).
78
- - **LIVE BUG (found 2026-08-17, not fixed): Quad's Tracking # column is empty today.** The two
79
- fulfillment views are a direct copy of Compass and read the **item-level bridge 318**, but a
80
- fresh count shows `Client_Quad` has **0** rows in 318 and **633** in the **unit-level bridge
81
- 319**. The views are structurally correct and will never show a tracking number until either they
82
- are repointed to 319 or 318 is backfilled. This supersedes the earlier note that Quad's tracking
83
- "lives at 317 (~98% coverage)". Repointing is the same shape as the Elite fix
84
- (`dbchanges2/Client_Elite/2026-08-17 - FulfillmentTableViews.sql`) - **but choose the bridge from
85
- Quad's own row counts, not from Compass**. See
79
+ - **RESOLVED 2026-08-26 (was a LIVE BUG found 2026-08-17): Quad's Tracking # column populated by
80
+ backfilling 318, not by repointing views.** Quad's fulfillment views correctly read the
81
+ **item-level bridge 318**, but `Client_Quad` had **0** rows in 318 (and **633** in unit-level
82
+ 319), so the column was blank. Root context: Quad's item fulfillments come from **NetSuite** via
83
+ the per-client TOGa Supply sync (`App_Api_Toga2::syncItemFulfillmentFromNetsuite`,
84
+ `worker/crons/sync/toga2/sync_togasupply_quad.php`) — **not** TOGa Supply "Fulfill & Ship" — and a
85
+ NetSuite item fulfillment carries **exactly one** tracking number that item (318) and unit (319)
86
+ tracking are meant to **mirror** off the single header (317). Fixed two ways: (1) the source writer
87
+ now fans that one number to every IF item + serialized unit (see the
88
+ [per-client sync doc](../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md)), so
89
+ new IFs populate 318/319 going forward; (2) historical rows backfilled by two idempotent,
90
+ single-tracking-number-only migrations —
91
+ `dbchanges2/Client_Quad/2026-08-26a - BackfillQuadUnitLevelTrackingSingleTn.sql` (319, 779 rows)
92
+ and `dbchanges2/Client_Quad/2026-08-26b - BackfillQuadItemLevelTrackingSingleTn.sql` (318, 5,129
93
+ rows), both run by the dev. **~120 multi-tracking-number IFs were deliberately excluded** (which
94
+ line/unit shipped under which label is not recorded). This supersedes both the earlier note that
95
+ Quad's tracking "lives at 317 (~98%)" and the interim proposal to repoint views to 319. See
86
96
  [Tracking-Number Bridge Migration](../../2.0/apps/_underscore/features/tracking-number-bridges.md).
87
97
  - Quad is one of only **two** clients (with Compass) whose fulfillment views are free of the
88
98
  dangling `joinRecordId` 41 / `parentRecordFieldId` 211+321 config, because both were migrated to
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.660",
3
+ "version": "1.0.662",
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",