toga-ai 1.0.815 → 1.0.817

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,6 @@
1
+ # store (Store) — 1.0 knowledge
2
+
3
+ | Doc | Summary |
4
+ |-----|---------|
5
+ | [Store (1.0 Storefront / SAML ACS) Architecture](architecture.md) | `store` is a framework **1.0** (`App_`) storefront app. |
6
+ | [Store SAML SP Certificate & Private Keys (togahub.com)](features/saml-sso-certificate-and-keys.md) | How the `store` repo's SAML Service Provider credential — the `togahub.com` X.509 cert and its RSA private key — is stored, loaded and rotated. |
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: Store (1.0 Storefront / SAML ACS) Architecture
3
+ framework: "1.0"
4
+ repo: store
5
+ project: Store
6
+ client: shared
7
+ type: architecture
8
+ status: active
9
+ updated: 2026-09-15
10
+ owners: ["rgirish"]
11
+ files:
12
+ - store/_/app/framework.php
13
+ - store/config.production.ini
14
+ related:
15
+ - ../library/architecture.md
16
+ - ./features/saml-sso-certificate-and-keys.md
17
+ - ../../../clients/prudential/workflows/store-saml-deploy-topology.md
18
+ ---
19
+
20
+ ## Summary
21
+
22
+ `store` is a framework **1.0** (`App_`) storefront app. It also hosts the **SAML Assertion
23
+ Consumer Service (ACS)** for several clients on the `*.togahub.com` hostnames — the inbound
24
+ IdP assertion is decrypted **here**, not in the 2.0 `saml` gateway. SAML lives inline in
25
+ `store/_/app/framework.php` as a large `switch ($returnTo)` on the RelayState value, with one
26
+ inline `$settingsInfo` block per client and a `case 'meta' / case 'acs'` pair inside each.
27
+
28
+ The SP cert/key material lives in the sibling `library` repo under `library/ssl/toga/` and is
29
+ read from disk at request time via `__LIBROOT__`.
30
+
31
+ **Critical rules:** never paste private-key material into `framework.php` — read it from
32
+ `library/ssl/toga/*.key` with `file_get_contents`. The SAML ACS host and the storefront host for
33
+ a client can be **different EB environments**; check DNS before assuming a deploy target
34
+ (see the Prudential store SAML deploy-topology workflow). Certificate and private key must be
35
+ rotated **together** — the three inline SP blocks each carry an `x509cert` that has to match the
36
+ key file.
37
+
38
+ ## SAML entry points
39
+
40
+ Routing is by the RelayState value (`$_POST['RelayState']`, read as `$returnTo`), not by path:
41
+
42
+ | Sub-route | Purpose |
43
+ |---|---|
44
+ | `?meta` | SP metadata XML from `OneLogin_Saml2_Settings::getSPMetadata()` |
45
+ | `?acs` | Assertion Consumer Service — decrypts the inbound `SAMLResponse` |
46
+
47
+ Two SAML stacks are in play in the same file:
48
+ - **php-saml (`OneLogin_Saml2_*`)** — builds the SP settings + `?meta` metadata.
49
+ - **LightSAML + xmlseclibs** — does the actual `?acs` assertion decryption
50
+ (`decryptMultiAssertion`). See the SAML SP certificate & keys feature doc.
51
+
52
+ The RUMCSI branch (`rumcsi.togaview.agilantsolutions.com`) skips decryption entirely and parses
53
+ the raw assertion XML with `SimpleXMLElement` + XPath.
54
+
55
+ ## Handoff to the storefront
56
+
57
+ After a successful assertion, the Prudential-style branches base64-encode a plain
58
+ `xid:first:last:email` string and redirect to `<returnTo>?hash=<base64>`. This is **not
59
+ encrypted and not signed** — unlike the 2.0 gateway's `?saml=` encrypted + uuid-validated
60
+ handoff. See the "Client_True migration (deferred)" section of the SAML SP certificate & keys
61
+ feature doc.
62
+
63
+ ## Known issues
64
+
65
+ - `case 'meta'` sets the `Content-Type` header and `exit`s **without ever echoing
66
+ `$metadata`** — the `?meta` endpoint returns an empty body. Present in both `meta` blocks.
67
+ - `'debug' => true` is set on all three SAML `$settingsInfo` blocks, and a `debugVar()` call
68
+ dumps SAML attributes (XID, name, email) to output.
69
+ - `'strict'` is never set, so php-saml defaults it to `false` and skips condition, timestamp and
70
+ audience validation. Neither `wantAssertionsSigned` nor `wantMessagesSigned` is set.
71
+ - Prudential's SAML config is ~700 lines of inline `switch` in one file with no
72
+ `_Model_<Client>_ClientAuthentication` equivalent.
@@ -0,0 +1,146 @@
1
+ ---
2
+ title: Store SAML SP Certificate & Private Keys (togahub.com)
3
+ framework: "1.0"
4
+ repo: store
5
+ project: Store
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-09-15
10
+ owners: ["rgirish"]
11
+ files:
12
+ - store/_/app/framework.php
13
+ - library/ssl/toga/togahub_com_2027-03-31.crt
14
+ - library/ssl/toga/toga_private_key_2027-03-31.key
15
+ related:
16
+ - ../architecture.md
17
+ - ../../library/features/app-sso-initiation.md
18
+ - ../../../2.0/apps/saml/workflows/rotating-the-saml-sp-certificate.md
19
+ - ../../../2.0/standards/ssl-certificate-trust-model.md
20
+ - ../../../clients/prudential/workflows/store-saml-deploy-topology.md
21
+ ---
22
+
23
+ ## Summary
24
+
25
+ How the `store` repo's SAML Service Provider credential — the `togahub.com` X.509 cert and its
26
+ RSA private key — is stored, loaded and rotated. `store` decrypts inbound IdP assertions itself
27
+ (LightSAML), so this credential is what makes Prudential/RUMCSI SSO work. It is **separate from**
28
+ the 2.0 `saml` gateway's SP credential — rotating one does not rotate the other. (As of
29
+ 2026-09-15 the gateway's `/acs` loads its decrypt certs from AWS Secrets Manager, not from disk;
30
+ `store` still reads files. See the SP cert-rotation workflow.)
31
+
32
+ The cert and key files live in the sibling `library` repo at `library/ssl/toga/` and are read at
33
+ request time through `__LIBROOT__`.
34
+
35
+ ## How it works
36
+
37
+ ### Where the material lives (never paste values)
38
+
39
+ - **AWS Secrets Manager**, account **654654170868**, region **us-east-1**.
40
+ - Secret: **`toga/ssl/togahub_com-casigned-2027-03-31`**.
41
+ - JSON keys: `certificate`, `privateKey`, `certificateChain`, `passphrase`.
42
+ - On disk (deployed with the `library` repo):
43
+ - `library/ssl/toga/togahub_com_2027-03-31.crt` — active cert
44
+ - `library/ssl/toga/togahub_com_2027-03-31-chain.crt` — chain
45
+ - `library/ssl/toga/toga_private_key_2027-03-31.key` — active key, **stored decrypted**, mode `600`
46
+ - `library/ssl/toga/togahub_com_2026-02-27.crt` + `toga_private_key.key` — previous pair,
47
+ kept only as the rotation fallback
48
+
49
+ ### Current certificate (rotated 2026-09-15)
50
+
51
+ | | Old | New |
52
+ |---|---|---|
53
+ | File | `togahub_com_2026-02-27.crt` | `togahub_com_2027-03-31.crt` |
54
+ | Issuer | DigiCert / Thawte | Amazon (ACM, `Amazon RSA 2048 M01`) |
55
+ | Validity | expired **2026-09-16** | **2026-09-15 → 2027-03-31** |
56
+ | SANs | 14 | **2** — `togahub.com`, `*.togahub.com` |
57
+
58
+ **The new cert covers only `togahub.com`.** The six other domains the old cert carried
59
+ (`togacommerce`, `togadesk`, `togaview`, `togasupply`, `togaretail`, `togaiq`, plus their
60
+ wildcards) are **not** on it — each of those needs its own ACM cert on its own listener.
61
+
62
+ ### Dual-keypair decrypt (rotation overlap)
63
+
64
+ `case 'acs'` in `store/_/app/framework.php` builds a `$samlKeyPairs` array — **active keypair
65
+ first, previous keypair second** — then one `LightSaml\Credential\X509Credential` per readable
66
+ pair, and passes the whole list to
67
+ `$reader->decryptMultiAssertion($credentials, $decryptDeserializeContext)`.
68
+
69
+ LightSAML's `decryptMulti()` loops the credentials, swallows each individual failure and returns
70
+ on the first success, re-throwing only when all fail. So **no custom retry logic is needed** and
71
+ it fails closed. An IdP still encrypting to the old cert and one already on the new cert both
72
+ work during the overlap. Unreadable keypairs are skipped with `error_log`; an empty credential
73
+ list throws `RuntimeException`.
74
+
75
+ This mirrors the overlap the 2.0 `saml` gateway provides, though the gateway now does it with
76
+ a multi-cert list out of Secrets Manager rather than two files on disk — see the SP
77
+ cert-rotation workflow.
78
+
79
+ ### Private key loading
80
+
81
+ The three inline `$settingsInfo` SP blocks read the key from disk:
82
+
83
+ ```php
84
+ 'privateKey' => trim(file_get_contents(__LIBROOT__ . '/ssl/toga/toga_private_key_2027-03-31.key')),
85
+ ```
86
+
87
+ The key file must be **decrypted on disk** because `framework.php` calls
88
+ `KeyHelper::createPrivateKey($path, '', true)` with an empty password — an encrypted PEM will not
89
+ load.
90
+
91
+ **Deploy model:** the `ssl/**/*.key` ignore rule was deliberately removed from
92
+ `library/.gitignore` so the key ships with the repo. That is the team's current model for this
93
+ key; it is a record of what we do, not a recommendation.
94
+
95
+ ## Gotchas
96
+
97
+ - **PEM newlines are stored as SPACES in Secrets Manager.** The `certificate` / `privateKey`
98
+ values in `toga/ssl/togahub_com-casigned-2027-03-31` have their line breaks replaced by spaces.
99
+ `openssl` rejects the value straight out of the secret. Re-wrap the base64 body at 64 chars
100
+ before writing it to disk.
101
+ - **The date in a secret name is the EXPIRY date, not the issue date.** The secret
102
+ `toga/ssl/togahub_com-casigned-2026-09-16` is **not** a newer cert — it holds the cert that was
103
+ already expiring. Read the dates out of the cert itself, never off the secret name.
104
+ - **Rotate the cert and the key together.** Before 2026-09-15 the three SP blocks had the raw
105
+ private key pasted inline as a base64 string, and the `x509cert` values had been rotated while
106
+ those inline keys had not — cert and key no longer matched, which would have broken SSO on the
107
+ next deploy. Keys are now read from a file, so they cannot drift apart silently.
108
+ - **Don't assume where the ACS runs.** The host that terminates `?acs` for a client may be a
109
+ different EB environment from the storefront. See the Prudential store SAML deploy-topology
110
+ workflow before deploying a cert change.
111
+
112
+ ## Client_True migration (deferred 2026-09-15)
113
+
114
+ Moving Prudential SAML onto the `Client_True` pattern (config `[saml]` section +
115
+ `Core.ClientAuthentications` / `ClientAuthentications_Sso` rows + `App_Sso` + a
116
+ `_Model_<Client>_ClientAuthentication`) was reviewed and **deferred to its own ticket** — the cert
117
+ expired the next day and the migration is multi-day work.
118
+
119
+ Reference implementation to copy when it is picked up: `tools/config.production.ini` `[saml]`
120
+ section, `library/app/sso.php` (`App_Sso::initiate`), `tools/mvc/sso/initiate/get.php`,
121
+ `tools/mvc/sso/get.php`.
122
+
123
+ What the migration must handle:
124
+ - Prudential today has ~700 lines of inline `switch` keyed on `$returnTo`, with certs pasted into
125
+ PHP, and no `_Model_Prudential_ClientAuthentication`.
126
+ - Its handoff is a plain-base64 `?hash=` of `xid:first:last:email` — unencrypted and forgeable —
127
+ versus the encrypted, uuid-validated `?saml=` handoff `Client_True` uses.
128
+ - Prudential **auto-creates users on first login**. `Client_True`'s `getAuthenticatedSsoUser()`
129
+ does not, so that behavior must be written in or SSO logins will start failing for new users.
130
+
131
+ ## Open security items (not fixed 2026-09-15)
132
+
133
+ - The old private key is still in git history — `library` commits `6addc58f7` and `29d55bb97`,
134
+ plus the `store` `framework.php` history. Tracked as a separate compromised-credential ticket.
135
+ - `'debug' => true`, missing `'strict'`, and the attribute-dumping `debugVar()` — see the store
136
+ architecture doc's Known issues.
137
+
138
+ ## Change history
139
+ - 2026-09-15 — Rotated the `togahub.com` SP cert to the ACM-issued `togahub_com_2027-03-31.crt`
140
+ and added a dual-keypair fallback in `?acs` so both the old and new certs decrypt during the
141
+ overlap (`decryptMultiAssertion`). Removed three hardcoded RSA private-key blobs from
142
+ `store/_/app/framework.php` and replaced them with `file_get_contents` on
143
+ `library/ssl/toga/toga_private_key_2027-03-31.key`; chmod 600 the old world-readable key.
144
+ Recorded the Secrets Manager location, the space-instead-of-newline PEM gotcha, and that the
145
+ new cert carries only 2 SANs. Deferred the `Client_True` SAML migration. Commit `3839e49a`.
146
+ (rgirish)
@@ -6,8 +6,8 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-03
10
- owners: [apeterson]
9
+ updated: 2026-09-15
10
+ owners: [apeterson, tcox]
11
11
  files:
12
12
  - toga25-supply/src/fieldsConfig/index.ts
13
13
  - toga25-supply/src/fieldsConfig/resolveRole.ts
@@ -196,11 +196,29 @@ modal's chrome from the same per-client JSON, not from the component:
196
196
  the Enter-PO modal fields — its nested `valueKey`s are **not** action buttons. `DEFAULT/orderViewFields.json`
197
197
  has no `recordActionFields` at all. Any tooling that walks action buttons must scope strictly to the
198
198
  `actionOptions` array.
199
+ - **⚠ A client JSON file existing does NOT mean it is wired.** Verified 2026-09-15: the per-client
200
+ `vendorItemRecordCreateFields.json` files (COMPASS, COMPASSCANADA, NYCHH) are **not imported into
201
+ `fieldsConfig/index.ts`**, so `FIELDS` resolves `DEFAULT` for every client and edits to those
202
+ files change nothing. Adding the file is step 1 of five (see *Adding a new client-configurable
203
+ field*) — if your override has no effect, check the wiring before you debug the JSON.
204
+ - **A missing `header.modalTag` renders a BLANK badge, not no badge.** The modal chrome is fully
205
+ config-driven, so a client file that omits a block another client has just draws empty chrome.
206
+ Found on the create modal: COMPASSCANADA's and NYCHH's `itemRecordCreateFields.json` lacked the
207
+ `header.modalTag` block COMPASS had. **When you add a header/action key to one client's file, add
208
+ it to every client's file for that modal mode** — there is no per-key fallback to DEFAULT once a
209
+ client bundle exists.
199
210
  - Worked examples: `salesOrdersPageFields` (client × role) and `inventoryGroupings` (client-only +
200
211
  hydration; full write-up in `src/pages/Inventory/README.md`); `itemRecordViewFields` /
201
212
  `vendorItemRecordViewFields` are record-modal-layout examples living under `src/layout/.../viewModel/FIELDS/`.
202
213
 
203
214
  ## Change history
215
+ - 2026-09-15 — Added the `header.modalTag` block to COMPASSCANADA's and NYCHH's
216
+ `itemRecordCreateFields.json` (both rendered a **blank** badge in the create modal because the
217
+ block was missing, not absent-by-design). Recorded two gotchas found while writing the create-modal
218
+ Cypress specs: a client JSON file can exist and be **unwired** (the per-client
219
+ `vendorItemRecordCreateFields.json` files are never imported, so `FIELDS` always uses DEFAULT), and
220
+ a header key added to one client's file must be added to every client's file for that modal mode.
221
+ (tcox)
204
222
  - 2026-09-03 — Documented the actual `resolveRole` flag → role table (COMPASS/COMPASSCANADA:
205
223
  `_isAdmin`/`_isSuperAdmin`/`_isSupervisor`, in that order, else `USER`; QUAD:
206
224
  `_isGlobalAdmin`/`_isBuyer`/`_isItShopper`, else `USER`; every other client `DEFAULT`) and the
@@ -6,8 +6,8 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-08-31
10
- owners: [jcardinal, apeterson]
9
+ updated: 2026-09-15
10
+ owners: [jcardinal, apeterson, tcox]
11
11
  files:
12
12
  - toga25-supply/amplify.yml
13
13
  - toga25-supply/src/api/api.ts
@@ -16,6 +16,7 @@ files:
16
16
  - toga25-supply/package.json
17
17
  - toga25-supply/.env.development
18
18
  related:
19
+ - ./cypress-testing.md
19
20
  - ../architecture.md
20
21
  - ../../toga-blox/workflows/dynamic-publish-pipeline.md
21
22
  - ../features/transfer-orders-page.md
@@ -70,6 +71,23 @@ production.
70
71
  - Node: Vite 7 requires Node 20.19+ or 22.12+ (Node 22 recommended). Amplify's default image
71
72
  is fine; optionally pin via `.nvmrc`.
72
73
 
74
+ ### The Cypress test phase (added 2026-09-15, `_sandbox-client`)
75
+
76
+ `amplify.yml` now runs the full Cypress suite in the build. This is the app's **only** CI — there
77
+ is no GitHub Actions workflow. See [cypress-testing](./cypress-testing.md) for the suite itself.
78
+
79
+ - **`preTest`** — writes the four `*.togasupply` entries into `/etc/hosts` (the tenant is resolved
80
+ from the hostname, so without them every spec visits the wrong client), installs Cypress with
81
+ `CYPRESS_CACHE_FOLDER` set so the binary is cacheable, installs `mochawesome`, and serves the
82
+ built app with **`pm2` + `vite preview` on 5173**. `vite.config.ts` needs
83
+ `preview.allowedHosts: ['.togasupply']` or preview rejects the tenant hostnames.
84
+ - **`test`** — one Cypress run **per tenant** (one superdomain per run) plus the component project.
85
+ - **`postTest`** — `mochawesome-merge` combines the per-tenant reports into one.
86
+ - **`USER_DISABLE_TESTS`** gates the phase per branch, so a branch can deploy without the suite.
87
+ - `.gitignore` excludes `cypress/reports` and `.cypress-cache`.
88
+
89
+ A failing spec fails the deploy — same posture as the `npx tsc -b` step.
90
+
73
91
  ### Why the exact `--mode` matters at runtime
74
92
 
75
93
  `import.meta.env.MODE` (the `--mode` value) is baked into the bundle and read at **runtime**
@@ -219,6 +237,11 @@ branch does NOT reconnect it**: pushes land in GitHub and Amplify ignores them e
219
237
  has ever been developed against a linked local checkout.
220
238
 
221
239
  ## Change history
240
+ - 2026-09-15 — Documented the **Cypress test phase** added to `amplify.yml` on `_sandbox-client`
241
+ (`preTest` hosts entries + cached Cypress install + `pm2`/`vite preview` on 5173, one `test` run
242
+ per tenant plus the component project, `postTest` mochawesome merge, `USER_DISABLE_TESTS` gate),
243
+ the required `preview.allowedHosts: ['.togasupply']` in `vite.config.ts`, and the new `.gitignore`
244
+ entries. This build phase is the app's only CI. (tcox)
222
245
  - 2026-08-31 — Restored the branch's deploy inputs and recorded three systemic findings.
223
246
  (1) **`amplify.yml` was absent from the root of every remote branch** (it lived in a single commit
224
247
  `5a276be`), so Amplify had been building from a **console-stored spec** and the fail-loud
@@ -6,36 +6,30 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-09-14
10
- owners: [apeterson]
9
+ updated: 2026-09-15
10
+ owners: [apeterson, tcox]
11
11
  files:
12
12
  - toga25-supply/cypress.config.ts
13
13
  - toga25-supply/cypress/README.md
14
- - toga25-supply/cypress/tsconfig.json
15
14
  - toga25-supply/cypress/support/e2e.ts
16
- - toga25-supply/cypress/support/commands.ts
17
- - toga25-supply/cypress/support/component.tsx
18
15
  - toga25-supply/cypress/support/tenants.ts
16
+ - toga25-supply/cypress/support/tenantExpectations.ts
19
17
  - toga25-supply/cypress/support/api/stubApi.ts
20
- - toga25-supply/cypress/support/api/envelope.ts
21
- - toga25-supply/cypress/support/api/users.ts
22
- - toga25-supply/cypress/support/api/surfaces.ts
23
- - toga25-supply/cypress/support/api/session.ts
24
18
  - toga25-supply/cypress/e2e/emptyNavigation.cy.ts
25
- - toga25-supply/cypress/component/harness.cy.tsx
26
- - toga25-supply/cypress/fixtures/tenants/COMPASS/surfaces/navigation.json
27
- - toga25-supply/cypress/fixtures/tenants/COMPASSCANADA/surfaces/navigation.json
28
- - toga25-supply/cypress/fixtures/tenants/NYCHH/surfaces/navigation.json
29
- - toga25-supply/cypress/fixtures/tenants/QUAD/surfaces/navigation.json
19
+ - toga25-supply/cypress/e2e/itemModalCreate.cy.ts
20
+ - toga25-supply/cypress/e2e/vendorItemModalCreate.cy.ts
21
+ - toga25-supply/cypress/e2e/vendorItemModalEdit.cy.ts
22
+ - toga25-supply/cypress/fixtures/tenants/COMPASS/expectedUI/itemCreate.ts
23
+ - toga25-supply/cypress/fixtures/tenants/COMPASS/expectedUI/vendorItemCreate.ts
30
24
  - toga25-supply/scripts/cypress.mjs
31
- - toga25-supply/tsconfig.node.json
32
- - toga25-supply/package.json
33
25
  related:
34
26
  - ../features/surface-frontend.md
35
27
  - ../features/client-configurable-fields.md
28
+ - ../features/record-modals-and-nested-tables.md
36
29
  - ../features/side-navigation-and-default-route.md
37
30
  - ../features/sso-redirect-and-session-gating.md
38
31
  - ../features/force-logout-on-deployment.md
32
+ - ./amplify-deployment.md
39
33
  - ../architecture.md
40
34
  - ../../../2.0/standards/frontend.md
41
35
  ---
@@ -47,8 +41,10 @@ a stub layer that answers **every** `/v2` call so no test touches a backend. It
47
41
  the thing that actually breaks in this app — **tenant × role × feature** — without needing real
48
42
  logins or a seeded database.
49
43
 
50
- Current state (2026-09-03, branch `TRUE-81735`, working tree only): **26 tests green** —
51
- 6 e2e specs × 4 tenants (COMPASS, COMPASSCANADA, NYCHH, QUAD) + 2 component tests.
44
+ Current state (2026-09-15, branch `TRUE-79810`): the base harness (2026-09-03, `TRUE-81735`)
45
+ plus the **record-modal specs** — create + edit for Items and Vendor Items, per tenant. Tenants are
46
+ COMPASS, COMPASSCANADA, NYCHH, QUAD; only COMPASS and COMPASSCANADA have a role that reaches
47
+ `/items` and `/vendor-items`, so NYCHH and QUAD run a single placeholder test per modal spec.
52
48
  `cypress/README.md` is the hands-on version of this doc for a developer working without Claude
53
49
  (run commands, recipes, traps). Read that to *write* a test; read this to understand *why* it is
54
50
  shaped this way.
@@ -121,6 +117,80 @@ Three custom commands: `cy.stubApi()`, `cy.visitAsGuest(path)` (storage cleared
121
117
  stable semantic attribute (`input[name]`, `button[type=submit]`, `a[href]`) rather than adding a
122
118
  testid for its own sake.
123
119
 
120
+ ## Record-modal specs (create + edit)
121
+
122
+ `e2e/itemModalCreate.cy.ts` (16 tests) and `e2e/vendorItemModalCreate.cy.ts` (14 tests) are the
123
+ template for any new create/edit modal spec. They test the Pattern 6 form modal — see
124
+ [record-modals-and-nested-tables](../features/record-modals-and-nested-tables.md).
125
+
126
+ **Shape — expectations live in a per-tenant fixture, never in the spec.**
127
+
128
+ - `cypress/fixtures/tenants/<SLUG>/expectedUI/<modal>.ts` holds everything tenant-specific:
129
+ section and field labels, the option text to pick, and the **names of the records to act on**
130
+ (`vendorNameWithoutIntegrationMethods`, `vendorNameWithIntegrationMethods`, `itemName`).
131
+ - The shape is typed in `cypress/support/tenantExpectations.ts`
132
+ (`ItemCreateExpectations`, `VendorItemCreateExpectations`), so a tenant that adds a field fails
133
+ type-check instead of failing at runtime.
134
+ - **A tenant with no fixture must fail as a failing `it()`, not a module-level `throw`.** A throw at
135
+ import time kills the whole spec file with no readable reason; a failing test names the missing
136
+ fixture.
137
+ - Tenants whose role cannot reach the page get **one placeholder test**, so the run count stays
138
+ honest instead of the spec silently registering zero tests.
139
+
140
+ **What the create specs cover** (both modals): empty-form state, the header label following the
141
+ picked vendor, the exact POST body (relations sent as `{uuid}`), required-field errors **per
142
+ field**, currency/number formats, `POST` 500 → error toast + modal closes, the unsaved-input guard
143
+ dialog, a clean form after discard, and the table refetch.
144
+
145
+ Item-create adds: Etilize ID lookup loading images (`GET /vendor-items` join row →
146
+ `GET /items/{uuid}`), option **objects** in the body, an `itemPrices` row for the Default Purchase
147
+ Method, `personaItems` nested as `{persona:{uuid}}`, **no** `itemAssortments` on the body (one
148
+ `POST /assortment-items` per assortment instead), and the *"Unit/Lease price is required when
149
+ Default Price Type is Each/Lease"* rule — which is only reachable on **create**, because on edit
150
+ blox formats the empty price to `0.00` and the rule never fires.
151
+
152
+ Vendor-item-create adds the PO-integration select, which only unlocks for a vendor that has
153
+ integrations: the spec serves `GET /integrations` **keyed on the vendor uuid in the URL**, because
154
+ the tenant-wide stub returns the same rows for every vendor. Use `stubNoIntegrations()` for the
155
+ "vendor without integrations" cases.
156
+
157
+ **New stubs (`support/api/stubApi.ts`):** a default `POST /vendor-items` and a `POST /items` that
158
+ echoes the body back under `data.items` with a 201. Both `/assortment-items` and
159
+ `/assortment-items/` are matched — the app posts the trailing-slash form from create hooks and the
160
+ bare form from edit hooks (see *App findings* below).
161
+
162
+ **Assertion rules learned here — follow them:**
163
+
164
+ - **Refetch = exactly one more GET than before**, asserted as `before + 1` on `cy.get('@alias.all')`.
165
+ `> 1` passes on a double-fetch bug.
166
+ - **Never assert "no create happened" without first proving the submit ran.** Untouched-field
167
+ errors only appear *after* a submit, so assert an error (or `cy.focused()` landing on the first
168
+ invalid field) and *then* assert no POST. Otherwise the test passes when the button did nothing.
169
+ - **Per-field required errors:** find the smallest ancestor that contains the message and assert it
170
+ contains **exactly one input** — the field's. Counting message text passes when the wrong field
171
+ is the one complaining.
172
+ - **Null-safe header assertions:** the vendor header falls back, so assert `number || "Part Number"`.
173
+
174
+ ## Selector rules for blox inputs
175
+
176
+ The edit spec (`vendorItemModalEdit.cy.ts`) failed **0/5 on both tenants** because it was written
177
+ against selectors that do not exist. These rules are why:
178
+
179
+ - **Only use a `data-testid` that is actually published in blox.** `getByTestId('Active')`,
180
+ `'Vendor Sku'`, `'toggleTrack'`, `'virtualRows'` exist in **no** published blox build — not the
181
+ pinned `1.1.2-production.144`, not the newest `1.1.4-sandbox-client.148`, not the local checkout.
182
+ Cypress just fails to find them. Check the built package before using a testid.
183
+ - **Target a field by `input#<valueKey>`** — the field's config key is its DOM id, and that is
184
+ stable across tenants.
185
+ - **Pick select options by their exact text, never by menu row index.** Option order differs per
186
+ tenant, so an index-based pick tests a different value on COMPASSCANADA than on COMPASS. Put the
187
+ option text in the tenant's `expectedUI` fixture.
188
+ - **Assert the guard dialog as `dialog[open]`.** `cy.get('cancelDialogGuardrail')` is a bogus
189
+ selector — it looks for a `<cancelDialogGuardrail>` element.
190
+ - **Re-picking a record's current value is not a change** and fires no dependent request. The
191
+ Canada edit spec had to switch its "vendor with integrations" case to **COMPASS CANADA**, because
192
+ re-selecting the record's existing vendor (GRAND & TOY) triggered no `/integrations` fetch.
193
+
124
194
  ## Guardrails built into the suite
125
195
 
126
196
  ### The fall-through check (the main one)
@@ -163,11 +233,13 @@ detectable instead of silently self-healing.
163
233
  `cypress/component/harness.cy.tsx` mounts a blox component and asserts one React + one
164
234
  QueryClient. It fails when the component-test Vite config drifts from `vite.config.ts`.
165
235
 
166
- ### Not built: CI
236
+ ### CI — now an Amplify build phase (not GitHub Actions)
167
237
 
168
- There is **no** CI for this yet. Worth knowing before you plan it: a workflow must add the four
169
- `*.togasupply` `/etc/hosts` entries as a step (the tenant is resolved from the hostname) and serve
170
- a build (`vite preview` / `start-server-and-test`).
238
+ The suite runs in the **Amplify build** for `_sandbox-client`: `preTest` writes the four
239
+ `*.togasupply` `/etc/hosts` entries and serves the build, `test` runs one Cypress run per tenant
240
+ plus the component project, `postTest` merges the mochawesome reports, and `USER_DISABLE_TESTS`
241
+ gates it per branch. Full spec and the reasoning live in
242
+ [amplify-deployment](./amplify-deployment.md).
171
243
 
172
244
  ## How to run it
173
245
 
@@ -245,10 +317,51 @@ Every `cy:*` script goes through **`node scripts/cypress.mjs`** — see the firs
245
317
  branch.** Otherwise mounted components get a second copy of React / react-query — "Invalid hook
246
318
  call", or "No QueryClient set" with the provider plainly visible in the stack.
247
319
  `component/harness.cy.tsx` is the canary that fails when it drifts.
320
+ - **⚠ A blox `AdvancedSelect` input collapses to ZERO width once a value is picked** — so clicking
321
+ the `<input>` to reopen the menu fails ("element has no size"). Click the **control**, i.e. the
322
+ input's `.parent()`.
323
+ - **⚠ Ignore the ResizeObserver error PER SPEC, never globally.** Chrome throws
324
+ `ResizeObserver loop completed with undelivered notifications` when the
325
+ `CancelDialogGuardrail` `<dialog>` opens, because the app's `ButtonPair.tsx` writes button widths
326
+ **inside** a `ResizeObserver` callback. Swallow that one message with `cy.on('uncaught:exception')`
327
+ in the spec that opens the dialog. A global handler would hide real app exceptions — the harness
328
+ is meant to fail loud (CTO call).
329
+ - **⚠ Some lists are fetched TWICE when they open** (once as the unfiltered search source, once for
330
+ the filtered options), so a `cy.wait('@alias')` can consume the wrong request and assert against
331
+ the wrong body. Assert on **`cy.get('@alias.all')`** and pick the request you mean.
332
+ - **⚠ After merging a branch off `_feature-cypress`, run `npm install` before anything else.** Those
333
+ branches import Talos packages (`@assistant-ui/react`, `@assistant-ui/react-markdown`,
334
+ `@langchain/langgraph-sdk`, `remark-gfm`); with a stale `node_modules` **every page** dies with
335
+ *"Failed to resolve import"* and every spec fails for a reason that looks nothing like the cause.
336
+ The lockfile already has them, so plain `npm install` fixes it. **Note:** `npm install` replaces a
337
+ locally linked blox with the registry copy — relink afterwards if you were testing a local blox.
248
338
  - **Scrub captured payloads.** Copying a real body out of Chrome DevTools (Network → filter
249
339
  `/v2/` → Response → Copy response) is the fastest way to build a fixture — but those payloads
250
340
  carry **real customer data**. Remove emails, names, addresses and tokens before committing.
251
341
 
342
+ ## App findings surfaced by the suite (open — not fixed)
343
+
344
+ Writing the create specs turned up five real app defects. They are recorded here because the specs
345
+ are what proves them; fix them in the app, then tighten the spec.
346
+
347
+ - **`useItemCreateForm`'s success toast says "New Vendor Item Created" for an item.** Copy bug.
348
+ - **`useVendorItemCreateForm`'s follow-up `POST /assortment-items/` can never fire.** It reads
349
+ `response.data.items.uuid`, but the API returns `data.vendorItems`. It also posts
350
+ `_unitPrice: { enteredPrice }`.
351
+ - **Trailing-slash split:** create hooks post to `"/assortment-items/"`, edit hooks to
352
+ `"/assortment-items"`. The stub matches both; api2's real behaviour is **unverified**.
353
+ - **`ButtonPair.tsx` writes button widths inside a `ResizeObserver`** — the source of the Chrome
354
+ ResizeObserver warning above.
355
+ - **The per-client `vendorItemRecordCreateFields.json` files are not wired** (COMPASS,
356
+ COMPASSCANADA, NYCHH) — `FIELDS` always resolves DEFAULT. See
357
+ [client-configurable-fields](../features/client-configurable-fields.md).
358
+
359
+ ## Why e2e per tenant, and not component tests
360
+
361
+ Decided 2026-09-15 (CTO + reviewer): the record modals are tested as **e2e specs, one run per
362
+ tenant**. There is no component mount harness for these modals, and building one inside a test
363
+ ticket is the bigger risk — the tenant axis is the URL, which only the e2e project models.
364
+
252
365
  ## Rest of the repo's test state (for context)
253
366
 
254
367
  - **Vitest is the other half.** `npm test` (`vitest run`) currently has **one failing test file**:
@@ -265,6 +378,19 @@ Every `cy:*` script goes through **`node scripts/cypress.mjs`** — see the firs
265
378
  `SurfaceOverrides IS_VISIBLE` row in `dbchanges2`; the fixture only simulates the grant.
266
379
 
267
380
  ## Change history
381
+ - 2026-09-15 — Added the **record-modal create specs** on `TRUE-79810`: `itemModalCreate.cy.ts`
382
+ (16 tests) and `vendorItemModalCreate.cy.ts` (14 tests), per-tenant `expectedUI` fixtures typed by
383
+ `support/tenantExpectations.ts`, and `POST /items` / `POST /vendor-items` stubs. **Rewrote the
384
+ failing `vendorItemModalEdit.cy.ts`** (0/5 on both tenants) — it used `data-testid`s that exist in
385
+ no published blox build and picked select options by menu row index; now `input#<valueKey>` +
386
+ exact option text + record names in the fixtures, `dialog[open]` for the guard dialog, and
387
+ `stubNoIntegrations()` for the no-integrations cases. Recorded three new traps (zero-width
388
+ `AdvancedSelect` input → click `.parent()`; the `ButtonPair` ResizeObserver error, ignored
389
+ per-spec only; double-fetched lists → assert on `@alias.all`), the per-field required-error
390
+ assertion pattern, the refetch-as-`before+1` rule, and the stale-`node_modules` /
391
+ `npm install` trap after merging from `_feature-cypress`. Logged five open app defects the specs
392
+ surfaced, and the decision to test these modals e2e-per-tenant rather than build a component
393
+ mount harness. CI section updated: the suite now runs as an Amplify build phase. (tcox)
268
394
  - 2026-09-14 — Re-confirmed the `useTalosSurface.test.ts` / blox `dist/global.css` Vitest load
269
395
  failure on a clean tree and refreshed the pass counts (148/148 on the rest). Still unfixed; it
270
396
  silently costs one whole suite. No code change. (apeterson)
@@ -14,6 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
14
14
  - **walmarttechservices** (Walmart Tech Services) — 1 doc(s) → [1.0/apps/walmarttechservices/INDEX.md](1.0/apps/walmarttechservices/INDEX.md)
15
15
  - **test** (Test) — 16 doc(s) → [1.0/apps/test/INDEX.md](1.0/apps/test/INDEX.md)
16
16
  - **toga** (TOGa) — 3 doc(s) → [1.0/apps/toga/INDEX.md](1.0/apps/toga/INDEX.md)
17
+ - **store** (Store) — 2 doc(s) → [1.0/apps/store/INDEX.md](1.0/apps/store/INDEX.md)
17
18
  - **tools** (Tools) — 18 doc(s) → [1.0/apps/tools/INDEX.md](1.0/apps/tools/INDEX.md)
18
19
 
19
20
  ## 2.0 framework
@@ -16,3 +16,4 @@
16
16
  | [Prudential Financial](profile.md) | 2.0 | Prudential is a TOGA client whose device-fulfillment flow is driven by **Dell** via the Dell API (`Client_Prudential.Apis.id = 2`). |
17
17
  | [Prudential: Dell ASN failed POST backfill replay](workflows/dell-asn-backfill-replay.md) | 2.0 | When Dell ASN POSTs fail in bulk (e.g. |
18
18
  | [Prudential — HTTP/2 disabled on the Commerce ALB (proxy workaround) + open re-enable action](workflows/http2-alb-workaround.md) | 2.0 | Prudential users hit **`ERR_HTTP2_PROTOCOL_ERROR`** in Chrome and Edge (both Chromium) loading `https://prudential.togacommerce.com`, while the site worked fine |
19
+ | [Prudential store/SAML Deploy Topology & the Wedged-Agent Deploy Timeout](workflows/store-saml-deploy-topology.md) | 1.0 | Where Prudential's SAML ACS actually runs versus where its storefront runs — they are **two different Elastic Beanstalk environments**, and the CodePipeline act |
@@ -12,6 +12,7 @@ apps:
12
12
  - toga2-commerce
13
13
  - toga25-supply
14
14
  - saml
15
+ - store
15
16
  project: _Underscore
16
17
  client: prudential
17
18
  type: profile
@@ -33,6 +34,7 @@ related:
33
34
  - features/supply-orders-csv-export.md
34
35
  - features/order-email-pdf-attachments.md
35
36
  - features/itemclasses-device-tier-data.md
37
+ - workflows/store-saml-deploy-topology.md
36
38
  ---
37
39
 
38
40
  ## Summary
@@ -80,6 +82,13 @@ The order-created cron branches on this id; the shipped/delivered crons branch o
80
82
  breaks HTTP/2, causing `ERR_HTTP2_PROTOCOL_ERROR` in Chrome/Edge. HTTP/2 is currently disabled
81
83
  globally on the `true-togacommerce-prod` ALB as a workaround — **open action to re-enable once
82
84
  their IT fixes the proxy.** See workflows/http2-alb-workaround.md.
85
+ - **SAML ACS and the storefront are DIFFERENT EB environments.** `prudential.togahub.com` (where
86
+ the SAML assertion is decrypted, in the 1.0 `store` repo) points at `prudential-store-prod`
87
+ (us-west-2, account 502614707982), deployed by the pipeline action **`PrudentialStore`**.
88
+ `prudential.togacommerce.com` (the storefront) points at `true-togacommerce-prod` and is
89
+ deployed by **`TrueProductionDeployment`** — so the action named "PrudentialStore" does *not*
90
+ deploy the storefront. `rumcsi.togahub.com` shares `prudential-store-prod`. See
91
+ workflows/store-saml-deploy-topology.md.
83
92
  - Dell will not change their payload shape — see the Dell ASN units interceptor feature doc for the
84
93
  PRE/POST translation that keeps their feed working after the tracking-number bridge migration.
85
94
  - Service request region is resolved from `Customers.name` string (`'USA'`/`'India'`/`'Ireland'`).
@@ -117,6 +126,10 @@ The order-created cron branches on this id; the shipped/delivered crons branch o
117
126
  linking, the "null null" contact-export incident + dedup/backfill remediation.
118
127
  - features/order-email-pdf-attachments.md — which PDF guide is attached to which customer
119
128
  order email at which stage, the S3 asset list, and the four crons that send them.
129
+ - workflows/store-saml-deploy-topology.md — where Prudential's SAML ACS actually runs vs
130
+ the storefront, the split AWS accounts, and the wedged-agent EB deploy timeout.
131
+ - ../../1.0/apps/store/features/saml-sso-certificate-and-keys.md — the togahub.com SP cert/key
132
+ that decrypts Prudential's assertions.
120
133
  - features/supply-orders-csv-export.md — the Prudential-only Sales Orders CSV export in TOGa 2.5
121
134
  Supply (`toga25-supply`): its two queries, why contacts join `SalesOrders.contactId` (not
122
135
  `Units.contactId`, NULL until Dell assigns the asset), and the `ServiceRequests_Units` fan-out.
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: Prudential store/SAML Deploy Topology & the Wedged-Agent Deploy Timeout
3
+ framework: "1.0"
4
+ project: Store
5
+ client: prudential
6
+ type: workflow
7
+ status: active
8
+ updated: 2026-09-15
9
+ owners: ["rgirish"]
10
+ files:
11
+ - store/_/app/framework.php
12
+ related:
13
+ - ../profile.md
14
+ - ../../../1.0/apps/store/architecture.md
15
+ - ../../../1.0/apps/store/features/saml-sso-certificate-and-keys.md
16
+ ---
17
+
18
+ ## Summary
19
+
20
+ Where Prudential's SAML ACS actually runs versus where its storefront runs — they are **two
21
+ different Elastic Beanstalk environments**, and the CodePipeline action names point at the
22
+ opposite one from what you would guess. Read this before deploying anything SAML-related for
23
+ Prudential. It also records the EB deploy failure mode we hit on 2026-09-15 (a wedged command
24
+ agent that times out after 14 minutes while the environment still shows Green).
25
+
26
+ ## The routing — the pipeline action names are misleading
27
+
28
+ | Hostname | CNAME → EB environment | Deployed by pipeline action |
29
+ |---|---|---|
30
+ | `prudential.togacommerce.com` (storefront) | `true-togacommerce-prod.us-west-2` | **`TrueProductionDeployment`** |
31
+ | `prudential.togahub.com` (**SAML ACS**) | `prudential-store-prod.us-west-2` | **`PrudentialStore`** |
32
+ | `rumcsi.togahub.com` | `prudential-store-prod.us-west-2` | `PrudentialStore` |
33
+
34
+ **The action named `PrudentialStore` does NOT deploy the Prudential storefront.**
35
+ `TrueProductionDeployment` does. `PrudentialStore` deploys the host that decrypts the SAML
36
+ assertion — so **cert / decrypt code changes must land on `prudential-store-prod`.**
37
+
38
+ `rumcsi.togahub.com` shares that same environment, so a bad SAML deploy there breaks RUMCSI
39
+ login too.
40
+
41
+ ### Accounts and regions (they are split)
42
+
43
+ - **Pipeline:** `Store-Production`, AWS account **502614707982**, region **us-west-2**.
44
+ Not `us-east-1`, and not account `654654170868` where most other TOGA pipelines live.
45
+ - **Route53** zones for `togacommerce.com` and `togahub.com` are in account **654654170868**,
46
+ while the EB environments they point at are in **502614707982**. You need both accounts to
47
+ trace a hostname end-to-end.
48
+
49
+ ## Failure mode — 14-minute command timeout with a Green environment
50
+
51
+ Symptoms in the EB event log:
52
+
53
+ ```
54
+ The following instances have not responded in the allowed command timeout time
55
+ Command execution completed on all instances. Summary: [Successful: 0, TimedOut: 1]
56
+ Failed to deploy application.
57
+ ```
58
+
59
+ The environment still reports **Green / Ok**, which is misleading — the deploy never ran, but
60
+ the old version is still serving traffic fine.
61
+
62
+ ### How to confirm it is a wedged command agent
63
+
64
+ 1. Check what the instance actually thinks it is running:
65
+ ```
66
+ aws elasticbeanstalk describe-instances-health --attribute-names All \
67
+ --environment-name prudential-store-prod --region us-west-2
68
+ ```
69
+ A wedged agent shows `Deployment.VersionLabel` still on the **old** build with
70
+ `Deployment.Status: Deployed`, while `CPU Idle` is ~99% and load average ~0.0. That means the
71
+ instance never picked up the deploy command at all — it is not busy, it is deaf.
72
+ 2. Check whether SSM sees the instance:
73
+ ```
74
+ aws ssm describe-instance-information --region us-west-2
75
+ ```
76
+ An **empty** result for the instance confirms the agent is not reporting.
77
+
78
+ Observed 2026-09-15: `prudential-store-prod` on `i-07ae1b81ee48df938` (t3.micro, running since
79
+ 2026-07-17), stuck on an Aug 13 build. `spglobal-store-prod` failed identically on
80
+ `i-04a6a50cffd37457e` — that environment has **no instances and no DNS pointing at it**.
81
+
82
+ ## What you need to do
83
+
84
+ 1. Confirm the diagnosis with the two AWS commands above before touching anything.
85
+ 2. Reboot the EC2 instance (EC2 console or `aws ec2 reboot-instances`).
86
+ 3. Watch the EB event log for `Command is executing on all instances` — that means the EB command
87
+ channel is back.
88
+ 4. Re-run the failed pipeline deploy.
89
+ 5. If it times out again, do **not** reboot a second time. Use an EB **Instance refresh** /
90
+ instance replacement instead.
91
+
92
+ A reboot restored the EB command channel on 2026-09-15, but SSM still did not report after 3
93
+ minutes — EB working again is the signal to retry, not SSM.
94
+
95
+ ## Change history
96
+ - 2026-09-15 — Created while rotating the `togahub.com` SAML cert. Documented that
97
+ `prudential.togahub.com` (SAML ACS) and `prudential.togacommerce.com` (storefront) are separate
98
+ EB environments with swapped-sounding pipeline action names, the split AWS accounts/regions,
99
+ and the wedged-command-agent deploy timeout with its `describe-instances-health` /
100
+ `describe-instance-information` diagnosis and reboot-then-instance-refresh remediation.
101
+ (rgirish)
@@ -5,12 +5,13 @@ apps:
5
5
  - togadesk
6
6
  - togaview
7
7
  - library
8
+ - store
8
9
  project: TOGa Desk
9
10
  client: rumcsi
10
11
  type: profile
11
12
  status: active
12
- updated: 2026-08-21
13
- owners: ["mhammontree"]
13
+ updated: 2026-09-15
14
+ owners: ["mhammontree", "rgirish"]
14
15
  files: []
15
16
  related:
16
17
  - ../../1.0/apps/togadesk/features/per-client-host-restriction.md
@@ -19,6 +20,7 @@ related:
19
20
  - ../../1.0/apps/togadesk/features/email-to-ticket-intake.md
20
21
  - ../../1.0/apps/togadesk/architecture.md
21
22
  - ../../1.0/apps/togaview/architecture.md
23
+ - ../../1.0/apps/store/features/saml-sso-certificate-and-keys.md
22
24
  ---
23
25
 
24
26
  ## Summary
@@ -124,6 +126,12 @@ hardcoded to `clientId = 2`; no RUMC row has `adPeopleId`/`ldap_user`), and
124
126
  - **No SAML auto-provisioning.** Unlike towfoundation and newcenturyholdingsllc, the rumcsi
125
127
  branch only `SELECT`s `people`; it never creates them. That is precisely why the bulk load was
126
128
  necessary.
129
+ - **`rumcsi.togahub.com` runs on the `prudential-store-prod` EB environment** (1.0 `store` repo,
130
+ us-west-2, account 502614707982) — the same host as Prudential's SAML ACS. A bad SAML/cert
131
+ deploy there breaks RUMCSI login too. RUMCSI's ACS branch skips assertion decryption (raw
132
+ `SimpleXMLElement` + XPath), so the SP cert rotation does not affect it directly. See
133
+ ../../1.0/apps/store/features/saml-sso-certificate-and-keys.md and
134
+ ../prudential/workflows/store-saml-deploy-topology.md.
127
135
 
128
136
  ## Change history
129
137
 
@@ -211,6 +211,13 @@
211
211
  "role": "app",
212
212
  "dependsOn": []
213
213
  },
214
+ {
215
+ "repo": "store",
216
+ "project": "Store",
217
+ "framework": "1.0",
218
+ "role": "app",
219
+ "dependsOn": []
220
+ },
214
221
  {
215
222
  "repo": "toga-blox",
216
223
  "project": "TOGa Blox",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.815",
3
+ "version": "1.0.817",
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",