toga-ai 1.0.583 → 1.0.585

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.
@@ -6,7 +6,7 @@
6
6
  | [ClickUp Sprint Dashboard (tools /clickup/react)](features/clickup-sprint-dashboard.md) | A native **ClickUp sprint dashboard** in the 1.0 `tools` app at route `/clickup/react`, built for a wall-mounted **TV display** ("TOGa IQ" internal sprint metri | tools/_/app/clickup/sprint.php, tools/v2/sprints/tile/index.php, tools/v2/sprints/current/index.php, tools/v2/sprints/status-breakdown/index.php, tools/v2/sprints/worktype-breakdown/index.php, tools/v2/sprints/points-by-dev/index.php, tools/v2/sprints/burndown/index.php, tools/assets/clickup/sprint-dashboard.html, tools/mvc/clickup/react/get.php, tools/mvc/login/get.php, tools/_/app/nav.php |
7
7
  | [CloudFront Client Setup](features/cloudfront-client-setup.md) | An SSO-gated admin tool at **`/devops/cloudfront-clients`** in the Tools 1.0 app that onboards a client onto **CloudFront + Route 53 across multiple AWS account | tools/_/app/devops/cloudfront.php, tools/mvc/devops/cloudfront-clients/get.php, tools/mvc/devops/cloudfront-clients/post.php, tools/assets/js/cloudfront-clients.js, tools/assets/css/cloudfront-clients.css, tools/_/app/nav.php, tools/_/app/frameworkindex.php, tools/config.production.ini |
8
8
  | [Design Demo Admin](features/design-demo-admin.md) | A self-serve admin UI at **`/design`** in the SSO-protected **Tools** app that lets the design team publish self-contained "Claude Design" HTML exports as **ver | tools/_/app/design/github.php, tools/mvc/design/get.php, tools/mvc/design/post.php, tools/assets/css/design.css, tools/assets/js/design.js, tools/_/app/frameworkindex.php, tools/_/app/nav.php, tools/composer.json |
9
- | [Tools — Developers Folder (UUID & Password Generators)](features/developer-tools.md) | The first two tools shipped in the Tools app, both under the **Developers** folder and gated to personas **Development Team** / **TOGa Technology**. | tools/mvc/developers/uuid/get.php, tools/mvc/developers/password/get.php |
9
+ | [Tools — Developers Folder (UUID & Password Generators)](features/developer-tools.md) | The first two tools shipped in the Tools app, both under the **Developers** folder and gated to personas **Development Team** / **TOGa Technology**. | tools/mvc/developers/uuid/get.php, tools/mvc/developers/password/get.php, tools/mvc/developers/password/post.php |
10
10
  | [/errors Curation Console (Tools → shared Core Logs DB)](features/errors-curation-console.md) | Internal-only triage/curation screen for the 2.0 Issue/Event error-reporting pipeline, built as a 1.0 Tools MVC page reading the **shared Core Logs DB** through | tools/mvc/errors/get.php, tools/mvc/errors/post.php, tools/mvc/errors/issue/get.php, tools/assets/css/style.css, tools/_/app/nav.php, tools/config.production.ini |
11
11
  | [Legacy Email Notifier (tools /email-migration/notify)](features/legacy-email-notifier.md) | An SSO-gated admin tool at **`/email-migration/notify`** (nav group **Email Migration** > **Legacy Notifier**, personas `['TOGa Technology','Development Team']` | tools/mvc/email-migration/notify/get.php, tools/mvc/email-migration/notify/post.php, tools/_/app/nav.php |
12
12
  | [Tools MVC — Routing, CSRF & App_Database Access Patterns](features/mvc-data-access-patterns.md) | The load-bearing 1.0 (`App_`) framework conventions a developer needs when adding a page to the Tools app — URL routing, CSRF, and DB access through `App_Databa | tools/_/app/nav.php, tools/mvc/get.php, library/app/error.php, library/app/database.php |
@@ -6,14 +6,16 @@ project: Tools
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-25
9
+ updated: 2026-08-14
10
10
  owners: [jcardinal]
11
11
  files:
12
12
  - tools/mvc/developers/uuid/get.php
13
13
  - tools/mvc/developers/password/get.php
14
+ - tools/mvc/developers/password/post.php
14
15
  related:
15
16
  - ../architecture.md
16
17
  - ./persona-gated-navigation.md
18
+ - ./mvc-data-access-patterns.md
17
19
  ---
18
20
 
19
21
  ## Summary
@@ -28,10 +30,28 @@ personas **Development Team** / **TOGa Technology**. They are the reference exam
28
30
  - Server generates an RFC 4122 **v4** UUID via `random_bytes(16)` with version/variant bits set.
29
31
  - UI is click-to-copy, then instant **client-side** regenerate using `crypto.getRandomValues`.
30
32
 
31
- ### Generate Password (`mvc/developers/password/get.php`)
32
- - Reproduces the standalone `test/team/generate_password.php` script.
33
- - `App_String::generateSupportCode(8)` produces the password; `App_String::passwordHash()`
34
- produces the bcrypt hash. The page shows **both** the password and the hash, each copyable.
33
+ ### Generate Password (`mvc/developers/password/get.php` + `post.php`)
34
+ - **Random support-code card (default, on page load):** reproduces the standalone
35
+ `test/team/generate_password.php` script. `App_String::generateSupportCode(8)` produces the
36
+ password; `App_String::passwordHash()` produces the bcrypt hash. The page shows **both** the
37
+ password and the hash, each copyable. Unchanged.
38
+ - **Hash-your-own-password card:** the user types a password and gets its bcrypt hash without a
39
+ full page reload. bcrypt cannot run client-side, so the UI `fetch`es a sibling
40
+ `post.php` — the framework routes any non-GET for `/developers/password` to
41
+ `mvc/developers/password/post.php` (folder == route 1:1). `post.php` exposes one JSON action
42
+ `hash`, guarded with the standard tools pair `App_Auth::requireAuth()` then
43
+ `App_Page::validateCrossSiteRequestForgery()`. That CSRF check is **Origin/Referer-based, not
44
+ a token**, so a same-origin `fetch` passes with no embedded token (see
45
+ [mvc-data-access-patterns](./mvc-data-access-patterns.md)).
46
+ - **Security discipline:** the clear-text password is **never logged or persisted**. A
47
+ `MAX_PASSWORD_BYTES = 4096` guard bounds input, and the endpoint returns a `truncated` flag
48
+ because bcrypt only consumes the first 72 bytes — the UI shows a note when that is exceeded.
49
+
50
+ ## Gotcha — bcrypt's 72-byte limit
51
+ `password_hash`/`App_String::passwordHash()` silently ignores everything past the first **72
52
+ bytes** of the input. The hash-your-own card surfaces this to the user (`truncated` flag + UI
53
+ note) rather than letting a longer password appear to "work" while extra characters are dead.
35
54
 
36
55
  ## Change history
56
+ - 2026-08-14 — Added a "hash your own password" card to the Generate Password tool: a new sibling `mvc/developers/password/post.php` exposes one JSON `hash` action (requireAuth + Origin/Referer CSRF), so the UI bcrypt-hashes user-typed input via `fetch` without a page reload; clear-text is never logged/persisted, `MAX_PASSWORD_BYTES=4096` guard, and a `truncated` flag surfaces bcrypt's 72-byte limit. Also fixed missing inter-card spacing by adding a `tool--password` wrapper modifier + `.tool--password .card { margin-bottom }` (mirroring `.tool--errors`; note style.scss is dead — edit style.css directly). (jcardinal)
37
57
  - 2026-06-25 — Built the first two Developers tools: RFC 4122 v4 UUID generator and a password+bcrypt-hash generator (ported from test/team/generate_password.php), both gated to Development Team / TOGa Technology personas (jcardinal)
@@ -142,6 +142,36 @@ breaks logic.
142
142
  > LABEL_MESSAGE` value stores a **message id** or a **raw message key** is not yet confirmed. Verify
143
143
  > against `Model/Core/Surface.php` `_loadOverrides`/`_buildBundle` before relying on either form.
144
144
 
145
+ ## Vocabulary emission is ELEMENT-DRIVEN (surfacing a vocabulary with no visible UI)
146
+
147
+ A vocabulary is emitted into the bundle (`bundle.vocabularies[<slug>]`) **only if a resolved
148
+ `SurfaceElement` on that surface references its `vocabularyId`** — the resolver batch-loads
149
+ `Vocabularies`+`VocabularyTerms` for *referenced* ids only (step 5), so an unreferenced vocabulary
150
+ never ships. **Pattern to surface a vocabulary that has no visible widget:** add a **hidden
151
+ (`isVisible=0`) carrier `SurfaceElement`** tagged via `config.role` whose only job is to reference the
152
+ `vocabularyId`. The Admin Notes component uses two such carriers on the `admin-notes` surface
153
+ (`config.role` = `noteBadges` / `notesChrome`) so the `note-badges` and `notes-chrome` vocabularies
154
+ emit even though neither renders a control itself. Seed side:
155
+ [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
156
+
157
+ ## Vocabulary TERMS are Core-global — there is NO per-client term gating
158
+
159
+ `SurfaceOverrides` keys only on **`surfaceElementId`** — never on a `VocabularyTerm`. So a Vocabulary
160
+ and all its terms live in **Core** and are the same for every tenant; a client **cannot** hide or add
161
+ a term. Per-client differentiation of a Core-global vocabulary is therefore achieved two other ways,
162
+ **not** by term gating:
163
+
164
+ 1. **Data presence** — only the tenants that generate a given value ever surface it (e.g. only
165
+ Compass / Compass Canada generate `delegate` notes, so only they show the delegate badge).
166
+ 2. **Per-tenant `ThemeTokens`** — a term references a **theme-token SLUG** (e.g. `color.note.delegate`
167
+ / `bg.note.delegate`) which resolves to hex **per tenant** via `Client.ThemeTokens` → `bundle.theme`.
168
+ A token seeded only in Compass / Compass Canada gives those tenants the color; other tenants simply
169
+ have no such token.
170
+
171
+ This let the FE delete its hardcoded `DELEGATE_NOTE_CLIENT_SLUGS` allowlist entirely (see
172
+ [surface-frontend](../../toga25-supply/features/surface-frontend.md)) — client scope became data +
173
+ theme tokens, not code.
174
+
145
175
  ## Caching & invalidation
146
176
 
147
177
  - Disk cache keyed `surface:meta:{app}:{slug}:{client}:{sortedPersonaIds}:{sortedRoleIds}:{lang}`.
@@ -432,6 +462,15 @@ Core record grants + their logic-group expressions all evaluate `all`/`"1"`. The
432
462
  match Compass, a follow-up migration aligning both `meta` and `meta-group` to roles 1,3,4 is needed.
433
463
 
434
464
  ## Change history
465
+ - 2026-08-14 — Documented two durable resolver mechanics surfaced by the Admin Notes migration:
466
+ (1) **vocabulary emission is element-driven** — a vocabulary emits only if a resolved element
467
+ references its `vocabularyId`, so the pattern to surface a UI-less vocabulary is a **hidden carrier
468
+ element** tagged via `config.role` (Admin Notes uses `noteBadges`/`notesChrome` carriers on the
469
+ `admin-notes` surface); (2) **vocabulary TERMS are Core-global — no per-client term gating**
470
+ (`SurfaceOverrides` keys on `surfaceElementId`, never a term), so per-client differentiation of a
471
+ Core-global vocab is by **data presence** + **per-tenant `ThemeTokens`** (token slug → hex per
472
+ tenant), not by hiding terms. This let the FE delete its hardcoded `DELEGATE_NOTE_CLIENT_SLUGS`
473
+ allowlist. (apeterson)
435
474
  - 2026-08-14 — **Discovered/documented** why a surface meta request returns an empty bundle
436
475
  (`surface:null`, empty elements/vocabularies/messages/theme): `_buildBundle()` short-circuits when the
437
476
  slug-only `Surfaces` load fails, and the lookup filters by slug ONLY (no appId/isVisible/client/role/
@@ -3,7 +3,7 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
5
  | [Database Changes (dbchanges2) Repository Architecture](architecture.md) | `dbchanges2` is the **schema-migration / SQL change-set repository** for the entire 2.0 platform. | Core/, Client/, Client_<Tenant>/, Logs/, Logs_Client/, _modules/ |
6
- | [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-20e - RestoreApproveDenyRowActions.sql, dbchanges2/Client_Compass/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md, dbchanges2/Client_Compass/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Compass/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_CompassCanada/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.sql, dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql, dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql, dbchanges2/Client_Quad/2026-07-21c - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_CompassCanada/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Core/2026-07-23a - PoNumberDetailFieldValueKey.sql, dbchanges2/Core/2026-07-29a - SalesOrderApprovalDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-05b - ApprovalDetailsShippingMethodConcatCharge.sql |
6
+ | [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-20e - RestoreApproveDenyRowActions.sql, dbchanges2/Client_Compass/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md, dbchanges2/Client_Compass/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Compass/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_CompassCanada/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.sql, dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql, dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql, dbchanges2/Client_Quad/2026-07-21c - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_CompassCanada/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Core/2026-07-23a - PoNumberDetailFieldValueKey.sql, dbchanges2/Core/2026-07-29a - SalesOrderApprovalDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-05b - ApprovalDetailsShippingMethodConcatCharge.sql, dbchanges2/Core/2026-08-14a - NoteBadgesVocabularySurface.sql, dbchanges2/Client/2026-08-14a - NoteBadgeThemeTokens.sql, dbchanges2/Client/2026-08-14b - NoteBadgeUserColorFix.sql, dbchanges2/Client_Compass/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Client_CompassCanada/2026-08-14a - NoteBadgeDelegateThemeTokens.sql |
7
7
  | [2.0 New-Client Onboarding (manual process)](workflows/client-onboarding.md) | > **A local browser wizard now automates this.** Steps 2–9 below (create DBs, generate Core/API > inserts, append to `Clients_Db.txt`) — plus the dbchanges2 bla | Client/, Client_<Tenant>/, Core/, Logs_Client/ |
8
8
  | [Auditing a client DB that drifted from its models (partially applied module migration)](workflows/client-schema-drift-audit.md) | A recurring 2.0 failure mode: **one client's database drifts from what the PHP models declare**, usually because a `_modules/<module>/` migration was applied to | dbchanges2/Client_Growrk/2026-05-28.sql, dbchanges2/Client_Growrk/2026-08-10c - GrowrkServiceRequestCustomFieldsCatchUp.sql, dbchanges2/Client_Growrk/2026-08-10d - GrowrkServiceRequestTypeAndDispositionSeeds.sql, dbchanges2/_modules/netsuite/2026-07-10a - UnitInventoryFields.sql, dbchanges2/Client_Growrk/2026-08-10 - GrowrkUnitInventoryFieldsCatchUp.sql, dbchanges2/Client_Growrk/2026-08-10b - GrowrkUnitItemDescriptionAcl.sql, dbchanges2/Client_Growrk/_modules.txt |
9
9
  | [Local vs prod MySQL config parity — why “it passed locally” is not evidence](workflows/local-vs-prod-mysql-config-parity.md) | Several migration failures that look like "prod-only bugs" are actually **per-machine MySQL server-configuration differences**. | |
@@ -68,6 +68,11 @@ files:
68
68
  - dbchanges2/Core/2026-07-23a - PoNumberDetailFieldValueKey.sql
69
69
  - dbchanges2/Core/2026-07-29a - SalesOrderApprovalDetailsSurfaceSeed.sql
70
70
  - dbchanges2/Core/2026-08-05b - ApprovalDetailsShippingMethodConcatCharge.sql
71
+ - dbchanges2/Core/2026-08-14a - NoteBadgesVocabularySurface.sql
72
+ - dbchanges2/Client/2026-08-14a - NoteBadgeThemeTokens.sql
73
+ - dbchanges2/Client/2026-08-14b - NoteBadgeUserColorFix.sql
74
+ - dbchanges2/Client_Compass/2026-08-14a - NoteBadgeDelegateThemeTokens.sql
75
+ - dbchanges2/Client_CompassCanada/2026-08-14a - NoteBadgeDelegateThemeTokens.sql
71
76
  related:
72
77
  - ../../_underscore/features/surface-resolver.md
73
78
  ---
@@ -366,6 +371,45 @@ The `category` enum already includes `SPACING`/`RADIUS`/`TYPOGRAPHY`/`ICON`, so
366
371
  the foundation into `ThemeTokens` rows is the target end-state (see the theming-architecture
367
372
  decision, held for senior review).
368
373
 
374
+ ## Admin Notes component — whole component surface-driven (note-badges + notes-chrome, 2026-08-14)
375
+
376
+ The Sales Order **Admin Notes** component (badges, chrome labels, toggles, colors) was moved out of the
377
+ hardcoded FE `addNotesFields.json` into Surface. Seed:
378
+ `Core/2026-08-14a - NoteBadgesVocabularySurface.sql`. FE adapter side:
379
+ [surface-frontend](../../toga25-supply/features/surface-frontend.md).
380
+
381
+ - **`note-badges` vocabulary** — terms `admin` / `user` / `delegate`, each carrying a **theme-token
382
+ SLUG** pair (`color.note.<term>` / `bg.note.<term>`) resolved to hex per tenant via
383
+ `Client.ThemeTokens` (**not** a literal hex in Core).
384
+ - **`notes-chrome` vocabulary** — 8 terms = chrome label slots (`viewAll` / `addNote` / `placeholder` /
385
+ `cancel` / `publish` / `modalTitle` / `modalClose` / `noteCount`), each pointing at a `Message`.
386
+ - **Two hidden carrier `SurfaceElements`** on the `admin-notes` surface (`config.role` =
387
+ `noteBadges` / `notesChrome`), `isVisible=0`, whose only job is to reference each vocabulary so the
388
+ **element-driven emission** ships it (see the resolver doc — a vocabulary emits only if a resolved
389
+ element references its `vocabularyId`).
390
+ - **Chrome toggles** (`showViewAllButton` / `showBottomNoteCount` / `notesPlacementBottom` /
391
+ `modalCloseIcon`) are merged onto the `admin-notes` `surface.config` via **`JSON_MERGE_PATCH`**.
392
+
393
+ **Per-client scope is data + tokens, NOT term gating.** Vocabulary terms are Core-global (see the
394
+ resolver doc); the delegate badge is scoped to Compass / Compass Canada by (a) only those tenants
395
+ generating `delegate` notes and (b) a per-tenant `delegate` ThemeToken — so the FE's hardcoded
396
+ `DELEGATE_NOTE_CLIENT_SLUGS` allowlist was deleted.
397
+
398
+ ### NoteBadge ThemeTokens (per-tenant hex)
399
+ Badge colors live in each tenant's `Client.ThemeTokens` (`slug → hex`), keyed by the `color.note.*` /
400
+ `bg.note.*` slugs the `note-badges` terms reference:
401
+
402
+ - `Client/2026-08-14a - NoteBadgeThemeTokens.sql` — generic **admin + user** tokens for **all** clients.
403
+ - `Client_Compass/2026-08-14a - NoteBadgeDelegateThemeTokens.sql` +
404
+ `Client_CompassCanada/2026-08-14a - NoteBadgeDelegateThemeTokens.sql` — the **delegate** color, only
405
+ for those two tenants.
406
+ - **User-badge color fix (sapphire, per Figma).** `user` was a placeholder zinc; corrected to sapphire
407
+ (`#194885` text / `#E6EEF7` bg). Because a **seeded ThemeToken value overrides the FE fallback
408
+ palette**, the stale zinc had to be fixed in the DB, not just the FE: `Client/2026-08-14a` was folded
409
+ to sapphire for fresh installs, and `Client/2026-08-14b - NoteBadgeUserColorFix.sql` UPDATEs
410
+ already-run tenant DBs. (The FE keeps a fallback palette matching the seeded values so the component
411
+ renders identically before migrations run.)
412
+
369
413
  ## Migration files (typed columns, not EAV)
370
414
 
371
415
  CREATE order is FK-safe (`Messages → Actions → Vocabularies → VocabularyTerms → Surfaces →
@@ -536,6 +580,18 @@ rule resumes.
536
580
  override is added). **Open follow-up.**
537
581
 
538
582
  ## Change history
583
+ - 2026-08-14 — Moved the whole Sales Order **Admin Notes** component into Surface
584
+ (`Core/2026-08-14a - NoteBadgesVocabularySurface.sql`): a `note-badges` vocabulary (terms
585
+ admin/user/delegate, each carrying `color.note.*`/`bg.note.*` theme-token slugs), a `notes-chrome`
586
+ vocabulary (8 chrome label slots → Messages), two hidden carrier `SurfaceElements` on `admin-notes`
587
+ (`config.role`=noteBadges/notesChrome) that reference each vocab so element-driven emission ships it,
588
+ and chrome toggles merged onto `surface.config` via `JSON_MERGE_PATCH`. Per-tenant badge colors seeded
589
+ in `Client.ThemeTokens`: generic admin+user for all clients (`Client/2026-08-14a`), delegate only for
590
+ Compass + Compass Canada (`Client_Compass`/`Client_CompassCanada/2026-08-14a`). Fixed the user badge to
591
+ sapphire per Figma — folded into `Client/2026-08-14a` for fresh installs plus
592
+ `Client/2026-08-14b - NoteBadgeUserColorFix.sql` to UPDATE already-run tenant DBs (a seeded ThemeToken
593
+ overrides the FE fallback, so the stale zinc had to be corrected in the DB). Per-client scope is data
594
+ presence + per-tenant tokens, not term gating (terms are Core-global). (apeterson)
539
595
  - 2026-08-14 — Recorded a durable idempotency-guard gotcha (below): a `SurfaceElements` seed
540
596
  guard keyed on `config.valueKey` is defeated by any later migration that mutates that same
541
597
  valueKey, causing duplicate element inserts on re-run. Concrete case: `Core/2026-07-29a`
@@ -3,6 +3,7 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
5
5
  | [TOGa Blox (toga-blox) Architecture](architecture.md) | `toga-blox` (npm package **`@agilant/toga-blox`**) is the **shared React + TypeScript component / template / utility library** reused across the 2.0 frontends — | toga-blox/package.json, toga-blox/src/components/index.ts, toga-blox/src/api/index.ts, toga-blox/src/templates/index.ts |
6
+ | [AddNotes template — badge style/label decoupling & Badge style passthrough](features/add-notes-template.md) | The `@agilant/toga-blox` `AddNotesTemplate` renders a list of notes, each with a per-note contact **badge** (e.g. | toga-blox-npm/src/templates/AddNotes/types.ts, toga-blox-npm/src/templates/AddNotes/AddNotesTemplate.tsx, toga-blox-npm/src/components/Badge/Badge.types.tsx, toga-blox-npm/src/components/Badge/Badge.tsx |
6
7
  | [AdvancedSelect (virtualized single/multi select)](features/advanced-select.md) | `AdvancedSelect<T>` is a **fully controlled, virtualized** single/multi select built from scratch (not react-select) on **`@tanstack/react-virtual`**, for large | toga-blox/src/components/AdvancedSelect/AdvancedSelect.tsx, toga-blox/src/components/AdvancedSelect/AdvancedSelect.types.ts, toga-blox/src/components/AdvancedSelect/AdvancedSelect.module.css |
7
8
  | [API client (axios wrapper, auth, table-data fetchers)](features/api-client.md) | `src/api/` is a thin **axios** wrapper that standardizes the **2.0 API envelope**, manages auth (Bearer + refresh), serializes complex query options, and provid | toga-blox/src/api/index.ts, toga-blox/src/api/axiosInstance.ts, toga-blox/src/api/apiFunctions.ts, toga-blox/src/api/auth.ts, toga-blox/src/api/genericApi.ts, toga-blox/src/api/types.ts, toga-blox/src/api/tableData |
8
9
  | [BaseInput (react-hook-form field factory)](features/base-input.md) | `BaseInput` is a **form-field factory** driven by react-hook-form. | toga-blox/src/components/BaseInput/BaseInput.tsx, toga-blox/src/components/BaseInput/BaseInput.types.ts, toga-blox/src/components/BaseInput/BaseInput.module.css, toga-blox/src/components/BaseInput/components |
@@ -0,0 +1,68 @@
1
+ ---
2
+ title: AddNotes template — badge style/label decoupling & Badge style passthrough
3
+ framework: "2.0"
4
+ repo: toga-blox
5
+ project: TOGa Blox
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-08-14
10
+ owners: [apeterson]
11
+ files:
12
+ - toga-blox-npm/src/templates/AddNotes/types.ts
13
+ - toga-blox-npm/src/templates/AddNotes/AddNotesTemplate.tsx
14
+ - toga-blox-npm/src/components/Badge/Badge.types.tsx
15
+ - toga-blox-npm/src/components/Badge/Badge.tsx
16
+ related:
17
+ - ../../toga25-supply/features/surface-frontend.md
18
+ ---
19
+
20
+ ## What it is
21
+
22
+ The `@agilant/toga-blox` `AddNotesTemplate` renders a list of notes, each with a per-note contact
23
+ **badge** (e.g. `Admin` / `User` / `Delegate Manager Updated`). This doc covers the durable
24
+ component contract used by the Surface-driven Admin Notes component in toga25-supply (adapter:
25
+ [surface-frontend](../../toga25-supply/features/surface-frontend.md)).
26
+
27
+ ## Badge style/label are DECOUPLED — `badgeVariant` is the style key, `badgeTitle` is the label
28
+
29
+ Originally the template used **`note.contact.badgeTitle` as BOTH** the visible label **and** the
30
+ `badgeStyles[]` lookup key. So relabeling a badge (e.g. `"Delegate"` → `"Delegate Manager Updated"`)
31
+ silently **orphaned its style** — the style map no longer had a key matching the new label, and the
32
+ badge rendered unstyled. This was the root cause of the original reported bug.
33
+
34
+ - **`badgeVariant`** (optional, `types.ts`) is now the **stable style-lookup key**; it **falls back to
35
+ `badgeTitle`** when unset (backward-compatible). `badgeStyles[]` is keyed by `badgeVariant`.
36
+ - **`badgeTitle`** is now **only** the displayed label and can be relabeled freely without breaking
37
+ the style.
38
+
39
+ **Rule:** anything that looks a badge up by identity (style, color, theme token) keys on the stable
40
+ **variant**, never the human label. This mirrors the Surface layer's "slug is internal identity,
41
+ Message is the label" separation.
42
+
43
+ ## Two-badge layout — `contact.badges[]`
44
+
45
+ Delegate notes render **two badges side by side** (`[User][Delegate Manager Updated]`, per Figma). The
46
+ template now accepts a `contact.badges[]` **array** (each entry `{badgeTitle, badgeVariant, ...}`) and
47
+ renders them in order, and remains **back-compatible** with the single `badgeTitle`/`badgeVariant`
48
+ shape. The consuming app (toga25-supply `getAdminNotes.ts`) composes the array from a
49
+ `NOTE_BADGE_COMPOSITION` map (delegate → `[user, delegate]`) and resolves each variant's label + color
50
+ from the Surface bundle.
51
+
52
+ ## `Badge` inline `style` passthrough
53
+
54
+ `Badge` previously accepted only `className` strings. A **runtime-resolved theme hex** cannot be a
55
+ Tailwind arbitrary class (the JIT will not safelist a value only known at runtime), so a new optional
56
+ **`style?: React.CSSProperties`** (`Badge.types.tsx`) is applied on the badge `span` (`Badge.tsx`). This
57
+ lets Surface theme-token hex (from `Client.ThemeTokens`, resolved per tenant) drive the badge color
58
+ directly.
59
+
60
+ ## Change history
61
+ - 2026-08-14 — Decoupled badge **style** from **label** in `AddNotesTemplate`: added optional
62
+ `badgeVariant` as the stable `badgeStyles` lookup key (falls back to `badgeTitle`), fixing the
63
+ root-cause bug where relabeling a badge orphaned its style. Added a `contact.badges[]` array for the
64
+ two-badge delegate layout (back-compat with single `badgeTitle`). Added an optional `style` prop to
65
+ `Badge` so runtime-resolved theme-token hex (which can't be a Tailwind arbitrary class) can color a
66
+ badge. Consumed by the Surface-driven Admin Notes component in toga25-supply. (apeterson)
67
+ </content>
68
+ </invoke>
@@ -10,6 +10,6 @@
10
10
  | [Force Logout on Deployment (useDeploymentGuard)](features/force-logout-on-deployment.md) | On large deployments the backend bumps the Core parameter `META_LAST_REFRESH_DATETIME`. | toga25-supply/src/hooks/useDeploymentGuard.tsx, toga25-supply/src/App.tsx |
11
11
  | [Meta-Driven Page & Table Setup](features/meta-driven-table-data.md) | A page in this app is **meta-driven end to end**: the page view model fetches *page meta* (labels, sections, ACL) and *table meta* (the columns/fields + table s | toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableState.ts, toga25-supply/src/hooks/useTablePageMeta.ts, toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts, toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts, toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts, toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts |
12
12
  | [Record Modals & Nested Tables](features/record-modals-and-nested-tables.md) | The repo's family of modal + nested-table patterns layered over toga-blox `TableRecordModal` and `PrimaryTable*Layout`. | toga25-supply/src/layout/ItemRecordModalLayout/, toga25-supply/src/layout/SalesOrderRecordModalLayout/, toga25-supply/src/layout/SalesOrderItemsTableLayout/, toga25-supply/src/layout/ItemFulfillmentModal/, toga25-supply/src/layout/GenericNestedTables/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/handleFormatApprovalWorkflowPayload.ts, toga25-supply/src/layout/RecordApprovalModal/api/approvalDecisionsApi.ts |
13
- | [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SurfaceRowActions.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
13
+ | [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/AdminNotesSection.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SurfaceRowActions.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
14
14
  | [AWS Amplify Multi-Environment Deployment](workflows/amplify-deployment.md) | How `toga25-supply` deploys to **all** of its environments on AWS Amplify from a **single shared `amplify.yml`**. | toga25-supply/amplify.yml, toga25-supply/src/api/api.ts, toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/vite.config.ts, toga25-supply/package.json |
15
15
  | [Cypress Testing Harness (component + e2e)](workflows/cypress-testing.md) | The Cypress test harness for the `toga25-supply` frontend, bootstrapped from scratch (`cypress` was already a dependency but there was no config, no `cypress/` | toga25-supply/cypress.config.ts, toga25-supply/cypress/tsconfig.json, toga25-supply/cypress/support/component.tsx, toga25-supply/cypress/support/component-index.html, toga25-supply/cypress/support/e2e.ts, toga25-supply/cypress/support/commands.ts, toga25-supply/cypress/support/fixtures.ts, toga25-supply/cypress/support/mocks/useApprovalModalViewModel.ts, toga25-supply/cypress/component/SalesOrderApprovalModalsLayout.cy.tsx, toga25-supply/cypress/component/RecordApprovalModalLayout.cy.tsx, toga25-supply/cypress/component/EnterPoNumberModal.cy.tsx, toga25-supply/cypress/e2e/salesOrderApproval.cy.ts |
@@ -22,6 +22,8 @@ files:
22
22
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx
23
23
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx
24
24
  - toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts
25
+ - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/AdminNotesSection.tsx
26
+ - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.ts
25
27
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts
26
28
  - toga25-supply/src/surface/evaluateSurfaceRule.ts
27
29
  - toga25-supply/src/surface/resolveElementState.ts
@@ -482,6 +484,32 @@ per-element span is therefore semantically impossible across differing column co
482
484
  `orderContentDetailsWorkflow` at one array), so those two grids currently **share one span value**;
483
485
  splitting them requires two arrays.
484
486
 
487
+ ## Admin Notes component — whole component surface-driven (not just badges)
488
+
489
+ The Sales Order **Admin Notes** component (badge labels/colors, chrome labels, layout toggles) was
490
+ moved entirely out of the hardcoded `viewModel/FIELDS/DEFAULT/addNotesFields.json` into Surface — the
491
+ developer asked for the **whole component**, not just badges. The old
492
+ `addNotesFields.json → fieldsConfig → model → view` plumbing was **retired**. Seed side:
493
+ [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md); blox template:
494
+ [add-notes-template](../../toga-blox/features/add-notes-template.md).
495
+
496
+ - **`buildNotesComponent()`** (in `surfaceBundleToTenantFields.ts`) reads
497
+ `bundle.vocabularies['note-badges' | 'notes-chrome']` + the `admin-notes` `surface.config` and builds
498
+ `adminNotes.component = { labels, toggles, badges }`. Badge colors resolve via `bundle.theme` (the
499
+ per-tenant `ThemeTokens` slug→hex map) with an **FE fallback palette** matching the seeded values, so
500
+ the component renders identically **before** the migrations run (decouples deploy order).
501
+ - **`AdminNotesSection.tsx`** consumes `adminNotes.component` with pre-seed fallbacks (the former
502
+ `addNotesFields.json` defaults + seeded hex). It builds each note's `contact.badges[]` from badge
503
+ **variants**, resolving each variant's label + color from the surface.
504
+ - **`getAdminNotes.ts`** composes the two-badge delegate layout via a **`NOTE_BADGE_COMPOSITION`** map
505
+ (delegate → `[user, delegate]`), so a delegate note renders `[User][Delegate Manager Updated]` side by
506
+ side.
507
+ - **`cleanOrder.ts`** — the hardcoded `DELEGATE_NOTE_CLIENT_SLUGS` allowlist was **deleted**: delegate
508
+ scope is now backend data (only Compass / Compass Canada generate delegate notes) + a per-tenant
509
+ delegate ThemeToken, never an FE client list (see
510
+ [surface-resolver](../../_underscore/features/surface-resolver.md) — terms are Core-global, no
511
+ per-term gating).
512
+
485
513
  ## Gotchas
486
514
 
487
515
  - **A named predicate that FAILS OPEN silently enables a button when its data isn't fetched.**
@@ -606,6 +634,18 @@ per-element span is therefore semantically impossible across differing column co
606
634
  treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
607
635
 
608
636
  ## Change history
637
+ - 2026-08-14 — Moved the whole Sales Order **Admin Notes** component into Surface (badge labels/colors,
638
+ chrome labels, layout toggles — not just badges), retiring the hardcoded
639
+ `addNotesFields.json → fieldsConfig → model → view` plumbing. New **`buildNotesComponent()`**
640
+ (`surfaceBundleToTenantFields.ts`) reads `bundle.vocabularies['note-badges'|'notes-chrome']` +
641
+ `admin-notes` `surface.config` into `adminNotes.component = {labels, toggles, badges}`, resolving
642
+ colors via `bundle.theme` with an FE fallback palette so it renders identically before migrations run.
643
+ `AdminNotesSection.tsx` consumes it; `getAdminNotes.ts` composes the two-badge delegate layout via
644
+ `NOTE_BADGE_COMPOSITION` (delegate → `[user, delegate]`). Deleted the hardcoded
645
+ `DELEGATE_NOTE_CLIENT_SLUGS` allowlist from `cleanOrder.ts` — delegate scope is now backend data +
646
+ per-tenant ThemeToken (terms are Core-global, no per-term gating). Depends on the blox
647
+ `AddNotesTemplate` badge-variant/`contact.badges[]`/`Badge.style` changes (see
648
+ [add-notes-template](../../toga-blox/features/add-notes-template.md)). (apeterson)
609
649
  - 2026-08-14 — **DECIDED (CTO-reviewed, AGREE)** where per-field grid `colSpan` lives in the FE Surface
610
650
  layer: span VALUES stay per-consumer/adapter keyed by `valueKey` (the adapter IS the grid scope, since
611
651
  each adapter renders into exactly one grid), NOT centralized as a universal per-element value and NOT
@@ -31,7 +31,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
31
31
  - **ai-bdr** (AI-BDR) — 9 doc(s) → [2.0/apps/ai-bdr/INDEX.md](2.0/apps/ai-bdr/INDEX.md)
32
32
  - **toga2-commerce** (TOGa Commerce) — 15 doc(s) → [2.0/apps/toga2-commerce/INDEX.md](2.0/apps/toga2-commerce/INDEX.md)
33
33
  - **toga25-supply** (TOGa 2.5 Supply) — 11 doc(s) → [2.0/apps/toga25-supply/INDEX.md](2.0/apps/toga25-supply/INDEX.md)
34
- - **toga-blox** (TOGa Blox) — 8 doc(s) → [2.0/apps/toga-blox/INDEX.md](2.0/apps/toga-blox/INDEX.md)
34
+ - **toga-blox** (TOGa Blox) — 9 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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.583",
3
+ "version": "1.0.585",
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",