toga-ai 1.0.229 → 1.0.230

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.
@@ -46,7 +46,11 @@ feature flag — see below). Backend: [surface-resolver](../../_underscore/featu
46
46
  - **`useFetchSurfaceMeta`** — the single fetch-once/React-Query-cached choke point for a surface's
47
47
  meta. Loading a different *record* into the same surface reuses cached meta. Builds the URL as
48
48
  `/surfaces/meta?slug=...` (query string), **not** `/surfaces/<slug>/meta` (the path form makes the
49
- engine parse the slug as a record uuid → 404 EV-6).
49
+ engine parse the slug as a record uuid → 404 EV-6). Unwraps the bundle via **`extractBundle()`**,
50
+ which locates the bundle by its **structural fingerprint** (the `elements` array) across
51
+ `raw` / `raw.data` / `raw.data.<route-key>` — because V2 nests a scripted-API return value one
52
+ level deeper, under a route-keyed slot of `data` (e.g. `data.meta`, `data.surfaces`). On a malformed
53
+ payload it falls back to an empty-elements bundle so the page never crashes.
50
54
  - **`evaluateSurfaceRule`** — the Tier-1 rule evaluator: a **frozen `all/any/none` + `{field,op,value}`
51
55
  grammar** that **throws on an unknown op** (generalized from the SalesOrders
52
56
  `buildPatchedTenantFields`/`evaluateEnableRule`/`resolveFlag` helpers). Evaluated client-side against
@@ -66,13 +70,14 @@ legacy pre-Surface fallback branches at the wired screens removed). Legacy per-s
66
70
 
67
71
  ## What's wired
68
72
 
69
- Login + sales-orders list + the sales-order modal + the **Items LIST** screen. The SalesOrder action
73
+ Login + sales-orders list + the sales-order modal + the **Items**, **VendorItems**, and
74
+ **Inventory** LIST screens. The SalesOrder action
70
75
  bar reproduces `orderViewFields.json` approve/deny/approvalWorkflow/viewLog/editOrder by delegating
71
76
  to the existing `onActionClick`/`onOpenLog` handlers. TABLE-type surfaces stay on the existing
72
77
  TableView pipeline via `tableViewSlug` (see [meta-driven-table-data](meta-driven-table-data.md)) —
73
78
  the Surface layer references the TableView, never replaces it.
74
79
 
75
- ## Per-screen migration recipe (proven on SalesOrders + Items)
80
+ ## Per-screen migration recipe (proven on SalesOrders + Items + VendorItems + Inventory)
76
81
 
77
82
  The screen-by-screen full-app refactor follows one recipe:
78
83
  1. Seed the screen's surface(s) in a `Core/*SurfaceSeed.sql` — a TABLE surface that references the
@@ -87,8 +92,51 @@ Keep TABLE rendering on the existing TableView pipeline; only the chrome (action
87
92
  moves to Surface. A screen with no client/role/lang variance ships a single DEFAULT bundle (no
88
93
  Client overrides), as Items did.
89
94
 
95
+ **VendorItems (screen 2)** followed the recipe exactly: `vendor-items-list` (TABLE → TableView 10,
96
+ recordId 19) + `vendor-items-list-actions` (Refresh / Columns[hidden+disabled] / New Vendor Item),
97
+ page+view-model rewired to resolve title+buttons from the bundle, dropped the
98
+ `useClientFields`/`vendorItemsPageFields` dependency, deleted `vendorItemsPageFields.json` + its
99
+ `fieldsConfig/index.ts` entry.
100
+
101
+ **Inventory (screen 3) — presentation-only, by decision.** Only the page title, header buttons
102
+ (Group by / Refresh), the Group-by modal copy, and the entity icon/color tag map moved to Surface.
103
+ The `inventoryGroupings` `groupings[].levels[]` topology
104
+ (`fetchSlug`/`role`/`additionalDataSlug`/`urlParamKey` driving `GenericNestedTables`) is **data-fetch
105
+ wiring, not presentation**, so per the config/logic boundary it **stays as JSON**. The
106
+ `inventory-list` surface carries `groupByLabels` + `entityTags` in its surface `config`, with
107
+ **recordId+tableViewId NULL** (the page has no single backing Record — it composes
108
+ purchase-orders/items/units via per-level fetchSlugs). Labels + `entityTags` were stripped from both
109
+ DEFAULT and NYCHH `inventoryGroupings.json` (groupings kept) and marked `@deprecated`/optional in the
110
+ `InventoryGroupingsConfig` type.
111
+
112
+ **The config/logic line for migration:** move presentation (titles, button labels, icons, colors,
113
+ modal copy, visibility/enable) to Surface; **leave data-fetch wiring (fetch slugs, roles, URL params,
114
+ nested-table topology) in JSON/code.** Not every screen reduces to a generic `SurfaceSection` swap —
115
+ see SalesOrders below.
116
+
117
+ ## SalesOrders remaining migration — bespoke, NOT a generic SurfaceSection swap (deferred)
118
+
119
+ The SalesOrders detail migration is the plan's explicitly-deferred "blessed escape hatch"/approval
120
+ work — it is bespoke-component + business-logic work, not a generic swap. `SalesOrderSummaryGrid` is
121
+ fully tenant-data-driven through three **bespoke renderers**: `detailSection` (blox `DetailSection` +
122
+ custom `renderField`), `locationCard` (`OrderSectionCard`, structured contact data), and `totalsCard`
123
+ (blox `BaseTotals` **with client-specific recurring-lease business logic**:
124
+ `_totalLease !== 0 → recurringKey` fields). The generic `SurfaceSection` (label/value) cannot
125
+ represent `BaseTotals`/`OrderSectionCard`, and the early-seeded order-details/ship-to/bill-to/
126
+ order-summary SECTION surfaces do **not** cleanly map onto the grid's arbitrary client-keyed section
127
+ model. A faithful migration needs a **registered-renderer approach** keyed off surface element config
128
+ (`detailSection`/`locationCard`/`totalsCard` → their components), with the lease logic staying in
129
+ code/`_underscore`, plus reconciling those seeds and the Compass approval-modal state machine.
130
+ **Deferred to a dedicated design-first session.**
131
+
90
132
  ## Gotchas
91
133
 
134
+ - **Scripted-API GET return values land under `data.<scriptRoute>`, not `data` directly.** V2 nests a
135
+ scripted-API return value inside the envelope under a route-keyed slot of `data` (e.g. `data.meta`,
136
+ `data.surfaces`), one level deeper than a normal data fetch. The bundle hook unwraps this with
137
+ `extractBundle()` by structural fingerprint (the `elements` array). Consumers (`SurfaceSection`,
138
+ `SurfaceActionBar`) are hardened to bail to `null` unless `elements` is a real array, so a malformed
139
+ payload never crashes the page. (Root cause of the "s.elements is not iterable" runtime crash.)
92
140
  - **No feature flag, no fallback.** Surface is the enforced path; a missing/broken surface bundle
93
141
  does not silently fall back to legacy JSON — that path is deleted per screen as it migrates.
94
142
  - **Endpoint is query-string form** — `GET /v2/surfaces/meta?slug=...`, not `/surfaces/<slug>/meta`
@@ -99,6 +147,13 @@ Client overrides), as Items did.
99
147
  treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
100
148
 
101
149
  ## Change history
150
+ - 2026-06-29 — Migrated VendorItems (screen 2, recipe-exact) and Inventory (screen 3,
151
+ **presentation-only by decision** — fetch topology stays JSON; only title/buttons/modal copy/entity
152
+ tags moved). Fixed the "s.elements is not iterable" crash: `extractBundle()` now finds the bundle by
153
+ its `elements`-array fingerprint across `raw`/`raw.data`/`raw.data.<route-key>` (V2 nests
154
+ scripted-API returns under `data.<scriptRoute>`), with an empty-bundle fallback + hardened
155
+ SurfaceSection/SurfaceActionBar guards. Recorded SalesOrders detail as deferred bespoke
156
+ (registered-renderer + lease logic + approval-modal) work. (jcardinal)
102
157
  - 2026-06-29 — DECISION: Surface is now enforced, not flagged — removed `VITE_SURFACE_ENABLED`/
103
158
  `SURFACE_ENABLED` (deleted `featureFlag.ts` + all gating/legacy-fallback branches). Fixed
104
159
  `useFetchSurfaceMeta` to the query-string endpoint `/surfaces/meta?slug=` (path form 404'd EV-6).
@@ -17,7 +17,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
17
17
  ## 2.0 framework
18
18
 
19
19
  - **_underscore** (_Underscore) _(framework core)_ — 17 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
20
- - **worker2** (Worker) — 21 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
20
+ - **worker2** (Worker) — 22 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
21
21
  - **api2** (API) — 7 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
22
22
  - **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
23
23
  - **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.229",
3
+ "version": "1.0.230",
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",