toga-ai 1.0.115 → 1.0.117

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.
@@ -4,4 +4,4 @@
4
4
  |-----|---------|-------|
5
5
  | [TOGa Supply (toga2-supply) Architecture](architecture.md) | `toga2-supply` is the **React + Vite frontend** for TOGa Supply — warehouse fulfillment tooling (shipment selection, fulfill & ship against carrier APIs, NetSui | toga2-supply/src/api/toga.ts, toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx |
6
6
  | [Fulfill & Ship](features/fulfill-and-ship.md) | Fulfill & Ship lets a warehouse user select sales-order line items, enter serials, pick a carrier/method, and in one action: create the Item Fulfillment records | toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx, toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts, _underscore/Model/Client/ItemFulfillment.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Component/Library/Carriers/Ups/Ups.php |
7
- | [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-supply` (React + Vite) builds and deploys to **AWS Amplify** for non-prod environments (qc-security, qc-performance, alpha, beta, gamma, …). | toga2-supply/amplify.yml, toga2-supply/package.json, toga2-supply/.env.qc-security |
7
+ | [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-supply` (React + Vite) builds and deploys to **AWS Amplify** for non-prod environments (Stage, QC Security, QC Performance, …). | toga2-supply/amplify.yml, toga2-supply/.github/workflows/sync-stage-environments.yml, toga2-supply/.env.qc-security |
@@ -10,7 +10,7 @@ updated: 2026-06-17
10
10
  owners: ["jcardinal"]
11
11
  files:
12
12
  - toga2-supply/amplify.yml
13
- - toga2-supply/package.json
13
+ - toga2-supply/.github/workflows/sync-stage-environments.yml
14
14
  - toga2-supply/.env.qc-security
15
15
  related:
16
16
  - ../architecture.md
@@ -19,34 +19,54 @@ related:
19
19
  ## Summary
20
20
 
21
21
  How `toga2-supply` (React + Vite) builds and deploys to **AWS Amplify** for non-prod
22
- environments (qc-security, qc-performance, alpha, beta, gamma, …). Note that the
23
- **alpha/beta/gamma/production** environments historically deploy via **GitHub Actions**
24
- (`.github/workflows/*.yml`) into the `agilantsolutions/toga2-supply-build` repo — Amplify
25
- is the newer path used when standing up additional non-prod environments. The first Amplify
26
- build of this app failed with a JavaScript heap OOM; this doc records the fix and the
27
- scalable per-environment build setup.
22
+ environments (Stage, QC Security, QC Performance, …). The **alpha/beta/gamma/production**
23
+ environments historically deploy via **GitHub Actions** (`.github/workflows/*.yml`) into the
24
+ `agilantsolutions/toga2-supply-build` repo — Amplify is the path used for the newer non-prod
25
+ environments. The first Amplify build failed with a JavaScript heap OOM; this doc records the
26
+ fix and the chosen **branch-per-environment** deployment model.
27
+
28
+ ## Deployment model — branch per environment, mirrored from `_stage`
29
+
30
+ The unit of an environment in Amplify is a **branch** (idiomatic Amplify: branch = environment).
31
+ A single branch cannot fan out to multiple environments, and a branch can only be connected once
32
+ per Amplify app. So:
33
+
34
+ - **`_stage` is the integration branch** — where work lands and is reviewed. It is **not deployed
35
+ directly**.
36
+ - Each environment is its **own deployment branch** that mirrors `_stage` exactly:
37
+ `stage`, `qc-security`, `qc-performance`, …
38
+ - **Branch name == Vite mode == `.env.<branch>` filename.** `amplify.yml` builds with
39
+ `--mode "$AWS_BRANCH"` (Amplify injects `$AWS_BRANCH`), so branch `qc-security` builds
40
+ `--mode qc-security` and loads `.env.qc-security`. No per-branch env var needed.
41
+ - A GitHub Action (`sync-stage-environments.yml`) **fast-forwards every environment branch to
42
+ `_stage` on each push**, triggering each branch's Amplify build with its own mode.
43
+
44
+ ```
45
+ _stage (integration, not deployed)
46
+ │ push → sync-stage-environments.yml fast-forwards ↓
47
+ ├──▶ stage ──▶ Amplify ──▶ build --mode stage
48
+ ├──▶ qc-security ──▶ Amplify ──▶ build --mode qc-security
49
+ └──▶ qc-performance ──▶ Amplify ──▶ build --mode qc-performance
50
+ ```
28
51
 
29
52
  ## Steps
30
53
 
31
- 1. **`amplify.yml` at the repo root drives the build.** When present, it **overrides the
32
- Amplify console build settings entirely, for every branch of the app** — you cannot split
33
- "memory in the file, build command in the console." Whichever owns the build phase owns all
34
- of it. Our `amplify.yml`:
35
- - Sets `NODE_OPTIONS=--max-old-space-size=4096` in both `preBuild` and `build` (the OOM fix).
36
- - Runs `npx tsc && npx vite build --mode "$VITE_BUILD_MODE"` — the build mode is **not**
37
- hardcoded.
38
- - Publishes `dist/` as the artifact baseDirectory.
39
- 2. **Per environment, set one Amplify env var on that branch:** `VITE_BUILD_MODE` =
40
- `qc-security` | `qc-performance` | `alpha` | `beta` | `gamma`. No new npm script per env.
41
- 3. **Each mode needs a matching `.env.<mode>` file** committed in the repo (Vite loads
42
- `.env.<mode>` for `--mode <mode>`). e.g. `.env.qc-security` provides `VITE_API`. A new
43
- environment requires creating its `.env.<mode>` file.
44
- 4. Commit `amplify.yml` + the env file, push the branch; Amplify rebuilds.
54
+ 1. **`amplify.yml` (repo root) drives the build.** When present it **overrides the Amplify
55
+ console build settings entirely** — you cannot split "memory in the file, build command in the
56
+ console." It sets `NODE_OPTIONS=--max-old-space-size=4096` (the OOM fix) and runs
57
+ `npx tsc && npx vite build --mode "$AWS_BRANCH"`, publishing `dist/`.
58
+ 2. **In the Amplify app, connect one branch per environment** (`stage`, `qc-security`,
59
+ `qc-performance`, …). Each branch is its own environment with its own hosting domain.
60
+ 3. **Each environment needs a committed `.env.<branch>` file** (Vite loads `.env.<mode>`).
61
+ e.g. `.env.qc-security` provides `VITE_API`.
62
+ 4. **Add the branch to the sync matrix.** Edit `sync-stage-environments.yml` → `matrix.branch`.
63
+ 5. Push to `_stage`; the Action mirrors the env branches; Amplify rebuilds each.
45
64
 
46
65
  ## Systems involved
47
66
 
48
- - AWS Amplify (build + hosting), per-branch environment variables.
49
- - Vite 4 build (`vite build --mode <mode>`), TypeScript `tsc` precompile.
67
+ - AWS Amplify (build + hosting) — one branch per environment, branch name selects the mode.
68
+ - GitHub Actions — `sync-stage-environments.yml` mirrors `_stage` → env branches on push.
69
+ - Vite 4 build (`vite build --mode <branch>`), TypeScript `tsc` precompile.
50
70
  - Sentry source-map upload via `@sentry/vite-plugin` (source maps are **on** —
51
71
  `build.sourcemap: true` in `vite.config.ts`).
52
72
 
@@ -59,18 +79,26 @@ scalable per-environment build setup.
59
79
  has ≥4 GB. Amplify **Standard** compute is 4 GB (tight with source maps on). If it OOMs
60
80
  again, switch to the **Large build instance** (Amplify → App settings → Build settings)
61
81
  and raise to `6144`/`7168`. Setting the heap above physical RAM makes it worse, not better.
62
- - **Mode/filename mismatch:** `--mode qc-security` loads `.env.qc-security` exactly — a file
63
- named `.env.qc-security.beta` is silently ignored (build succeeds but ships with no
64
- `VITE_API`). Name the env file to match the mode.
65
- - **Secret hygiene:** `.env.<mode>` files have historically carried a live
66
- `SENTRY_AUTH_TOKEN`. Prefer rotating it and moving it to an Amplify env var rather than
67
- committing it.
82
+ - **"Can't pick `_stage` again" in the UI:** within one Amplify app a branch connects only once.
83
+ This is why environments are separate *branches*, not multiple connections to `_stage`. (An
84
+ alternative — multiple Amplify *apps* all pointed at `_stage` — works but is a non-idiomatic
85
+ workaround: N apps to manage, no single dashboard. Branch-per-env is preferred.)
86
+ - **Env branches are disposable deployment pointers — never commit to them directly.** The sync
87
+ Action force-updates them (`--force-with-lease`) to match `_stage`; any direct commit is lost.
88
+ - **Branch name must match the mode/`.env` filename.** `--mode qc-security` loads
89
+ `.env.qc-security` exactly — a mismatch (e.g. a stray `.env.qc-security.beta`) is silently
90
+ ignored: the build succeeds but ships with no `VITE_API`.
91
+ - **Secret hygiene:** `.env.<mode>` files have historically carried a live `SENTRY_AUTH_TOKEN`.
92
+ Prefer rotating it and moving it to an Amplify env var rather than committing it.
68
93
 
69
94
  ## Change history
70
95
 
96
+ - 2026-06-17 — Switched to branch-per-environment: `amplify.yml` now builds `--mode "$AWS_BRANCH"`
97
+ and added `sync-stage-environments.yml` to mirror `_stage` → env branches. (Superseded the
98
+ earlier `VITE_BUILD_MODE` per-branch-env-var idea — unworkable because multiple environments
99
+ must deploy from the single `_stage` branch.) (jcardinal)
71
100
  - 2026-06-17 — Created on first Amplify setup of a new QC environment: added root `amplify.yml`
72
- with raised Node heap + parameterized `VITE_BUILD_MODE`; renamed `.env.qc-security.beta` →
73
- `.env.qc-security` (jcardinal)
101
+ with raised Node heap (OOM fix); renamed `.env.qc-security.beta` → `.env.qc-security`. (jcardinal)
74
102
 
75
103
  ## Related docs
76
104
 
@@ -80,6 +80,14 @@ None — uniform (platform-wide Forecast2 sync).
80
80
  - **REST shape ≠ SOAP shape.** The cron reads the SOAP-shim shape; this handler reads the REST record
81
81
  (`status->refName`, line `quantityBilled`, `class->refName`, `entity->id`, `salesRep->id`,
82
82
  `shippingCost`). Verified against live orders via `test/@dave/probe_salesorder_rest_shape.php`.
83
+ - **REST OMITS `rate` for a $0 line — treat missing/non-numeric rate as `0`, don't skip the line.**
84
+ A $0-priced line comes through SOAP as `rate = 0` (numeric); the cron computes `revenue = 0`,
85
+ `profit = −qtyOpen × unitCost`, and — since profit ≠ 0 — **inserts** the row (tracking open *cost* at
86
+ zero revenue). REST instead **leaves `rate` out entirely**, so an early `if (!is_numeric($rate)) continue;`
87
+ silently drops the whole class of rows. **Prod has 575 such rows** (`revenue = 0`, profit ≠ 0), so this
88
+ was real data loss. Fix (2026-06-17): `$unitPrice = is_numeric($line->rate ?? null) ? (float)$line->rate : 0.0;`
89
+ — the genuine filter is the both-zero check (`revenue == 0 && profit == 0`), not rate presence. Caught
90
+ live by editing a $0 test SO (7163257 → revenue 0, profit −105).
83
91
  - **Three deliberate departures from the legacy cron (all intentional):**
84
92
  1. **No date-window gate.** The cron flips `isOrderOpen=false` for tranDate outside −365d/+90d
85
93
  (`common_import_…:1753`) to bound its windowed scan. Irrelevant to a single-id webhook — dropped.
@@ -99,6 +107,18 @@ None — uniform (platform-wide Forecast2 sync).
99
107
  before *every* action — keeping it would couple the REST import to SOAP config. The push methods
100
108
  (`Create`/`Update`/`Sync`) self-construct `NetSuiteService`, so push is unaffected and the Forecast
101
109
  DB is registered globally in `_.php`.
110
+ - **Local testing — the webhook path hides errors; run the action directly to see them.** A real NS
111
+ edit reaches the local worker via the legacy `{action,parameters}` envelope, and `_Worker_NetSuite::Webhook`
112
+ dispatches the per-record action through `_Worker::runTask()`, whose **debug-mode self-POST swallows the
113
+ sub-action result** (you get `Successfully Executed: ok` from the *router* even when the import threw).
114
+ To see the truth, POST the sub-action straight to the worker and read the response:
115
+ `curl -X POST http://worker2/ -d '{"action":"Netsuite/SalesOrder/PUT","parameters":{"internalId":<id>}}'`.
116
+ - **Local `Forecast.Items` is a partial/stale copy → the "unknown item" guard throws.** The importer
117
+ (correctly) throws `Forecast.Items has no row for NetSuite item <id>` when a line's item isn't local;
118
+ prod keeps Items current (hourly import) so it won't, but local testing of an arbitrary SO often hits
119
+ this. Pre-seed the missing item locally (copy the row from the prod reader) or test that SO against prod.
120
+ - **Local worker2 writes to the DB its config points at — not prod.** With `[database] hostname = localhost`,
121
+ rows land in **local** `Forecast.OpenOrderItems`; verify there, not on the prod reader.
102
122
 
103
123
  ## Related work
104
124
 
@@ -111,6 +131,11 @@ and are a candidate to extract into a shared `_Component_Forecast_Db` before the
111
131
 
112
132
  ## Change history
113
133
 
134
+ - 2026-06-17 — **Fixed dropped zero-revenue/open-cost lines.** REST omits `rate` for $0 lines, and the
135
+ `if (!is_numeric($rate)) continue;` guard was skipping them — but the cron inserts these (profit =
136
+ −qtyOpen × unitCost; 575 such rows in prod). Now defaults missing/non-numeric rate to 0 so the both-zero
137
+ filter alone decides. Verified e2e from a real NS edit: 7162297 → $3,698, 7163257 → revenue 0/profit −105.
138
+ Added local-testing gotchas (webhook debug-swallow; stale local Items guard; local-DB write target). (dfranks)
114
139
  - 2026-06-17 — Initial open-orders importer merged into `_Worker_Netsuite_SalesOrder` (TRUE-79142):
115
140
  REST-only, no date-window gate, no shipping line, cascade bug fixed. (dfranks)
116
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.115",
3
+ "version": "1.0.117",
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",