toga-ai 1.0.396 → 1.0.398

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.
@@ -9,6 +9,6 @@
9
9
  | [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx |
10
10
  | [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 |
11
11
  | [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 |
12
- | [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/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 |
12
+ | [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/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 |
13
13
  | [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 |
14
14
  | [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 |
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-20
9
+ updated: 2026-07-21
10
10
  owners: [jcardinal, apeterson]
11
11
  files:
12
12
  - toga25-supply/src/surface/useFetchSurfaceMeta.ts
@@ -47,6 +47,8 @@ files:
47
47
  - toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx
48
48
  - toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx
49
49
  - toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx
50
+ - toga25-supply/src/surface/useStatusColors.ts
51
+ - toga25-supply/src/surface/SurfaceHeader.tsx
50
52
  related:
51
53
  - ../../_underscore/features/surface-resolver.md
52
54
  - meta-driven-table-data.md
@@ -344,6 +346,25 @@ shaped `{ order }`, so the predicate reads **`order.currentStage` / `order.stage
344
346
  `clipboardListCheck`, `penToSquare`) and must be used as-is (`element.tokens?.icon ?? "check"`).
345
347
  Running it through `resolveThemeToken` misses on every icon and silently falls back to `"check"` for
346
348
  every button.
349
+ - **Vocabulary color tokens are unresolvable because the resolved bundle's `theme` map is empty —
350
+ resolve status-badge dot colors from the status endpoint's `colorHex` instead.** A resolved bundle
351
+ ships theme-token SLUGS on each vocabulary term (`tokens.color = "color.status.<slug>"`,
352
+ `tokens.bg = "bg.status.<slug>"`) but the bundle's `theme` map arrives as `{}`, so
353
+ `resolveThemeToken(theme, term.tokens?.color)` returns `undefined` and the status dot renders
354
+ colorless in the sales-order record-modal header. Fix: resolve the dot color from the
355
+ `sales-order-statuses` API's `colorHex` field keyed by slug — a `SurfaceVocabularyTerm.value` **IS**
356
+ the status slug, so it maps 1:1 to `sales-order-statuses[].slug`. The **`useStatusColors(vocabularySlug)`**
357
+ hook (`src/surface/useStatusColors.ts`) fetches `GET /v2/sales-order-statuses?fields=slug,colorHex&sort=sortOrder`
358
+ (session-cached via react-query, `staleTime`/`gcTime` Infinity, same pattern as `useFetchSurfaceMeta`)
359
+ and returns a `(slug) => "#RRGGBB" | undefined` resolver, gated by a
360
+ `VOCABULARY_COLOR_ROUTES = { "sales-order-status": "sales-order-statuses" }` map so it is reusable
361
+ for other STATUS vocabularies (walks the V2 envelope to find the row array, no hard-coded
362
+ `data.salesOrderStatuses`). `SurfaceHeader.tsx`'s BADGE branch was extracted into a
363
+ `SurfaceStatusBadge` sub-component (needed because `useStatusColors` is a hook) whose dot color is
364
+ `resolveStatusColor(term.value) ?? resolveThemeToken(theme, term.tokens?.color)` — `colorHex` wins,
365
+ the theme token remains a fallback if the `theme` map is ever populated. Label resolution and the
366
+ "no matching term → render nothing" behavior are unchanged. The legacy table path
367
+ (`PrimaryTable*Layout` via `statusTypes.json`) already used `colorHex` and was untouched.
347
368
  - **Scripted-API GET return values land under `data.<scriptRoute>`, not `data` directly.** V2 nests a
348
369
  scripted-API return value inside the envelope under a route-keyed slot of `data` (e.g. `data.meta`,
349
370
  `data.surfaces`), one level deeper than a normal data fetch. The bundle hook unwraps this with
@@ -373,6 +394,17 @@ shaped `{ order }`, so the predicate reads **`order.currentStage` / `order.stage
373
394
  treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
374
395
 
375
396
  ## Change history
397
+ - 2026-07-21 — Fixed the surface-driven **status badge dot rendering colorless** in the sales-order
398
+ record-modal header: the resolved bundle ships vocabulary color TOKEN SLUGS
399
+ (`color.status.*`/`bg.status.*`) but its `theme` map is empty (`{}`), so `resolveThemeToken` returns
400
+ `undefined`. New **`useStatusColors(vocabularySlug)`** hook (`src/surface/useStatusColors.ts`)
401
+ resolves the dot color from the `sales-order-statuses` API's `colorHex` keyed by slug (a
402
+ `SurfaceVocabularyTerm.value` IS the status slug → 1:1 to `sales-order-statuses[].slug`);
403
+ session-cached via react-query, gated by a `VOCABULARY_COLOR_ROUTES` map for reuse across STATUS
404
+ vocabularies. `SurfaceHeader.tsx`'s BADGE branch extracted into a `SurfaceStatusBadge` sub-component
405
+ (hook use), dot color = `resolveStatusColor(term.value) ?? resolveThemeToken(...)` (colorHex wins,
406
+ theme token remains a fallback). Legacy table path (via `statusTypes.json`) already used `colorHex`,
407
+ untouched. `tsc --noEmit` clean. (apeterson)
376
408
  - 2026-07-20 — Documented the **two FE rule engines** (frozen surface Tier-1 `evaluateSurfaceRule`,
377
409
  which THROWS on `{type}` nodes, vs legacy `evaluateEnableRule` with `{type}` NAMED_RULES incl
378
410
  `stepTwoAssigned` reading `ctx.currentStage`/`ctx.stages`) and why the corrected Approve rule is
@@ -0,0 +1,73 @@
1
+ ---
2
+ type: session
3
+ slug: bdr-vapi-golive
4
+ title: BDR VAPI go-live day 2 — loop verified live, four funnel features shipped
5
+ author: tcox
6
+ repos: [bdr, ai-bdr, worker2]
7
+ framework: "2.0"
8
+ client: shared
9
+ status: active
10
+ created: 2026-07-21
11
+ updated: 2026-07-21
12
+ ---
13
+
14
+ # Session: bdr-vapi-golive
15
+ **Date:** 2026-07-21 (continues 2026-07-20-bdr-vapi-golive-tcox)
16
+ **Project/Repo:** bdr + worker2 (2.0)
17
+ **Task:** Verify the AI-BDR call loop end-to-end live on the real campaign and finish the BDR funnel for go-live (enrichment, fast dial, anonymous guard, real share).
18
+
19
+ ---
20
+
21
+ ## What WORKED
22
+ - **Full call loop verified live on campaign 2** using Rohan's existing-campaign approach: flip ALL campaign-2 contacts `isOkayToCall=0` (tester included), extend `Campaigns.dateEnd`, then the tester's UI Call Now submit re-arms ONLY them (`isOkayToCall=1` + `dtNextContactRequested`). Call arrived from the healthcare number +1 (331) 244-5958 (campaign 2's `c_fromPhoneNumberIdentifier` maps there); webhook write-back confirmed (dtEnded set, dtNext cleared).
23
+ - **Scheduled-call path + CST conversion verified**: an 11:30 slot dialed at 11:30:50 (attempt 183).
24
+ - **Outcome mapping verified**: "call me back..." conversation wrote `c_callOutcome=CALLBACK_SCHEDULED`, `c_actionToTake=SCHEDULE_CALLBACK`, sentiment, qualification score.
25
+ - **Enrichment shipped and live-proven** (bdr: src/server/hubspot.ts, toga.ts, leadSink.ts, app/page.tsx): HubSpot jobtitle/company/industry → Contacts.title/doingBusinessAs/industry (the three no-fallback dialer fields), city/state/website → callContext keys. Contact 248 got title=INTERN, doingBusinessAs="Agilant Solutions, Inc." on its first funnel pass and the Vapi opener used them. Committed by tcox this morning (with the phone-prefill fallback).
26
+ - **Fast dial built** (src/server/workerDialer.ts + callbackService.ts, env TOGA_WORKER_URL): Call Now triggers the worker's `Ai/Bdr/Vapi/InitiateOutboundCall` with the primary phone-row uuid — seconds instead of the 0-60s cron wait; best-effort with cron fallback; scheduled calls excluded. NOT yet live-tested.
27
+ - **Anonymous-visit guard built** (CallNow.tsx, Schedule.tsx, content schema/default): no lead ref → submit disabled + notice, matching info's "Missing contact reference" (replaces silent fake success).
28
+ - **Share button made real** (src/lib/shareMailto.ts, CallSummary.tsx): Send opens the visitor's mail client via mailto: with recipient/subject/summary body. BDR suite 131 tests green, tsc + eslint clean.
29
+ - **Parity audit (info → BDR)**: everything ported; enrichment was net-new in BOTH repos; visit route intentionally dropped; TOGA_PUBLIC_TOKEN_URL and NEXT_PUBLIC_ENV are dead even in info.
30
+ - **Vapi API diagnostics work** with the bearer const at worker2/Worker/Ai/Bdr/Vapi.php:6 (GET /call/{id} endedReason, GET /assistant/{id} config).
31
+ - /capture published 4 doc updates (web-funnel-app, safe-call-loop-testing, vapi-webhook-handler, vapi-integration).
32
+
33
+ ## What did NOT work — DO NOT RETRY THESE
34
+ - **Answering the test call silently** → 8 seconds of mutual silence, Vapi `endedReason=customer-ended-call`, empty transcript. The healthcare assistant uses `firstMessageMode=assistant-waits-for-user` (deliberate: first-phoneme timing for voicemail/iPhone screening). **Testers must say "Hello?" first.**
35
+ - **Telling the assistant "call me back in ten minutes"** → structured output stored `c_callbackTime="in ten minutes"` (spelled-out number); worker2 `convertDateTime` (Worker/Vapi.php ~L609) returns null (PHP parses "10 minutes" but not "ten minutes") → `actionScheduleCallback` aborts after error_log "Cannot parse callbackTime" → `dtNextContactRequested` never set, **the promised callback silently never happens**. Open HIGH bug; until fixed, spoken-word callback times are dropped.
36
+ - **The dbchanges2 test-campaign migration** (withdrawn, PR to close unmerged): its INSERT...SELECT missed `c_fromPhoneNumberIdentifier`, which `buildVapiPayload` hard-requires (throws) — the SQL-created campaign would have failed at call time. Raw SQL campaign creation is the wrong vehicle; `_Worker_Ai_Bdr_ZoomInfo::SetupCampaign` is the sanctioned creator.
37
+ - **Vapi API auth with worker2 Config [vapi] api_secret** → `unauthorized`. The working credential is the const at Worker/Ai/Bdr/Vapi.php:6 (itself a security finding — rotate + move to config; value not recorded here).
38
+ - **BDR API client reading /contact-attempts** → 403 (EZ-1). Blocks real call summaries + live call status until the backend grants read ACL.
39
+
40
+ ## Not tried yet (candidates for next session)
41
+ - **Fast-dial live test** — restart the dev server (TOGA_WORKER_URL loads at startup), submit Call Now, phone should ring in seconds. First live use of the worker's direct-action endpoint as a product feature.
42
+ - **Self-heal changed-number live test** — Call Now with a different owned number; dialer should ring the new number.
43
+ - **Real summary + live call status** — blocked on the ContactAttempts ACL grant (read-only: uuid, contactId, dtStarted, dtEnded, c_callSummary, c_callOutcome for BDR API client ead1d6ad-04a5-41f9-9a87-669d66ad49a2). One grant unlocks: real summary card content, real Copy/Share body, live status timer.
44
+ - **Callback-time parse fix** (worker2 + Vapi schema): constrain schema callbackTime format AND normalize spelled-out numbers in convertDateTime.
45
+ - **Booking path live test** (Cal.com — campaign 2's eventIdentifier is real).
46
+ - **Restore SQL** = business go-live switch for the ~70 prospects: `UPDATE Contacts c INNER JOIN Campaigns_Contacts cc ON cc.contactId=c.id SET c.isOkayToCall=1 WHERE cc.campaignId=2 AND c.id NOT IN (120,132,134,146,155);` — **never restore those 5 ids (pre-existing DNC)**.
47
+
48
+ ## Current file state
49
+ | File | Status | Notes |
50
+ |------|--------|-------|
51
+ | BDR enrichment + prefill (hubspot/toga/leadSink/page + tests) | committed by tcox (BDR-Phase-2), merge state to _dev-sandbox unverified | live-proven on contact 248 |
52
+ | BDR fast-dial + anon guard + share (workerDialer, callbackService, CallNow, Schedule, schema, default, shareMailto, CallSummary, .env.example + 3 test files) | UNCOMMITTED in C:\WWW\BDR | 131 tests green; suggested msg: "feat: fast dial, anonymous submit guard, real share via mailto" |
53
+ | BDR/.env.local | TOGA_WORKER_URL added (local only) | dev server restart needed to load |
54
+ | dbchanges2 feature/ai-bdr-test-campaign (e99a367) | pushed; PR must be CLOSED UNMERGED | superseded + buggy (see above) |
55
+ | Prod Client_True data | campaign 2 dateEnd=2026-07-28; all campaign-2 contacts isOkayToCall=0 EXCEPT 248 | restore = go-live switch; exclude ids 120/132/134/146/155 |
56
+ | Team knowledge | 4 docs updated + pushed today | web-funnel-app, safe-call-loop-testing, vapi-webhook-handler, vapi-integration |
57
+
58
+ ## Decisions made
59
+ - **Existing campaign + flip-everyone over a new test campaign** (Rohan): zero campaign-config risk, the UI submit is the only re-arm, and the real Cal.com event stays testable. Rejected: SetupCampaign-created test campaign (viable, kept as Approach B in the runbook), raw SQL (buggy + wrong vehicle).
60
+ - **Share via mailto:, not server-side send**: server-sending client-supplied text through TOGA infra is a spam relay; the visitor's own mail client is honest and zero-backend. Real summary content deferred to the ACL grant.
61
+ - **Fast dial is best-effort**: absorbed failures + cron fallback + in-progress dedupe; env-gated (TOGA_WORKER_URL) so deploys without the var behave exactly as before.
62
+ - **Anonymous submits blocked with notice** (matches info) per tcox's directive that anything diverging from info needs correcting.
63
+
64
+ ## Blockers
65
+ - **Amplify app still doesn't exist** (ClickUp 868kdf4tj, Jeff) — 7 env vars now: the 5 server-side + NEXT_PUBLIC_GA_MEASUREMENT_ID + TOGA_WORKER_URL.
66
+ - **ContactAttempts ACL grant** (backend, DB config only) — gates real summaries + live status.
67
+ - **Business decision on the restore SQL** (resumes cold-calling ~70 prospects).
68
+
69
+ ## Exact next step
70
+ > In `C:\WWW\BDR`: commit the uncommitted batch ("feat: fast dial, anonymous submit guard, real share via mailto"), push to BDR-Phase-2, merge to _dev-sandbox. Then restart the dev server (`npm run dev`) and submit Call Now — the phone should ring within seconds (fast-dial's first live test). If it takes ~a minute instead, check the dev console for a `[workerDialer]` warning: that means the worker's direct-action endpoint rejected the trigger and the cron fallback placed the call.
71
+
72
+ ---
73
+ _Saved by /session-save on 2026-07-21_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.396",
3
+ "version": "1.0.398",
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",