toga-ai 1.0.836 → 1.0.837

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.
@@ -17,6 +17,6 @@
17
17
  | [Tools Persona-Gated Navigation (App_Nav)](features/persona-gated-navigation.md) | `App_Nav` is the Tools app's two-level, **persona-gated** navigation. |
18
18
  | [Tools SAML SSO Consumer & Persona-Gated Auth (App_Auth)](features/saml-sso-auth.md) | `App_Auth` is the Tools app's authentication layer: it consumes the SAML gateway `?saml=` handoff (see the 2.0 SAML downstream integration contract), establishe |
19
19
  | [Talos Knowledge Base Admin UI (KB Documents + Vocabulary)](features/talos-kb-documents-admin.md) | > **PER-AI-MODEL, DATA-DRIVEN SCOPING (2026-07-29).** The KB-documents and Vocabulary admin > UIs were refactored from a single hard-coded **"development-team"* |
20
- | [Talos Pricing UI (Contracts, Pricing Dashboard, Usage, Settings + Estimator)](features/talos-pricing-ui.md) | The 1.0 (tools app) face of the **Talos Pricing Platform** — a "Talos Pricing" nav folder with four pages plus the estimate engine. |
20
+ | [Talos Pricing UI (Contracts, Pricing Dashboard, Usage, Settings + Estimator)](features/talos-pricing-ui.md) | The 1.0 (tools) face of the Talos Pricing Platform — Contracts / Pricing Dashboard / Usage / Pricing Settings pages + the estimate engine, reading the Team DB v |
21
21
  | [App-Wide Colour Theme (light / dark / auto)](features/theme-light-dark.md) | A **light / dark / auto** colour theme for the *entire* Tools app, built as a single semantic-token layer (`assets/css/theme.css`) rather than per-page edits. |
22
22
  | [Deploying Tools to Elastic Beanstalk (PHP 8.5 / Amazon Linux 2023)](workflows/deploy-to-elastic-beanstalk-al2023.md) | How the **Tools** 1.0 app boots on Elastic Beanstalk running `PHP 8.5 on 64bit Amazon Linux 2023/4.13.1 (aarch64)`. |
@@ -6,11 +6,12 @@ project: Tools
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-04
9
+ updated: 2026-09-17
10
10
  owners: [jcardinal]
11
11
  files:
12
12
  - tools/_/app/nav.php
13
13
  - tools/_/app/talos/estimator.php
14
+ - tools/_/app/talos/estimator.parity.test.php
14
15
  - tools/_/app/talos/usage.php
15
16
  - tools/mvc/talos/contracts/get.php
16
17
  - tools/mvc/talos/contracts/post.php
@@ -29,100 +30,56 @@ related:
29
30
  - ../../../2.0/apps/worker2/features/talos-pricing-automation.md
30
31
  ---
31
32
 
33
+ The 1.0 (tools) face of the Talos Pricing Platform — Contracts / Pricing Dashboard / Usage / Pricing Settings pages + the estimate engine, reading the Team DB via `db_team`; open for the contract lifecycle, worker-owned read-only rows, or the fetchOne/PHP 8.5 traps.
34
+
32
35
  ## Summary
33
36
 
34
- The 1.0 (tools app) face of the **Talos Pricing Platform** — a "Talos Pricing" nav folder
35
- with four pages plus the estimate engine. Restructured 2026-08-03 to
36
- **Contracts / Pricing Dashboard / Usage / Pricing Settings**. All pages read the **Team DB**
37
- through the `db_team` connection (`[database_team]` in `config.*.ini`).
37
+ The 1.0 (tools app) face of the **Talos Pricing Platform** — a "Talos Pricing" nav folder with four pages plus the estimate engine. Restructured 2026-08-03 to **Contracts / Pricing Dashboard / Usage / Pricing Settings**. All pages read the **Team DB** through the `db_team` connection (`[database_team]` in `config.*.ini`).
38
38
 
39
39
  ## How it works
40
40
 
41
41
  ### Contracts (`mvc/talos/contracts/{get,post}.php`) — replaces `onboarding/*`
42
- The old Onboarding page could only **INSERT** (hardcoded `status='ONBOARDED'`, no edit, no
43
- delete), which is why nothing ever reached the tables. Contracts is a real lifecycle, reusing
44
- the existing status enum: **PROSPECT** = draft, **ACTIVE** = signed, **CHURNED** = ended.
45
-
46
- - **A signed contract can never be hard-deleted.** `TalosPricingBands` is
47
- `ON DELETE CASCADE`, so deleting the client would destroy the agreed rate schedule and the
48
- margin history with it. Signed contracts **churn**; they do not delete.
49
- - **Post-signature edits append to `TalosContractAmendments`** rather than mutating the
50
- signed terms.
51
- - Includes an **Actual-vs-Estimate** comparison with variance chips, a month-by-month history
52
- table plus cost/margin chart, and a one-click **"apply recommended org fee"**.
42
+ The old Onboarding page could only **INSERT** (hardcoded `status='ONBOARDED'`, no edit, no delete), which is why nothing ever reached the tables. Contracts is a real lifecycle, reusing the existing status enum: **PROSPECT** = draft, **ACTIVE** = signed, **CHURNED** = ended.
43
+ - **A signed contract can never be hard-deleted.** `TalosPricingBands` is `ON DELETE CASCADE`, so deleting the client would destroy the agreed rate schedule and the margin history with it. Signed contracts **churn**; they do not delete.
44
+ - **Post-signature edits append to `TalosContractAmendments`** rather than mutating the signed terms.
45
+ - Includes an **Actual-vs-Estimate** comparison with variance chips, a month-by-month history table plus cost/margin chart, and a one-click **"apply recommended org fee"**.
46
+ - **Pricing model toggle + contract terms (2026-09-17).** The page now sets `pricingModel` (`PER_USER` / `PER_INTERACTION`), the fixed 1/3/5-year term dropdown (`contractTermMonths`), the per-interaction inputs (`contractedInteractionCount`, voice-only `avgMinutesPerInteraction`, `perInteractionFee`), and the two one-time contract fees (`implementationFee`, `trainingFee`). Model design, term ramp, fee amortization, and the recurring-vs-blended margin split are all in [pricing-cogs-model](../../../2.0/apps/talos/features/pricing-cogs-model.md) — not restated here.
53
47
 
54
48
  ### Pricing Dashboard (`pricing/get.php`) — rebuilt for sales leadership
55
- Portfolio KPIs, a 12-month blended-margin trend with the target band shaded, an exception
56
- list with deterministic explanations, and licensed-vs-active utilisation.
49
+ Portfolio KPIs, a 12-month blended-margin trend with the target band shaded, an exception list with deterministic explanations, and licensed-vs-active utilisation.
57
50
 
58
51
  ### Usage (`usage/get.php`, `_/app/talos/usage.php`)
59
- Adds `costIntensity()` and a **Cost intensity** panel (tool-mix driven cost).
60
-
61
- **Fixed:** `clientOptions()` read `TalosClients WHERE status='ONBOARDED'` from an
62
- always-empty table, so the client selector rendered **empty**. It now calls
63
- `GET /api/usage/clients` with a Team-DB fallback.
52
+ Adds `costIntensity()` and a **Cost intensity** panel (tool-mix driven cost). **Fixed:** `clientOptions()` read `TalosClients WHERE status='ONBOARDED'` from an always-empty table, so the client selector rendered **empty**. It now calls `GET /api/usage/clients` with a Team-DB fallback.
64
53
 
65
54
  ### Pricing Settings (`settings/{get,post}.php`) — replaces `factors/*`
66
- Technical-only (persona-narrowed on the action, per `mvc-data-access-patterns`). **Measured
67
- (derived) cost-factor rows are worker-owned and read-only**, enforced server-side with
68
- `UPDATE ... AND isDerived = 0`. Policy rows are editable and carry full-sentence labels.
55
+ Technical-only (persona-narrowed on the action, per `mvc-data-access-patterns`). **Measured (derived) cost-factor rows are worker-owned and read-only**, enforced server-side with `UPDATE ... AND isDerived = 0`. Policy rows are editable and carry full-sentence labels.
69
56
 
70
57
  ### Removed
71
- - `mvc/talos/benchmarks/get.php` — read the never-populated `TalosUsageMonthly` and
72
- duplicated a worse subset of the Usage page.
58
+ - `mvc/talos/benchmarks/get.php` — read the never-populated `TalosUsageMonthly` and duplicated a worse subset of the Usage page.
73
59
  - `mvc/talos/onboarding/*` and `mvc/talos/factors/*` — superseded above.
74
60
 
75
61
  ### Estimator (`App_Talos_Estimator`, `_/app/talos/estimator.php`)
76
- Now driven by the **token unit price** and **workload profiles** instead of the feature
77
- checklist and per-client calibration factor (methodology in `pricing-cogs-model`). Service
78
- offering (CHAT / VOICE_TO_VOICE / NATURAL_VOICE) describes **modality only** and says nothing
79
- about cost; cost is driven by tool-call **intensity** via the profile
80
- (retrieval / general / analytics).
62
+ Now driven by the **token unit price** and **workload profiles** instead of the feature checklist and per-client calibration factor (methodology in `pricing-cogs-model`). Service offering (CHAT / VOICE_TO_VOICE / NATURAL_VOICE) describes **modality only** and says nothing about cost; cost is driven by tool-call **intensity** via the profile (retrieval / general / analytics). Per-interaction model adds `costPerInteraction`, `estimateByInteraction`, `recommendedPricePerInteraction`, `recommendedOrgFeeInteraction`, `orgFeeOptionsInteraction`. Constants live in Team-DB `TalosCostFactors` (shared with worker2); a golden-master parity test (`estimator.parity.test.php` ↔ worker2 `PricingParityTest.php`, 11/11 pass) guards drift — see [pricing-cogs-model](../../../2.0/apps/talos/features/pricing-cogs-model.md).
81
63
 
82
64
  ## Gotchas
83
-
84
- - **`App_Database::fetchOne()` is not an existence test** — it 500s on zero rows. See
85
- [mvc-data-access-patterns](./mvc-data-access-patterns.md); the original
86
- `onboarding/post.php` carried this exact bug and would have fataled on the first save
87
- anyone attempted, which is good evidence nobody ever completed an onboarding through it.
88
- - **This app runs PHP 8.5 and escalates deprecations to fatals** — `$arr[$row['nullableCol']]`
89
- is fatal even inside `isset()`. Local dev on PHP 8.2 does **not** reproduce it. Cast to
90
- `(string)` before indexing. See `mvc-data-access-patterns`.
91
- - **`.tool--wide` did nothing.** `assets/css/style.css` documented the class as escaping the
92
- 960px `.app-content` cap "via `:has()`", but that rule was never written. Added
93
- `.app-content:has(.tool--wide) { max-width: 100% }` — which also makes the pre-existing
94
- `mvc/errors` pages genuinely full-width for the first time.
95
- - **Put Talos page CSS in `style.css`, not a page-local `<style>` block.** The Contracts page
96
- **returns early** for list mode, so a `<style>` block placed after that return applied to
97
- the calculator only and the list rendered with no CSS at all.
98
- - **Voice multipliers (0.25 / 0.50) are UNVALIDATED** — supplied second-hand, never measured,
99
- and possibly inverted. Flagged in the UI. **Do not quote voice from them.**
65
+ - **`App_Database::fetchOne()` is not an existence test** — it 500s on zero rows. See [mvc-data-access-patterns](./mvc-data-access-patterns.md); the original `onboarding/post.php` carried this exact bug and would have fataled on the first save anyone attempted, which is good evidence nobody ever completed an onboarding through it.
66
+ - **This app runs PHP 8.5 and escalates deprecations to fatals** — `$arr[$row['nullableCol']]` is fatal even inside `isset()`. Local dev on PHP 8.2 does **not** reproduce it. Cast to `(string)` before indexing. See `mvc-data-access-patterns`.
67
+ - **`.tool--wide` did nothing.** `assets/css/style.css` documented the class as escaping the 960px `.app-content` cap "via `:has()`", but that rule was never written. Added `.app-content:has(.tool--wide) { max-width: 100% }` — which also makes the pre-existing `mvc/errors` pages genuinely full-width for the first time.
68
+ - **Put Talos page CSS in `style.css`, not a page-local `<style>` block.** The Contracts page **returns early** for list mode, so a `<style>` block placed after that return applied to the calculator only and the list rendered with no CSS at all.
69
+ - **Voice multipliers (0.25 / 0.50) are UNVALIDATED** — supplied second-hand, never measured, and possibly inverted. Flagged in the UI. **Do not quote voice from them.** Superseded for per-interaction voice by the `voiceCostPerMinute` factor (see pricing-cogs-model).
70
+ - **`pricingModel` is an enum/FIELD_LIST — validate it in PHP before save.** MySQL silently stores **NULL** for an unknown enum value, so `contracts/post.php` must check the incoming value against the allowed set (`PER_USER`/`PER_INTERACTION`) before saving, and re-validate the locked stored value on a signed row. (General 2.0 rule: `security.md` "Enum columns need validating in PHP".)
71
+ - **Security — two HIGH fixed on the contract page (cso, 2026-09-17):** (a) a signed contract could **flip its `pricingModel` on resubmit** to dodge the other model's minimum-field validation — the "locked at signing" override must run **before** per-model validation; (b) `clientIdentifier` (the AWS cost/usage join key) was only readonly client-side — it **must be re-pinned server-side** once signed, or a resubmit can misattribute a tenant.
100
72
  - **`db_team`** is read/write on Contracts and Settings, read-only on the dashboards.
101
- - **Chart.js colours must come from theme tokens, and a live theme switch needs
102
- `chart.update()`.** These pages now read series/axis/tick/gridline colours via
103
- `TogaTheme.color('--c-…')` inside a `paint()` registered with `TogaTheme.onChange(paint)`,
104
- then call `chart.update()` — `update()` clears Chart.js's resolver cache, so without it a
105
- `Chart.defaults` change does not reach charts that already exist. Chart.js otherwise defaults
106
- axis/tick text to a dark colour that is unreadable in dark mode. See
107
- [App-Wide Colour Theme](./theme-light-dark.md).
108
- - **These pages keep an explicit `--c-bs-*` token group** because they were authored against
109
- Bootstrap's palette (`#0d6efd`, `#6c757d`, `#495057`, `#dc3545`, `#198754`…), which is close
110
- to but not the TOGA brand palette — folding them into `--c-primary` visibly restyles them.
111
- - **Open (dark mode):** the error toast measures ~2.4:1 (white on `--c-danger`) and likely
112
- wants a fixed dark fill; the Pricing Dashboard target-band dataset never had a
113
- `backgroundColor` (pre-existing authoring slip) so the band shades rather than tints; the
114
- three chart pages cannot be statically proven light-identical and still need a browser pass.
115
-
116
- ## Security
117
- - `config.*.ini` carries committed plaintext secrets (team-accepted, see architecture Known
118
- issues) — **all still owe rotation**. Config keys referenced by name only
119
- (`[api] talos_backend_url` / `talos_backend_key`); no values recorded. Do not add secrets.
73
+ - **Chart.js colours must come from theme tokens, and a live theme switch needs `chart.update()`.** These pages now read series/axis/tick/gridline colours via `TogaTheme.color('--c-…')` inside a `paint()` registered with `TogaTheme.onChange(paint)`, then call `chart.update()` — `update()` clears Chart.js's resolver cache, so without it a `Chart.defaults` change does not reach charts that already exist. Chart.js otherwise defaults axis/tick text to a dark colour that is unreadable in dark mode. See [App-Wide Colour Theme](./theme-light-dark.md).
74
+ - **These pages keep an explicit `--c-bs-*` token group** because they were authored against Bootstrap's palette (`#0d6efd`, `#6c757d`, `#495057`, `#dc3545`, `#198754`…), which is close to but not the TOGA brand palette — folding them into `--c-primary` visibly restyles them.
75
+ - **Open (dark mode):** the error toast measures ~2.4:1 (white on `--c-danger`) and likely wants a fixed dark fill; the Pricing Dashboard target-band dataset never had a `backgroundColor` (pre-existing authoring slip) so the band shades rather than tints; the three chart pages cannot be statically proven light-identical and still need a browser pass.
76
+ - **Security:** `config.*.ini` carries committed plaintext secrets (team-accepted, see architecture Known issues) — **all still owe rotation**. Config keys referenced by name only (`[api] talos_backend_url` / `talos_backend_key`); no values recorded. Do not add secrets.
120
77
 
121
- ## Change history
122
- - 2026-08-04 — Tokenised these pages for the new app-wide light/dark theme: page-local styles
123
- now use `--c-*` tokens (Talos pages keep their own `--c-bs-*` Bootstrap ramp), and the three
124
- Chart.js pages read colours via `TogaTheme.color()` + repaint on `TogaTheme.onChange()` with
125
- `chart.update()`. Recorded the dark-mode open items (error-toast contrast, missing
126
- target-band `backgroundColor`). Not committed. (jcardinal)
127
- - 2026-08-04 — Restructured to **Contracts / Pricing Dashboard / Usage / Pricing Settings**. New Contracts pages replace Onboarding with a real PROSPECT/ACTIVE/CHURNED lifecycle (signed contracts churn, never hard-delete — `TalosPricingBands` cascades; post-signature edits append to `TalosContractAmendments`), plus actual-vs-estimate variance, month history chart and one-click apply-recommended-org-fee. Settings replaces the Cost Factors editor with measured rows read-only (`UPDATE ... AND isDerived = 0`). Benchmarks page deleted. Pricing Dashboard rebuilt for sales leadership. Fixed `usage.php clientOptions()` reading an always-empty `TalosClients` (selector rendered empty) → `GET /api/usage/clients` with Team-DB fallback; added cost-intensity panel. Estimator moved to token unit price + workload profiles. Wrote the missing `.app-content:has(.tool--wide)` rule and consolidated Talos CSS into `style.css`. (jcardinal)
128
- - 2026-06-29 — Built the Talos Pricing UI: nav folder + 4 pages (Onboarding w/ live JS estimate and contract-signing that locks the band schedule into `TalosClients`+`TalosPricingBands` in one txn; read-only Pricing Dashboard + Usage Benchmarks; technical-only Cost Factors editor) and `App_Talos_Estimator` (reads `TalosCostFactors`, derives conversations/user so sales need not enter it). Reads Team DB via new `db_team` connection. Offerings CHAT/VOICE_TO_VOICE/NATURAL_VOICE = 1.0/0.25/0.50 (voice ambiguous, deferred). (jcardinal)
78
+ ## Related
79
+ - [App-Wide Colour Theme](./theme-light-dark.md)
80
+ - [Persona-gated navigation](./persona-gated-navigation.md)
81
+ - [Tools MVC — data-access patterns](./mvc-data-access-patterns.md)
82
+ - [Tools architecture](../architecture.md)
83
+ - [Talos architecture (2.0)](../../../2.0/apps/talos/architecture.md)
84
+ - [Pricing COGS model (2.0)](../../../2.0/apps/talos/features/pricing-cogs-model.md)
85
+ - [Talos pricing automation (worker2)](../../../2.0/apps/worker2/features/talos-pricing-automation.md)
@@ -100,6 +100,24 @@ its IDLE write. **There is no timeout, memory cap, or resource limit anywhere in
100
100
  in prod* (see below), never *tune the window size down*. (Corollary: a window that has grown back to
101
101
  the cap is positive evidence the section is completing.)
102
102
 
103
+ #### Reading `NETSUITE_EXECUTION_MODE_<SECTION>` at a glance (2026-09-17)
104
+
105
+ The value is `<seconds>-<STATE>`. Three readings, and only the first is a real freeze:
106
+
107
+ - **`1-RUNNING`** (window collapsed ÷3 to 1s) = throwing on **every** run = real freeze. Go read the
108
+ error (`Logs.Event` by clientId → file:line → `Logs_<Client>.Api` on the transactionId).
109
+ - **A large-window `*-RUNNING`** (e.g. `864000-RUNNING`, `288000-RUNNING`) = healthy mid-run **or** a
110
+ deliberate pause/backfill — **NOT** stuck. Do not "fix" it.
111
+ - **`IDLE` + cursor frozen exactly at a reset value + NO error events = the section toggle is off**
112
+ (`IS_ENABLED_INTEGRATION_<SECTION> = false` in the wrapper). Resetting the cursor does **nothing**
113
+ while the toggle is off. Worked example: NYCHH SALES_ORDERS looked "stuck" after a cursor reset to
114
+ 2020-01-01, but `sync_togasupply_hh.php:33` had `IS_ENABLED_INTEGRATION_SALES_ORDERS = false`;
115
+ re-enabling it (line 33 `true`) made the cursor advance immediately.
116
+
117
+ Before calling any section stuck, **check for error events first.** The developer routinely rewinds
118
+ cursors (and re-enables feeds) after fixes and to prioritize data, so a cursor sitting years back
119
+ (e.g. NYCHH IF/receipts rewound to 2023) can be an intentional backfill, not a fault.
120
+
103
121
  **Where the error is: prod `Logs.Issue` / `Logs.Event`, NOT Sentry.** Read worker errors from the
104
122
  production log DB by default — you do not need a developer to point you there. The base `Logs`
105
123
  schema (prod-logs cluster) holds `Issue` (deduped, with `errorMessage` + `trace`) and `Event` (each
@@ -1681,6 +1699,39 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
1681
1699
  SalesOrders" branch, classify the order authoritatively (`App_Api_Netsuite_Rest::fetchSalesOrderByIdFull`
1682
1700
  + `self::isTransferOrder`) and **return early** when it is a transfer order, gated on
1683
1701
  `IS_ENABLED_INTEGRATION_TRANSFER_ORDERS`. Existing TO-billed invoices were deleted by the developer.
1702
+ - **⚠ The invoice's billed-SO lookup must be PER-CLIENT scoped — the `==1` guard false-throws for a
1703
+ client that keeps two SalesOrders per NetSuite id (fixed + deployed 2026-09-17, Compass).**
1704
+ `syncInvoiceFromNetsuite` (`library/app/api/toga2.php` ~L3326-3339) looked up the billed sales order
1705
+ by `c_netsuiteInternalSalesOrderId` **only** and required `meta->totalRecordCount == 1`. Compass keeps
1706
+ **two** `SalesOrders` rows for one NetSuite id — the Office-Depot-created `customerId=1` order and the
1707
+ NetSuite-synced `customerId=3` order — so the lookup returned 2, failed the `==1` check, and the
1708
+ fail-loud dependency guard threw *"refusing to import invoice … its billed sales order … is not
1709
+ imported in toga"* (prod `Logs.Issue` 792, clientId 2, INVOICES stuck `96000-RUNNING`, cursor
1710
+ ~2026-08-20) even though the correct order (id 97932, `customerId=3`, `locationId=615286`) was present.
1711
+ **Fix:** add a per-client scope block to this lookup **mirroring** `syncSalesOrderFromNetsuite` (~L974-987)
1712
+ and `syncItemFulfillmentFromNetsuite` (~L5197-5204) — Compass adds `SalesOrders.locationId=615286` +
1713
+ `customerId=3`; Prudential adds the `Customers` join + `CLIENT_UUID_PRUDENTIAL_CUSTOMER`; no other client
1714
+ changed, no else/default, and the guard still throws for a genuinely-missing SO. PHP 7.2-safe
1715
+ (`$options['join'] ?? []`), signature unchanged.
1716
+ - **⚠ OPEN follow-up (own ticket): six OTHER clients also keep two SalesOrders per NetSuite id, and the
1717
+ fix does NOT scope them.** A read-only sweep found the same dual-SO pattern in **Growrk (2789), Canon
1718
+ (2890), Endeavorhealth (4828), Spglobal (2893), Browardsheriff (160), Masonite (17)** (Nychh/Elite/Quad/Sbasite
1719
+ = 0; Compass 28377). Dormant today — only Compass is actually throwing the invoice guard (0 "refusing to
1720
+ import invoice" events for any other client in the last 5 days) — but if any of those six ever bills an
1721
+ invoice against a duplicated SO it will false-throw the same way, and **their duplicates have a DIFFERENT
1722
+ cause than Compass's Office-Depot dual-source.** Also a data-quality question: why do six non-Compass
1723
+ clients have duplicate SalesOrders rows at all.
1724
+ - **⚠ ITEM_RECEIPTS auto-creates a warehouse Location — the client's `LocationTypes` MUST have a
1725
+ "Warehouse" row or it EV-12-freezes (fixed 2026-09-17, Prudential).** `syncItemReceiptFromNetsuite`
1726
+ (`library/app/api/toga2.php`) `POST /v2/locations` with `locationType.uuid = WAREHOUSE_LOCATION_TYPE_UUID
1727
+ = 78c24f51-b33b-438b-e3f2-b6232d9ecb75` (:3997). `Client_Prudential.LocationTypes` had only a "Shipping"
1728
+ row — no "Warehouse" — so the reference didn't resolve → **EV-12** (prod `Logs.Issue` 757, clientId 5,
1729
+ ITEM_RECEIPTS `1-RUNNING`, cursor stuck 2026-09-14 15:12:43; confirmed via `Logs_Prudential.Api` `POST
1730
+ /v2/locations` 400). **Fix:** guarded/re-runnable INSERT of the Warehouse `LocationTypes` row using the
1731
+ **same literal uuid every client uses** (the documented shared-lookup-uuid exception to "uuids must be
1732
+ random"; `Client_Nychh` has it as id 2) —
1733
+ `dbchanges2/Client_Prudential/2026-09-17 - SeedWarehouseLocationType.sql`. **The live `LocationTypes`
1734
+ column is `name`, NOT the older `type`.** The section self-heals once the row exists (no manual mode reset).
1684
1735
  - **⚠ ROOT CAUSE of the duplicate `Items`: the inventory-adjustment item-create OMITTED the catalog
1685
1736
  (fixed 2026-08-31).** `getCreateItem` (`toga2.php:6233`) and the fulfillment create (~4951) stamp
1686
1737
  `'catalog' => ['name' => 'Agilant']`; `syncInventoryAdjustmentFromNetsuite`'s item-create did **not** →
@@ -1773,6 +1824,20 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
1773
1824
  [NetSuite Sync Alert Monitor](../../library/features/netsuite-sync-alert-monitor.md).
1774
1825
 
1775
1826
  ## Change history
1827
+ - 2026-09-17 — **Cross-client sync-freeze debugging (Compass / Prudential / NYCHH / Elite).**
1828
+ (1) **Compass INVOICES** unfrozen (`96000-RUNNING`, `Logs.Issue` 792): `syncInvoiceFromNetsuite`'s
1829
+ billed-SO lookup keyed on `c_netsuiteInternalSalesOrderId` with a `==1` guard, but Compass keeps two
1830
+ `SalesOrders` per NetSuite id (ODP `customerId=1` + synced `customerId=3`) → lookup returned 2 →
1831
+ false-throw. Added a per-client scope block mirroring the SO/IF paths (Compass `locationId=615286`+`customerId=3`;
1832
+ Prudential `Customers` join). php-reviewer + cso clean, deployed. **Open follow-up (own ticket):** six other
1833
+ clients (Growrk/Canon/Endeavorhealth/Spglobal/Browardsheriff/Masonite) have the same dual-SO pattern from a
1834
+ different cause and are NOT scoped by this fix — dormant today. (2) **Prudential ITEM_RECEIPTS** unfrozen
1835
+ (`Logs.Issue` 757, EV-12): the receipt sync auto-creates a warehouse Location, but `Client_Prudential.LocationTypes`
1836
+ had no "Warehouse" row → seeded it via `dbchanges2/Client_Prudential/2026-09-17 - SeedWarehouseLocationType.sql`
1837
+ (shared-lookup-uuid; live column is `name`), deployed, section self-healed. (3) Recorded the
1838
+ **execution-mode reading key** (`1-RUNNING` = real freeze; large-window `*-RUNNING` = healthy/paused;
1839
+ `IDLE`+frozen-at-reset+no-errors = toggle off) — NYCHH SALES_ORDERS looked stuck but was toggled off at
1840
+ `sync_togasupply_hh.php:33`; re-enabling advanced the cursor. (jcardinal)
1776
1841
  - 2026-09-17 — Four importer changes, all found on Elite. (1) New
1777
1842
  `App_Api_Toga2::retireStaleSalesOrderForTransferOrder()` retires the stale `SalesOrder` left
1778
1843
  behind when an order is reclassified as a TransferOrder — 218 Elite duplicates were
@@ -2,11 +2,11 @@
2
2
 
3
3
  | Doc | Summary |
4
4
  |-----|---------|
5
- | [TOGa IQ (talos) Architecture](architecture.md) | **TOGa IQ** is TOGA Technology's AI agent platform. |
5
+ | [TOGa IQ (talos) Architecture](architecture.md) | Whole-repo map of TOGa IQ / Aegra (Python AI agent platform); open to understand subsystems, multi-tenancy, or where a component lives. |
6
6
  | [aegra-api — Agent Protocol HTTP + Execution Pipeline](features/aegra-api.md) | `aegra-api` is the **FastAPI Agent Protocol server** at the heart of TOGa IQ. |
7
7
  | [TOGa IQ Chat Frontend (Next.js) — streaming stack & Aegra wiring](features/chat-frontend.md) | The repo **`agilantsolutions/talos`** is the TOGa IQ **chat front-end** — the browser client that talks to the Aegra Agent-Protocol backend. |
8
8
  | [Deployment — Docker, Compose, Entrypoint, External PG/Redis](features/deployment.md) | TOGa IQ ships as a **single container** (`aegra` service) wrapping the `aegra-api` FastAPI server. |
9
9
  | [MCP Servers — clickup-mcp and toga-db-mcp](features/mcp-servers.md) | Two internal **FastMCP** servers exposed over **HTTP** with API-key auth and PM2 process management: - **`clickup-mcp`** — ClickUp workspace surface (spaces / f |
10
10
  | [Observability — Langfuse, OTEL, Prometheus, OneUptime](features/observability.md) | TOGa IQ uses **two complementary tracing planes** plus optional Prometheus metrics and external uptime monitoring: - **Langfuse (native v3 SDK)** — LLM-shaped t |
11
- | [Pricing & COGS Model (Talos / TOGa IQ Pricing Calculator)](features/pricing-cogs-model.md) | The cost basis and pricing methodology for selling Talos (TOGa IQ) chat and voice deployments. |
11
+ | [Pricing & COGS Model (Talos / TOGa IQ Pricing Calculator)](features/pricing-cogs-model.md) | Cost basis + pricing methodology for selling Talos chat and voice; open to re-price Talos or understand COGS drivers, token rates, and the band/org-fee model. |
12
12
  | [talos_agent — LangGraph ReAct Agent with Plan, BLP, MCP, Canvas](features/talos-agent.md) | `talos_agent` is the **reference LangGraph agent** shipped under `agents/talos_agent/`. |
@@ -6,7 +6,7 @@ project: TOGa IQ
6
6
  client: shared
7
7
  type: architecture
8
8
  status: active
9
- updated: 2026-06-29
9
+ updated: 2026-09-17
10
10
  owners: [akhokhani, jcardinal]
11
11
  files:
12
12
  - talos/libs/aegra-api/src/aegra_api/main.py
@@ -30,33 +30,17 @@ related:
30
30
  - ../../1.0/apps/tools/features/talos-pricing-ui.md
31
31
  ---
32
32
 
33
+ Whole-repo map of TOGa IQ / Aegra (Python AI agent platform); open to understand subsystems, multi-tenancy, or where a component lives.
34
+
33
35
  ## Summary
34
36
 
35
- **TOGa IQ** is TOGA Technology's AI agent platform. The on-disk repo is **`talos`**
36
- (the "talos backend"). It is a Python/FastAPI implementation of the **Agent Protocol**
37
- spec, multi-tenant, with a LangGraph **ReAct agent** as the reference agent and a
38
- fleet of internal **MCP servers** for ClickUp and the TOGa MySQL clusters.
39
-
40
- Unlike the PHP 2.0 apps (`_underscore`, `api2`, `worker2`), TOGa IQ has **no PHP
41
- dependency** — it is a self-contained Python product that consumes TOGa data
42
- read-only via the `toga-db-mcp` server. It lives under `2.0/apps/talos/` because
43
- its consumers are 2.0-era TOGa apps (TOGa Hub, TOGa View) and TogaHub auth.
44
-
45
- > **Repo scope — this doc covers the BACKEND ONLY, which is not in the `talos` repo.**
46
- > Verified 2026-08-28 (`git ls-tree` across all 16 `origin/*` refs): no branch of
47
- > `agilantsolutions/talos` contains `libs/aegra-api`, `agents/`, `mcp-servers/`, or
48
- > `docker-compose.yml` — every branch is the chat **front-end**, documented in
49
- > [features/chat-frontend.md](features/chat-frontend.md). The Aegra backend described below lives
50
- > in a separate, currently unregistered repo, so the `files:` paths above do not resolve.
51
-
52
- **Critical rules:** Clients speak the **Agent Protocol** — never invent a custom wire shape; SDK and
53
- LangGraph-Studio compatibility depend on it. **Tenant provisioning is explicit with no auto-create**,
54
- so an unregistered `org_id` gets a clean **403 on every call even with a valid token** — provision
55
- the tenant before debugging the caller. `AUTH_TYPE` ∈ {`togahub`, `entra`, `noop`} and **`noop` must
56
- never ship to prod**. `toga-db-mcp` is the **only** sanctioned path into TOGa MySQL (read-only,
57
- `LIMIT` 1–1000, enforced inside the MCP rather than the agent), and the Fernet `MCP_ENCRYPTION_KEY`
58
- is a single **global** key shared by every tenant — not per-tenant — so treat rotating it as a
59
- cross-tenant event.
37
+ **TOGa IQ** = TOGA's AI agent platform. On-disk repo **`talos`** (the "talos backend"). Python/FastAPI implementation of the **Agent Protocol** spec, multi-tenant, with a LangGraph **ReAct agent** as reference agent and internal **MCP servers** for ClickUp and TOGa MySQL clusters.
38
+
39
+ No PHP dependency (unlike `_underscore`, `api2`, `worker2`). Self-contained Python product; consumes TOGa data read-only via `toga-db-mcp`. Lives under `2.0/apps/talos/` because its consumers are 2.0-era apps (TOGa Hub, TOGa View) and TogaHub auth.
40
+
41
+ **Repo scope — this doc is BACKEND ONLY, which is NOT in the `talos` repo.** Verified 2026-08-28 (`git ls-tree` across all 16 `origin/*` refs): no branch of `agilantsolutions/talos` contains `libs/aegra-api`, `agents/`, `mcp-servers/`, or `docker-compose.yml` — every branch is the chat **front-end** ([features/chat-frontend.md](features/chat-frontend.md)). The Aegra backend below lives in a separate, currently unregistered repo, so the `files:` paths do not resolve.
42
+
43
+ **Critical rules:** Clients speak the **Agent Protocol** — never invent a custom wire shape; SDK and LangGraph-Studio compatibility depend on it. **Tenant provisioning is explicit, no auto-create**, so an unregistered `org_id` gets a clean **403 on every call even with a valid token** — provision the tenant before debugging the caller. `AUTH_TYPE` ∈ {`togahub`, `entra`, `noop`} and **`noop` must never ship to prod**. `toga-db-mcp` is the **only** sanctioned path into TOGa MySQL (read-only, `LIMIT` 1–1000, enforced inside the MCP not the agent), and the Fernet `MCP_ENCRYPTION_KEY` is a single **global** key shared by every tenant — treat rotating it as a cross-tenant event.
60
44
 
61
45
  ## Top-level layout
62
46
 
@@ -82,136 +66,73 @@ talos/
82
66
 
83
67
  ## Five subsystems
84
68
 
85
- 1. **HTTP / Agent Protocol** — `libs/aegra-api`. FastAPI app exposes ~40 routers
86
- covering Assistants, Threads, Runs, Crons, Store, Files/Folders, Knowledge
87
- Bases, MCP, Sandboxes, Teams, BLP, admin/tenants. Pluggable auth via
88
- `AUTH_TYPE` ∈ {`togahub`, `entra`, `noop`}. See `features/aegra-api.md`.
89
- 2. **Execution pipeline** — Broker → Executor → LangGraph → Streaming. Local
90
- mode uses an in-memory queue + asyncio. Prod uses a Redis pub/sub broker +
91
- Redis BLPOP worker queue with lease-based crash recovery. SSE replay is
92
- backed by an in-memory ring + Postgres `run_events` table. See
93
- `features/aegra-api.md`.
94
- 3. **Reference agent (LangGraph)** — `agents/talos_agent`. A ReAct graph with
95
- optional planner gate, tool router, BLP semantic context selection,
96
- running-summary memory, canvas artifacts, code-interpreter offload, and MCP
97
- passthrough. See `features/talos-agent.md`.
98
- 4. **MCP fleet** — `clickup-mcp` and `toga-db-mcp`. Both FastMCP servers
99
- exposed over HTTP, API-key gated, PM2-managed, with OneUptime heartbeats.
100
- `toga-db-mcp` is the canonical read-only data plane across 4 prod clusters
101
- + legacy + 16 non-prod environments (see `clusters.yaml`). See
102
- `features/mcp-servers.md`.
103
- 5. **Observability** — Langfuse (native v3 SDK) for LLM traces, OTEL (Phoenix /
104
- Langfuse OTLP / generic OTLP / console) for spans, optional Prometheus
105
- `/metrics`, plus OneUptime for uptime + heartbeats on MCP servers. See
106
- `features/observability.md`.
69
+ 1. **HTTP / Agent Protocol** — `libs/aegra-api`. FastAPI exposes ~40 routers: Assistants, Threads, Runs, Crons, Store, Files/Folders, Knowledge Bases, MCP, Sandboxes, Teams, BLP, admin/tenants. Pluggable auth via `AUTH_TYPE` ∈ {`togahub`, `entra`, `noop`}. See `features/aegra-api.md`.
70
+ 2. **Execution pipeline** — Broker → Executor → LangGraph → Streaming. Local = in-memory queue + asyncio. Prod = Redis pub/sub broker + Redis BLPOP worker queue with lease-based crash recovery. SSE replay backed by in-memory ring + Postgres `run_events` table. See `features/aegra-api.md`.
71
+ 3. **Reference agent (LangGraph)** — `agents/talos_agent`. ReAct graph with optional planner gate, tool router, BLP semantic context selection, running-summary memory, canvas artifacts, code-interpreter offload, MCP passthrough. See `features/talos-agent.md`.
72
+ 4. **MCP fleet** — `clickup-mcp` and `toga-db-mcp`. Both FastMCP servers over HTTP, API-key gated, PM2-managed, OneUptime heartbeats. `toga-db-mcp` = canonical read-only data plane across 4 prod clusters + legacy + 16 non-prod environments (see `clusters.yaml`). See `features/mcp-servers.md`.
73
+ 5. **Observability** — Langfuse (native v3 SDK) for LLM traces, OTEL (Phoenix / Langfuse OTLP / generic OTLP / console) for spans, optional Prometheus `/metrics`, OneUptime for uptime + MCP heartbeats. See `features/observability.md`.
107
74
 
108
75
  ## Multi-tenancy
109
76
 
110
- A **control plane DB** (`core/control_plane_db.py`, `control_plane_orm.py`) holds
111
- the organization → tenant-DB mapping. `core/tenant_router.py` keeps an LRU
112
- cache (max 100) of per-tenant `DatabaseManager` instances keyed by `org_id`.
113
- Requests without a registered tenant return HTTP 403; tenant provisioning is
114
- explicit (no auto-create). When `CONTROL_PLANE_DATABASE_URL` is unset the
115
- service operates in **zero-overhead single-DB mode**.
116
-
117
- At startup the lifespan handler migrates every registered tenant DB and seeds
118
- base infrastructure before the health-check loop opens — slow first boot but
119
- guarantees readiness.
77
+ - **Control plane DB** (`core/control_plane_db.py`, `control_plane_orm.py`) holds organization → tenant-DB mapping.
78
+ - `core/tenant_router.py` keeps an LRU cache (max 100) of per-tenant `DatabaseManager` instances keyed by `org_id`.
79
+ - Unregistered tenant → HTTP 403. Provisioning is explicit (no auto-create).
80
+ - `CONTROL_PLANE_DATABASE_URL` unset → **zero-overhead single-DB mode**.
81
+ - Startup lifespan migrates every registered tenant DB and seeds base infrastructure before the health-check loop opens — slow first boot but guarantees readiness.
120
82
 
121
83
  ## Auth
122
84
 
123
- `AUTH_TYPE` selects the backend:
124
-
85
+ `AUTH_TYPE` selects backend:
125
86
  - **togahub** — validates JWTs against TogaHub (`core/togahub_auth.py`).
126
87
  - **entra** — Microsoft Entra ID JWT validation (`core/entra_auth.py`).
127
88
  - **noop** — local-dev bypass; **never** ship to prod.
128
89
 
129
- Auth is implemented as a FastAPI **dependency** (`require_auth` in
130
- `core/auth_deps.py`), not middleware — so OpenAPI sees `401` correctly and
131
- public routes (`/health`, `/ready`, `/live`, `/info`, `/docs`) cleanly skip it.
132
- API-key auth (`core/api_key_auth.py`) supplements user auth for service-to-service.
90
+ Auth is a FastAPI **dependency** (`require_auth` in `core/auth_deps.py`), not middleware — so OpenAPI sees `401` correctly and public routes (`/health`, `/ready`, `/live`, `/info`, `/docs`) cleanly skip it. API-key auth (`core/api_key_auth.py`) supplements user auth for service-to-service.
133
91
 
134
92
  ## Run lifecycle (mental model)
135
93
 
136
- A client POSTs to `/threads/{id}/runs` (or stateless `/runs`). `run_preparation`
137
- materializes the run, the **broker** publishes a `run_created` event, the
138
- **executor** (local or worker) picks it up, `run_executor.py` calls
139
- `langgraph_service` to compile the graph for this tenant/assistant, streams
140
- events through `graph_streaming.py` into the broker, and the streaming endpoint
141
- (`streaming_service.py`) replays from the broker buffer + Postgres event store
142
- to the SSE consumer with 15s keepalive comments. Interrupts (HITL) surface as
143
- `interrupt` events; resume re-enters via `Command(resume=…)`.
94
+ Client POSTs to `/threads/{id}/runs` (or stateless `/runs`). `run_preparation` materializes the run → **broker** publishes `run_created` → **executor** (local or worker) picks it up → `run_executor.py` calls `langgraph_service` to compile the graph for this tenant/assistant → streams events through `graph_streaming.py` into the broker → streaming endpoint (`streaming_service.py`) replays from broker buffer + Postgres event store to the SSE consumer with 15s keepalive comments. Interrupts (HITL) surface as `interrupt` events; resume re-enters via `Command(resume=…)`.
144
95
 
145
96
  ## Storage
146
97
 
147
- - **Postgres** (with `pgvector`) — primary store: assistants, threads, runs,
148
- checkpoints, `run_events`, `store` namespace KV + embeddings, knowledge bases.
149
- - **Redis** — broker pub/sub + replay buffer, worker BLPOP queue, sequence
150
- counters, optional rate-limit windows.
98
+ - **Postgres** (with `pgvector`) — primary store: assistants, threads, runs, checkpoints, `run_events`, `store` namespace KV + embeddings, knowledge bases.
99
+ - **Redis** — broker pub/sub + replay buffer, worker BLPOP queue, sequence counters, optional rate-limit windows.
151
100
  - **S3** — file uploads (presigned URL flow via `api/files.py`).
152
101
  - **Bedrock AgentCore sandbox** — code-interpreter session + large-output offload.
153
102
 
154
103
  ## Encryption
155
104
 
156
- A single global `MCP_ENCRYPTION_KEY` (Fernet) encrypts MCP server headers
157
- before DB storage (`core/encryption.py` → `encrypt_headers` / `decrypt_headers`).
158
- **Not per-tenant** — all tenants share one key. Document *where* a credential
159
- lives (env var name, config key) but never the value itself.
105
+ Single global `MCP_ENCRYPTION_KEY` (Fernet) encrypts MCP server headers before DB storage (`core/encryption.py` → `encrypt_headers` / `decrypt_headers`). **Not per-tenant** — all tenants share one key. Document *where* a credential lives (env var name, config key) but never the value.
160
106
 
161
107
  ## Deployment
162
108
 
163
- - **Container** — multi-stage `python:3.11-slim-bookworm` build with `uv`,
164
- WeasyPrint runtime deps for HTML→PDF. Non-root `app:app`.
165
- - **Entrypoint** — loads `.env.${ENV_NAME}` then runs
166
- `alembic upgrade head && uvicorn aegra_api.main:app --host 0.0.0.0 --port 8000 --workers 2`.
167
- - **Compose** — single `aegra` service; **PostgreSQL and Redis are
168
- user-managed** (external). Health check hits `/live`. See
169
- `features/deployment.md`.
109
+ - **Container** — multi-stage `python:3.11-slim-bookworm` build with `uv`, WeasyPrint runtime deps for HTML→PDF. Non-root `app:app`.
110
+ - **Entrypoint** — loads `.env.${ENV_NAME}` then runs `alembic upgrade head && uvicorn aegra_api.main:app --host 0.0.0.0 --port 8000 --workers 2`.
111
+ - **Compose** — single `aegra` service; **PostgreSQL and Redis are user-managed** (external). Health check hits `/live`. See `features/deployment.md`.
170
112
 
171
113
  ## Talos Pricing Platform (cross-framework)
172
114
 
173
- A database-backed pricing/margin system replacing the Excel calculator. Three tiers:
174
-
175
- - **System of record — Team DB (9 `Talos*` tables, dbchanges2):** TalosClients (contract
176
- header), TalosPricingBands (locked per-user band schedule), TalosCostFactors (editable
177
- key/value cost constants — the "technical tab"), TalosUsageMonthly + TalosUsageFeatureMonthly
178
- (Langfuse import targets), TalosAwsActualsMonthly (Cost Explorer), TalosClientUserCounts
179
- (headcount for band-breach), TalosCalibrationMonthly + TalosFeeRecommendations (cron-derived).
180
- Keyed by `clientIdentifier` (= Langfuse `org:<slug>` tag and AWS cost tag) + `periodMonth`
181
- (1st-of-month DATE), UNIQUE on those for idempotent upsert. Tenant link to `Core.Clients` is a
182
- plain **indexed column, not a hard FK** (Team DB may be a separate instance). Conventions:
183
- id+uuid, dtCreated/dtUpdated, InnoDB, utf8mb4_0900_ai_ci.
184
- - **Sales/admin UI — tools (1.0):** onboarding (locks band schedule), dashboard, benchmarks,
185
- technical-only Cost Factors editor + `App_Talos_Estimator`. Reads Team DB via `db_team`.
186
- - **Automation — worker2 (2.0):** monthly crons import AWS actuals, calibrate Langfuse→AWS,
187
- recompute margins/recommendations, send the leadership report. Uses `_underscore::DB_TEAM`.
188
-
189
- **Data flow:** Langfuse usage (structure) + AWS Cost Explorer (dollars) → calibrate per client →
190
- margin vs. locked band → org-fee recommendation after N consecutive out-of-band months.
191
-
192
- **Critical rules:** Team-DB fact tables are upsert-keyed on `(clientIdentifier, periodMonth
193
- [, toolCategory])` — never INSERT-only. The Core tenant link is an indexed column, not an FK.
194
- Always use Langfuse's cache-corrected cost (it undercounts cache badly).
115
+ Database-backed pricing/margin system replacing the Excel calculator. Three tiers:
116
+
117
+ - **System of record — Team DB (9 `Talos*` tables, dbchanges2):** TalosClients (contract header — now carries `pricingModel` PER_USER|PER_INTERACTION + contract-term + one-time fee columns), TalosPricingBands (locked per-user band schedule — **per-user model only**), TalosCostFactors (editable key/value cost constants — the "technical tab"; also holds `voiceCostPerMinute` + the `termDiscount.*` ramp), TalosUsageMonthly + TalosUsageFeatureMonthly (Langfuse import targets), TalosAwsActualsMonthly (Cost Explorer), TalosClientUserCounts (headcount for band-breach), TalosCalibrationMonthly + TalosFeeRecommendations (cron-derived). Keyed by `clientIdentifier` (= Langfuse `org:<slug>` tag and AWS cost tag) + `periodMonth` (1st-of-month DATE), UNIQUE on those for idempotent upsert. Tenant link to `Core.Clients` is a plain **indexed column, not a hard FK** (Team DB may be a separate instance). Conventions: id+uuid, dtCreated/dtUpdated, InnoDB, utf8mb4_0900_ai_ci.
118
+ - **Sales/admin UI — tools (1.0):** onboarding (locks band schedule), dashboard, benchmarks, technical-only Cost Factors editor + `App_Talos_Estimator`. Reads Team DB via `db_team`.
119
+
120
+ **Two revenue models (2026-09-17):** per-user (org fee + locked per-user band schedule) and per-interaction (org fee + single locked price per conversation, no bands). Both use the org fee as the sole monthly margin lever. Contract-term discount ramp (1/3/5-yr, % off year 1) applies to both, only when a term is set. Design in features/pricing-cogs-model.md.
121
+ - **Automation — worker2 (2.0):** monthly crons import AWS actuals, calibrate Langfuse→AWS, recompute margins/recommendations, send the leadership report. Uses `_underscore::DB_TEAM`.
122
+
123
+ **Data flow:** Langfuse usage (structure) + AWS Cost Explorer (dollars) → calibrate per client → margin vs. locked band → org-fee recommendation after N consecutive out-of-band months.
124
+
125
+ **Critical rules:** Team-DB fact tables are upsert-keyed on `(clientIdentifier, periodMonth [, toolCategory])` — never INSERT-only. The Core tenant link is an indexed column, not an FK. Always use Langfuse's cache-corrected cost (it undercounts cache badly). The **recurring** margin (org fee + usage/interaction revenue) is the ONLY input to the fee-change recommendation + streak; the **blended** margin (adds amortized one-time fees: implementation + training) is display-only — never feed amortized fees into the streak. The term ramp applies only to contracts with a set term (no retroactive discount).
195
126
 
196
127
  ## Key decisions
197
128
 
198
- - **Agent Protocol over a custom shape** — clients (TOGa Hub, TOGa View, third-party)
199
- share one wire format; LangGraph Studio works out of the box.
200
- - **Pluggable executors** — `LocalExecutor` for dev/CI, `WorkerExecutor` with
201
- Redis lease + reaper for prod horizontal scaling.
202
- - **Native Langfuse + OTEL side-by-side** — Langfuse for LLM-shaped traces
203
- (token/cost), OTEL for everything else; the OTEL Langfuse target is skipped
204
- when native mode is on to avoid duplicate spans.
205
- - **MCP for data access** — `toga-db-mcp` is the *only* sanctioned path into
206
- TOGa MySQL for the agent; query enforcement (read-only, LIMIT 1–1000) lives
207
- inside the MCP, not the agent.
208
-
209
- ## Change history
210
- - 2026-08-28 — Scoped this doc to the Aegra backend and recorded that it is **not** the `talos` repo
211
- (verified across all 16 origin refs); the `talos` repo is the chat front-end, now documented in
212
- `features/chat-frontend.md`. Dropped `"language": "python"` from the `talos` `registry.json` entry
213
- so the front-end repo stops loading `2.0/standards/python.md`. **Deferred half — once the Aegra
214
- backend repo's name is known, register it and set `"language": "python"` on THAT entry**; until
215
- then no repo loads the Python standard. Added a backend-scoped `Critical rules:` line to the
216
- Summary (the pre-existing one under Talos Pricing Platform covers pricing only). (apeterson)
217
- - 2026-06-16 — Initial architecture doc for talos / TOGa IQ under 2.0/apps. (akhokhani)
129
+ - **Agent Protocol over a custom shape** — clients (TOGa Hub, TOGa View, third-party) share one wire format; LangGraph Studio works out of the box.
130
+ - **Pluggable executors** — `LocalExecutor` for dev/CI, `WorkerExecutor` with Redis lease + reaper for prod horizontal scaling.
131
+ - **Native Langfuse + OTEL side-by-side** — Langfuse for LLM-shaped traces (token/cost), OTEL for everything else; the OTEL Langfuse target is skipped when native mode is on to avoid duplicate spans.
132
+ - **MCP for data access** — `toga-db-mcp` is the *only* sanctioned path into TOGa MySQL for the agent; query enforcement (read-only, LIMIT 1–1000) lives inside the MCP, not the agent.
133
+
134
+ ## Gotchas
135
+
136
+ - **`files:` paths don't resolve** — the Aegra backend lives in a separate unregistered repo (see repo-scope note in Summary); file-based knowledge search cannot bridge from Aegra code to this doc.
137
+ - **Slow first boot** — lifespan migrates every registered tenant DB before opening the health check; plan for >30s with many tenants.
138
+ - **`MCP_ENCRYPTION_KEY` is global** — rotating requires decrypt-then-re-encrypt for all `mcp_servers` rows across every tenant.