toga-ai 1.0.266 → 1.0.268
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.
- package/knowledge/1.0/apps/tools/INDEX.md +1 -1
- package/knowledge/1.0/apps/tools/features/talos-kb-documents-admin.md +57 -12
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -0
- package/knowledge/2.0/apps/_underscore/features/error-reporting-issue-event.md +95 -0
- package/knowledge/2.0/apps/worker2/INDEX.md +1 -1
- package/knowledge/2.0/apps/worker2/features/talos-transcript-ingestion.md +73 -14
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
| [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 |
|
|
8
8
|
| [Tools Persona-Gated Navigation (App_Nav)](features/persona-gated-navigation.md) | `App_Nav` is the Tools app's two-level, **persona-gated** navigation. | tools/_/app/nav.php, tools/mvc/get.php |
|
|
9
9
|
| [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 | tools/_/app/auth.php, tools/mvc/sso/initiate/get.php, tools/mvc/sso/get.php, tools/mvc/login/get.php, tools/mvc/login/post.php, tools/mvc/logout/get.php, tools/mvc/get.php, tools/config.production.ini, tools/config.local.ini |
|
|
10
|
-
| [Talos Knowledge Base Admin UI (KB Documents + Vocabulary)](features/talos-kb-documents-admin.md) | A Tools (1.0) admin UI to browse/fix the Talos knowledge-base documents and manage the transcript-cleanup vocabulary — without a deploy. | tools/mvc/talos/kb-documents/get.php, tools/mvc/talos/kb-documents/post.php, tools/mvc/talos/vocabulary/get.php, tools/mvc/talos/vocabulary/post.php, tools/_/app/talos/s3.php, tools/_/app/worker.php, tools/_/app/nav.php |
|
|
10
|
+
| [Talos Knowledge Base Admin UI (KB Documents + Vocabulary)](features/talos-kb-documents-admin.md) | A Tools (1.0) admin UI to browse/fix the Talos knowledge-base documents and manage the transcript-cleanup vocabulary — without a deploy. | tools/mvc/talos/kb-documents/get.php, tools/mvc/talos/kb-documents/post.php, tools/mvc/talos/vocabulary/get.php, tools/mvc/talos/vocabulary/post.php, tools/_/app/talos/s3.php, tools/_/app/worker.php, tools/_/app/nav.php, tools/config.production.ini, tools/config.alpha.ini |
|
|
11
11
|
| [Talos Pricing UI (Onboarding, Dashboard, Benchmarks, Cost Factors + 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 a client-side estimate engine. | tools/_/app/nav.php, tools/_/app/talos/estimator.php, tools/mvc/talos/onboarding/get.php, tools/mvc/talos/onboarding/post.php, tools/mvc/talos/pricing/get.php, tools/mvc/talos/benchmarks/get.php, tools/mvc/talos/factors/get.php, tools/mvc/talos/factors/post.php, tools/assets/css/style.css |
|
|
12
12
|
| [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)`. | tools/.ebextensions/004_http_to_https.config, tools/.ebextensions/006_mount-s3fs.config, tools/.ebextensions/007_setup_export_cache_folders.config, tools/.ebextensions/008_setup_ldap.config, tools/.ebextensions/009_setup_phpini.config, tools/.ebextensions/020_setup_git_libraries.config, tools/.ebextensions/050_register_instance_to_shared_application_load_balancer.config, tools/ebs/git.json |
|
|
@@ -6,7 +6,7 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-02
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/mvc/talos/kb-documents/get.php
|
|
@@ -16,6 +16,8 @@ files:
|
|
|
16
16
|
- tools/_/app/talos/s3.php
|
|
17
17
|
- tools/_/app/worker.php
|
|
18
18
|
- tools/_/app/nav.php
|
|
19
|
+
- tools/config.production.ini
|
|
20
|
+
- tools/config.alpha.ini
|
|
19
21
|
related:
|
|
20
22
|
- ./mvc-data-access-patterns.md
|
|
21
23
|
- ./persona-gated-navigation.md
|
|
@@ -37,12 +39,27 @@ Technology*) exposes two pages.
|
|
|
37
39
|
- KB list derived from the **actual S3 sub-folders** under `development-team/` (title-cased,
|
|
38
40
|
e.g. `office-depot` → "Office Depot") — not from `Team.KnowledgeBases`.
|
|
39
41
|
- Lists approved docs excluding `*.metadata.json`; columns Meeting Date (`n/j/y`) / Title /
|
|
40
|
-
Size / Last modified (`n/j/y g:i A`).
|
|
42
|
+
Size / Last modified (`n/j/y g:i A`). **Last-modified is converted from S3's UTC to
|
|
43
|
+
America/Chicago (US Central) before formatting**; the sort key stays UTC ISO-8601.
|
|
41
44
|
- Clickable rows open an overlay **modal** with a type-aware preview: text inline; PDF via a
|
|
42
45
|
presigned S3 URL in an iframe; Office files via the Google Docs viewer (presigned URL; MS
|
|
43
46
|
Office viewer noted as an alternative); unsupported → download/delete only.
|
|
44
|
-
-
|
|
45
|
-
re-sync** so Bedrock reflects the
|
|
47
|
+
- Modal actions are icon buttons: download, **move**, **edit** (text docs only), and delete.
|
|
48
|
+
**Delete / move / edit each enqueue the worker `SyncKb` re-sync** so Bedrock reflects the change.
|
|
49
|
+
- **Move** (right-arrow icon) opens a small centered dialog (KB picker + Move/Cancel) to re-file a
|
|
50
|
+
mis-classified doc from one KB to another. `App_Talos_S3::moveApprovedDocument` copies the `.txt`
|
|
51
|
+
and its `.metadata.json` sidecar to the destination via **get+put (NOT server-side `CopyObject` —
|
|
52
|
+
the togaiq key is denied CopyObject even same-bucket)**, rewrites the sidecar's KB-slug attribute
|
|
53
|
+
(`metadataAttributes.client.value.stringValue`), then deletes the originals. It enqueues a
|
|
54
|
+
**single** `SyncKb("source,dest")` job (worker2 syncs both sequentially — Bedrock allows one
|
|
55
|
+
in-flight ingestion per KB). Helpers `copyWithinBucket` + `putObjectBody` added.
|
|
56
|
+
- **Edit** (pencil icon, text docs only) enters edit mode in the modal: a title text box, a
|
|
57
|
+
**Find & Replace** tool ported from the old `test/team/talos/kb_processor.php` (Find/Replace,
|
|
58
|
+
Match case, Whole word only, Replace All with match count), and a contents textarea, with
|
|
59
|
+
Save/Cancel top-right. The `update` action + `App_Talos_S3::updateApprovedDocument` rewrites the
|
|
60
|
+
body and, if the title changed (`sanitizeTitle`), renames the object to
|
|
61
|
+
`"YYYY-MM-DD - {title}.txt"` (date preserved), moves+updates the sidecar filename attribute,
|
|
62
|
+
deletes the old key, and enqueues a `SyncKb` re-sync.
|
|
46
63
|
|
|
47
64
|
### `/talos/vocabulary` — cleanup vocabulary manager
|
|
48
65
|
Single tabbed page: Prompt Template, Applications, Clients, People, Business Terms, Examples,
|
|
@@ -53,16 +70,29 @@ prompt/replacement tables that the worker2 pipeline reads at runtime.
|
|
|
53
70
|
|
|
54
71
|
## Helpers
|
|
55
72
|
|
|
56
|
-
- **`App_Talos_S3`** (`_/app/talos/s3.php`) — S3 client; list KB slugs; list/count approved
|
|
57
|
-
presigned URL; get/delete;
|
|
73
|
+
- **`App_Talos_S3`** (`_/app/talos/s3.php`) — S3 client; list KB slugs; list/count approved
|
|
74
|
+
(UTC→Central for display); presigned URL; get/delete; `moveApprovedDocument`;
|
|
75
|
+
`updateApprovedDocument`; `sanitizeTitle`; `copyWithinBucket`; `putObjectBody`; slug
|
|
76
|
+
sanitize+lowercase; approved-prefix path guard. **`client()` credential resolution:** prefers a
|
|
77
|
+
dedicated **`[talos]`** config section (the **togaiq**-capable key), falling back to `[aws]`, then
|
|
78
|
+
the SDK default chain — because `App_Talos_S3` only ever talks to **togaiq**, so it needs the
|
|
79
|
+
togaiq-account key (the tools `[aws]` key is the toga-private key, which can cross-account *read*
|
|
80
|
+
togaiq but not *write* it).
|
|
58
81
|
- **`App_Worker::enqueue()`** (`_/app/worker.php`) — inserts a `Core.WorkerJobs` row and sends
|
|
59
|
-
the `{"workerJobId": id}` SQS message, so a 1.0 app can enqueue worker2 jobs.
|
|
82
|
+
the `{"workerJobId": id}` SQS message, so a 1.0 app can enqueue worker2 jobs. Requires the
|
|
83
|
+
`[worker]` (`queue_url`/`queue_region`) and `[aws]` (main-account key for SQS `SendMessage`)
|
|
84
|
+
config sections.
|
|
60
85
|
|
|
61
86
|
## Deploy prerequisites
|
|
62
87
|
|
|
63
88
|
- `aws/aws-sdk-php` installed in `tools/vendor` (S3 + SQS).
|
|
64
|
-
-
|
|
65
|
-
|
|
89
|
+
- `tools/config.production.ini` / `config.alpha.ini` sections (now added):
|
|
90
|
+
- **`[talos]`** — the **togaiq**-account S3 key (`App_Talos_S3` write path); required or move/edit/
|
|
91
|
+
delete fail with `AccessDenied`.
|
|
92
|
+
- **`[worker]`** — worker2 production SQS `queue_url` / `queue_region` (for `App_Worker::enqueue`).
|
|
93
|
+
- **`[aws]`** — main-account key for SQS `SendMessage` (toga-private key; cross-account reads
|
|
94
|
+
togaiq but cannot write it — that's why `[talos]` exists).
|
|
95
|
+
- `[database_toga2core]` — writable 2.0 Core (already present).
|
|
66
96
|
- Apply the dbchanges2 `Team/2026-06-30a..e` + `Core/2026-06-30a` migrations in order.
|
|
67
97
|
- Verify the Bedrock KB region / data-source name against the live account (TODOs in code).
|
|
68
98
|
|
|
@@ -70,12 +100,27 @@ prompt/replacement tables that the worker2 pipeline reads at runtime.
|
|
|
70
100
|
|
|
71
101
|
- **KB list is derived from S3, not the DB.** The browse UI enumerates live S3 folders under
|
|
72
102
|
`development-team/`; `Team.KnowledgeBases` is not the source of truth for what's shown.
|
|
73
|
-
- **Delete
|
|
74
|
-
Bedrock data source.
|
|
75
|
-
-
|
|
103
|
+
- **Delete / move / edit are not just S3 ops** — each enqueues a worker `SyncKb` job to re-sync
|
|
104
|
+
the Bedrock data source.
|
|
105
|
+
- **`App_Talos_S3` needs the togaiq-account key (`[talos]`).** The tools `[aws]` key is the
|
|
106
|
+
toga-private key: it can cross-account *read* togaiq but **cannot write** it, so move/edit/delete
|
|
107
|
+
fail `AccessDenied` without a `[talos]` section.
|
|
108
|
+
- **Never use server-side `CopyObject` for togaiq** — the togaiq key is denied `CopyObject` even
|
|
109
|
+
within the same bucket; move/edit copy via get+put.
|
|
110
|
+
- **Move/edit re-sync must be one `SyncKb("source,dest")` job**, not two parallel jobs — Bedrock
|
|
111
|
+
allows only one in-flight ingestion per KB and parallel syncs race.
|
|
112
|
+
- No new secret literals introduced; credentials live in the config sections above (documented by
|
|
113
|
+
section/account, never by value).
|
|
76
114
|
|
|
77
115
|
## Change history
|
|
78
116
|
|
|
117
|
+
- 2026-07-02 — Added **move** (re-file a doc between KBs) and **edit** (title + Find&Replace + body,
|
|
118
|
+
ported from the old `kb_processor.php`) to the doc modal, each via get+put (togaiq denies
|
|
119
|
+
`CopyObject`) and each enqueuing a single `SyncKb("source,dest")` re-sync. Fixed togaiq
|
|
120
|
+
`AccessDenied`: `App_Talos_S3::client()` now uses a dedicated `[talos]` (togaiq-account) key; added
|
|
121
|
+
`[talos]`, `[worker]`, and `[aws]` sections to `config.production.ini`/`config.alpha.ini` so
|
|
122
|
+
`App_Worker::enqueue` (SQS) works. Last-modified now displayed in US Central (converted from S3
|
|
123
|
+
UTC). (jcardinal)
|
|
79
124
|
- 2026-06-30 — Built the Talos KB admin UI: `/talos/kb-documents` (S3-derived KB list, approved
|
|
80
125
|
doc browser with type-aware preview modal, download + delete-with-resync) and `/talos/vocabulary`
|
|
81
126
|
(tabbed prompt/vocabulary/replacements manager with transactional full-set reconciliation;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
| [Assortment Name Translation (AssortmentTranslations sidecar)](features/assortment-name-translation.md) | Serves Assortment (product-grouping) **names** in multiple languages by adding a per-language **sidecar** table `AssortmentTranslations`, reusing the platform's | _underscore/Model/Client/AssortmentTranslation.php, dbchanges2/Client/2026-06-26a - AssortmentTranslations.sql, dbchanges2/Core/2026-06-26a - AssortmentTranslationsRecord.sql, dbchanges2/Client/2026-06-26b - AssortmentTranslationsAcl.sql |
|
|
8
8
|
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
9
9
|
| [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
|
|
10
|
+
| [Error Reporting — Issue/Event Aggregation (exceptionHandler)](features/error-reporting-issue-event.md) | `_underscore`'s global exception handler persists every uncaught exception into a two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore | _underscore/Error.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql |
|
|
10
11
|
| [Record-Changed Event Publishing (_Event::publish to SQS)](features/event-publish-sqs.md) | `_Event::publish()` (in `_underscore/Event.php`) is the PHP side of the real-time event pipeline. | _underscore/Event.php |
|
|
11
12
|
| [Forecast.Sales NetSuite import engine (real-time webhook)](features/forecast-sale-import.md) | Real-time importer that takes a NetSuite **sale** record and writes its lines into `Forecast.Sales` (the Forecast2 revenue table). | _underscore/Component/Forecast/SaleImport/SaleImport.php, _underscore/Component/Forecast/Db/Db.php, _underscore/Component/Api/Netsuite/Netsuite.php, worker2/Worker/Netsuite/Invoice.php, worker2/Worker/Netsuite/CashSale.php, worker2/Worker/Netsuite/CreditMemo.php, worker2/Worker/Netsuite/CashRefund.php, worker2/Worker/Netsuite/JournalEntry.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Forecast/2026-06-26a - Add journalEntry to Sales transaction type enum.sql, test/@dave/test_invoice_lifecycle.php, test/@dave/test_je_lifecycle.php, test/@dave/test_creditmemo_lifecycle.php, test/@dave/test_cashsale_lifecycle.php, test/@dave/test_cashrefund_lifecycle.php, test/@dave/test_fetchrecord_routes.php, test/@dave/verify_je_classification.php, test/@dave/probe_je_accounts.php, test/@dave/probe_je_shape.php, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
|
|
12
13
|
| [Item-Fulfillment Stage Lifecycle (picked/packed/shipped) & Order Status](features/item-fulfillment-stage-lifecycle-and-order-status.md) | Every ItemFulfillment (IF) now carries an explicit **stage** — picked → packed → shipped — resolved through `ItemFulfillmentStages → ItemFulfillmentStatuses` (m | _underscore/Model/Client/SalesOrder.php, _underscore/Model/Quad/SalesOrder.php, _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/SalesOrderStatus.php, _underscore/Model/Client/SalesOrderItem.php, _underscore/Model/Client/Item.php, _underscore/Model/Client/PurchaseOrderItem.php, library/app/api/toga2.php, dbchanges2/Client/2026-06-30a - BackfillNullStageItemFulfillmentsToShipped.sql, dbchanges2/Client/2026-06-30b - SalesOrderStatusesPickedPacked.sql, dbchanges2/Client/2026-06-30c - ItemFulfillmentStageIdNotNull.sql, dbchanges2/Client_CompassCanada/2026-06-30a - ItemFulfillmentLifecycleAndShippedBackfill.sql |
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Error Reporting — Issue/Event Aggregation (exceptionHandler)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-06
|
|
10
|
+
owners: ["dfranks"]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Error.php
|
|
13
|
+
- _underscore/Model/Core/Logs/Issue.php
|
|
14
|
+
- _underscore/Model/Core/Logs/Event.php
|
|
15
|
+
- dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql
|
|
16
|
+
related:
|
|
17
|
+
- ../architecture.md
|
|
18
|
+
- ./per-client-database-connections.md
|
|
19
|
+
- ../../dbchanges2/architecture.md
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Summary
|
|
23
|
+
|
|
24
|
+
`_underscore`'s global exception handler persists every uncaught exception into a
|
|
25
|
+
two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore::DB_LOGS`,
|
|
26
|
+
models under `Model/Core/Logs/`) — **not** the per-client Logs DB. An **Issue** is the
|
|
27
|
+
deduplicated record for a distinct error (keyed by a hash of the backtrace) carrying an
|
|
28
|
+
aggregate `occurrences` counter; an **Event** is one row per individual occurrence with
|
|
29
|
+
its own stack trace and context. This is platform-wide error-reporting infrastructure,
|
|
30
|
+
not client-specific.
|
|
31
|
+
|
|
32
|
+
## Key files / entry points
|
|
33
|
+
|
|
34
|
+
- **`_underscore/Error.php`** — `exceptionHandler()` (registered via
|
|
35
|
+
`set_exception_handler`). Computes `$issueHash = md5(serialize($backtrace))`, then, only
|
|
36
|
+
when the Core-Logs register is present (`isset(\_Database::$_registers[_underscore::DB_LOGS])`),
|
|
37
|
+
upserts an `_Model_Core_Logs_Issue` and inserts an `_Model_Core_Logs_Event`, committing
|
|
38
|
+
the `DB_LOGS` transaction.
|
|
39
|
+
- **`_underscore/Model/Core/Logs/Issue.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
40
|
+
const TABLE = 'Issues';`. Fields: `id, uuid, dtCreated, dtLastOccurred, urgency, subject,
|
|
41
|
+
description, clickupIdentifier, hash, errorMessage, occurrences, trace`.
|
|
42
|
+
- **`_underscore/Model/Core/Logs/Event.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
43
|
+
const TABLE = 'Events';`. Fields: `id, uuid, issueId (FK → Issue), errorMessage, trace,
|
|
44
|
+
context, dtCreated`.
|
|
45
|
+
|
|
46
|
+
## How it works
|
|
47
|
+
|
|
48
|
+
1. On an uncaught exception, hash the backtrace to identify the distinct error.
|
|
49
|
+
2. Load the existing `Issue` by `hash`. On a miss, create it with `errorMessage`, `trace`,
|
|
50
|
+
`urgency = 'LOW'`, and `occurrences = 0`.
|
|
51
|
+
3. **Increment `occurrences`** (`($issue->occurrences ?? 0) + 1`) and set
|
|
52
|
+
`dtLastOccurred`, then save — so the counter advances on every occurrence, hit or new.
|
|
53
|
+
4. Insert one `Event` for this occurrence: `issueId`, `errorMessage`, per-occurrence
|
|
54
|
+
`trace`, and `context` (`print_r($GLOBALS, true)`).
|
|
55
|
+
5. Commit the `DB_LOGS` transaction; surface `Error {issueId}, Event {eventId}` in the
|
|
56
|
+
debug body.
|
|
57
|
+
|
|
58
|
+
The `Issues`/`Events` tables live in the shared **Core Logs** DB, provisioned from
|
|
59
|
+
`dbchanges2/Logs/` (the `Logs/` folder targets the framework-level, non-tenant `Logs` DB
|
|
60
|
+
with unqualified table names — see the dbchanges2 architecture folder→DB mapping). Contrast
|
|
61
|
+
with per-client API/error logging in `Logs_<Tenant>` via `Model/Client/Logs/*`
|
|
62
|
+
(`DB_CLIENT_LOGS`, `dbchanges2/Logs_Client/`).
|
|
63
|
+
|
|
64
|
+
## Data model
|
|
65
|
+
|
|
66
|
+
- **Issue** = one row per distinct error (dedup key = `hash`); `occurrences` is the rolling
|
|
67
|
+
count, `dtLastOccurred` the most-recent hit, `urgency` a list field, `clickupIdentifier`
|
|
68
|
+
links to an escalation ticket. There is **no `type` column** — it was dropped.
|
|
69
|
+
- **Event** = one row per occurrence, FK `issueId` → Issue, carrying the occurrence's own
|
|
70
|
+
`trace` and `context`.
|
|
71
|
+
|
|
72
|
+
## Gotchas / known issues
|
|
73
|
+
|
|
74
|
+
- **`occurrences` must be incremented, not just seeded.** It was previously set to `0` only
|
|
75
|
+
at creation and never advanced, so the aggregate counter never moved. Fixed 2026-07-06 to
|
|
76
|
+
increment on every occurrence.
|
|
77
|
+
- **`Event.trace` must be set per occurrence.** The schema has a per-Event `trace` column
|
|
78
|
+
that was never populated; now recorded on every Event.
|
|
79
|
+
- **Persistence is conditional on the Core-Logs register.** If
|
|
80
|
+
`\_Database::$_registers[_underscore::DB_LOGS]` isn't set, the whole Issue/Event block is
|
|
81
|
+
skipped silently — no error row is written.
|
|
82
|
+
- **Shared-schema migration collision.** Because `Issues`/`Events` live in the *shared* Core
|
|
83
|
+
Logs DB, any two dbchanges2 `Logs/` migrations that both `CREATE TABLE Issues`/`Events`
|
|
84
|
+
will collide — the second fails with "table already exists". Two separate error-reporting
|
|
85
|
+
workstreams introduced these same tables (one adding the escalation cron + an
|
|
86
|
+
`IssueEmailAddresses` table, one adding this error-class persistence). Only **one**
|
|
87
|
+
migration may create the shared tables; reconcile overlapping `Logs/` create-scripts at
|
|
88
|
+
merge so the tables are created exactly once and later migrations only `ALTER`.
|
|
89
|
+
|
|
90
|
+
## Change history
|
|
91
|
+
|
|
92
|
+
- 2026-07-06 — Moved Issue/Event tables and exceptionHandler persistence from the per-client
|
|
93
|
+
Logs DB to the shared Core Logs DB and dropped the `type` column; fixed `occurrences` to
|
|
94
|
+
increment each occurrence and set `Event.trace` per occurrence; removed dead commented-out
|
|
95
|
+
per-client-logs persistence block. (dfranks)
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
| [Startech Webhook Handler (worker2)](features/startech-webhook-handler.md) | Receives inbound webhook events from Startech (Easeedesk) and creates or updates the corresponding ticket in TOGA 2.0. | worker2/Worker/Startech.php |
|
|
22
22
|
| [Talos (TOGa IQ) Meeting-Notes Integration & Token Auto-Refresh (consumer)](features/talos-meeting-notes-integration.md) | How a **dev tool / agent consumes Talos (TOGa IQ)** to query the team meeting-notes corpus programmatically. | .claude/skills/plan-ticket/scripts/talos.js |
|
|
23
23
|
| [Talos Pricing Automation (worker2 Cron — AWS Actuals, Calibration, Monthly Report)](features/talos-pricing-automation.md) | The worker2 half of the **Talos Pricing Platform** (see the talos `pricing-cogs-model` and tools `talos-pricing-ui` docs for the other halves). | worker2/Worker/Talos/Pricing.php, worker2/Database/TalosPricingCrons.sql |
|
|
24
|
-
| [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | `_Worker_Team_Transcripts` (in addition to the upstream `Export` action — see [Teams Meeting Transcript Export](./teams-transcript-export.md)) now runs a fully | worker2/Worker/Team/Transcripts.php, worker2/bin/reprocess-transcripts.php, worker2/Config/production.ini, dbchanges2/Team/2026-06-30a, dbchanges2/Team/2026-06-30b, dbchanges2/Team/2026-06-30c, dbchanges2/Team/2026-06-30d, dbchanges2/Team/2026-06-30e, dbchanges2/Core/2026-06-30a |
|
|
24
|
+
| [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | `_Worker_Team_Transcripts` (in addition to the upstream `Export` action — see [Teams Meeting Transcript Export](./teams-transcript-export.md)) now runs a fully | worker2/Worker/Team/Transcripts.php, worker2/bin/reprocess-transcripts.php, worker2/bin/sync-knowledge-bases.php, worker2/Config/production.ini, dbchanges2/Team/2026-06-30a, dbchanges2/Team/2026-06-30b, dbchanges2/Team/2026-06-30c, dbchanges2/Team/2026-06-30d, dbchanges2/Team/2026-06-30e, dbchanges2/Core/2026-06-30a, dbchanges2/Core/2026-07-02a, dbchanges2/Team/2026-07-02a |
|
|
25
25
|
| [Team Sprint Management & Reporting](features/team-sprint-management.md) | `_Worker_Team_Sprint` (file `Worker/Team/Sprint.php`) is the engine behind TOGA's internal **development-sprint process and reporting**. | worker2/Worker/Team/Sprint.php |
|
|
26
26
|
| [Teams Meeting Transcript Export](features/teams-transcript-export.md) | `_Worker_Team_Transcripts` (action `Team/Transcripts/Export`) polls Microsoft Graph for Teams meeting transcripts produced by a set of organizers and archives t | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Core/2026-06-18a - Teams Transcript Export schedule.sql |
|
|
27
27
|
| [VAPI Webhook Handler (worker2 — AI-BDR end-of-call processing)](features/vapi-webhook-handler.md) | `_Worker_Vapi` ([worker2/Worker/Vapi.php](worker2/Worker/Vapi.php)) is the **PHP side of the AI-BDR call loop** — the webhook that receives VAPI's end-of-call r | worker2/Worker/Vapi.php, worker2/Worker/Ai/Bdr/Vapi.php |
|
|
@@ -6,11 +6,12 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-02
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Team/Transcripts.php
|
|
13
13
|
- worker2/bin/reprocess-transcripts.php
|
|
14
|
+
- worker2/bin/sync-knowledge-bases.php
|
|
14
15
|
- worker2/Config/production.ini
|
|
15
16
|
- dbchanges2/Team/2026-06-30a
|
|
16
17
|
- dbchanges2/Team/2026-06-30b
|
|
@@ -18,6 +19,8 @@ files:
|
|
|
18
19
|
- dbchanges2/Team/2026-06-30d
|
|
19
20
|
- dbchanges2/Team/2026-06-30e
|
|
20
21
|
- dbchanges2/Core/2026-06-30a
|
|
22
|
+
- dbchanges2/Core/2026-07-02a
|
|
23
|
+
- dbchanges2/Team/2026-07-02a
|
|
21
24
|
related:
|
|
22
25
|
- ./teams-transcript-export.md
|
|
23
26
|
- ./creating-worker-actions.md
|
|
@@ -45,9 +48,13 @@ sidecar written to `s3://togaiq/development-team/{kb_slug}/approved/` → raw ar
|
|
|
45
48
|
## Key files / entry points
|
|
46
49
|
|
|
47
50
|
- `Worker/Team/Transcripts.php` — actions `Scan`, `Process(string $sourceKey)`,
|
|
48
|
-
`SyncKb(string $kbSlug)` (
|
|
51
|
+
`SyncKb(string $kbSlug)` (comma-separated slug list), `SyncKnowledgeBases(bool $apply=true)`
|
|
52
|
+
(all alongside the existing `Export`).
|
|
49
53
|
- `bin/reprocess-transcripts.php` — standalone CLI backlog reprocessor.
|
|
50
|
-
- `
|
|
54
|
+
- `bin/sync-knowledge-bases.php` — CLI entry for the Bedrock→`Team.KnowledgeBases` registry
|
|
55
|
+
sync (`--dry-run` supported).
|
|
56
|
+
- `Config/production.ini` `[talos]` and `[teams]` sections — S3/Bedrock targets + the two
|
|
57
|
+
per-account S3 credential sets (see Configuration).
|
|
51
58
|
- DB config + ledger tables in the `Team.*` schema (see Data model), authored in dbchanges2
|
|
52
59
|
under `Team/2026-06-30a..e` + `Core/2026-06-30a`.
|
|
53
60
|
|
|
@@ -61,28 +68,56 @@ each via `_Worker::runTask('Team/Transcripts/Process', ['sourceKey' => …])`. S
|
|
|
61
68
|
|
|
62
69
|
### `Process(string $sourceKey)`
|
|
63
70
|
Port of `kb_processor`, per transcript:
|
|
64
|
-
1. Fetch the raw VTT from S3 (`toga-private`, **us-west-2**).
|
|
71
|
+
1. Fetch the raw VTT from S3 (`toga-private`, **us-west-2**). Parse **date + meeting title up
|
|
72
|
+
front** (previously parsed from the filename only *after* classification).
|
|
65
73
|
2. Call the TOGa IQ AI endpoint `https://api.togaiq.com/api/ai/generate`, model
|
|
66
74
|
`bedrock/us.anthropic.claude-haiku-4-5-20251001-v1:0`, with an **extended** `output_schema`
|
|
67
75
|
returning **both** `knowledge_doc` **and** `kb_slug` — the AI picks the single best active KB
|
|
68
|
-
from the supplied list, or `general`. The
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
from the supplied list, or `general`. The **meeting title is passed into**
|
|
77
|
+
`callAICleaningAPI` and surfaced as a `MEETING TITLE:` block; the classifier instruction
|
|
78
|
+
**prioritizes the title** (often the only KB signal on short transcripts). **Classification
|
|
79
|
+
rule: customer over company** — `toga-technology` is our *own* company, so a meeting involving
|
|
80
|
+
both a customer and TOGA staff must classify to the **customer** KB; pick `toga-technology`
|
|
81
|
+
only for purely-internal meetings; fall back to `general` when neither can be determined. The
|
|
82
|
+
system prompt is **assembled at runtime** from DB tables: a template body with
|
|
83
|
+
`{{APPLICATIONS}}`/`{{CLIENTS}}`/`{{PEOPLE}}`/`{{BUSINESS_TERMS}}`/`{{EXAMPLES}}`
|
|
84
|
+
placeholders filled from `Team.TranscriptPromptTerms`. The active template body also
|
|
85
|
+
**excludes personal/social/non-work content** and must never emit a "Personal Notes" section
|
|
86
|
+
(see Change history / the 2026-07-02a Team migration).
|
|
71
87
|
3. Apply DB-driven text replacements from `Team.TranscriptReplacements` (`:c` case-sensitive,
|
|
72
88
|
`:w` whole-word, `:cw`) via `preg_replace_callback` **so replacement backrefs stay literal**.
|
|
73
89
|
4. Build the approved name `"{YYYY-MM-DD} - {Title Case}.txt"`.
|
|
74
|
-
5. Upload to `s3://togaiq/development-team/{kb_slug}/approved/` (**us-east-1
|
|
75
|
-
`{name}.txt.metadata.json` Bedrock sidecar.
|
|
76
|
-
6. Archive the raw VTT to `…/{kb_slug}/archive/{date}
|
|
90
|
+
5. Upload to `s3://togaiq/development-team/{kb_slug}/approved/` (**us-east-1**, togaiq account
|
|
91
|
+
client) plus a `{name}.txt.metadata.json` Bedrock sidecar.
|
|
92
|
+
6. Archive the raw VTT to `…/{kb_slug}/archive/{date}/` via **get(raw) + put(talos)** (NOT
|
|
93
|
+
`CopyObject` — source and dest are in **different AWS accounts**, so cross-account
|
|
94
|
+
`CopyObject` is impossible), then delete the source.
|
|
77
95
|
7. AWS Bedrock `startIngestionJob` on that KB's data source (retry 5×30s on
|
|
78
96
|
`ConflictException` / throttling). **If `kb_slug === 'general'`, sync ALL active KBs.**
|
|
79
97
|
|
|
98
|
+
### `SyncKnowledgeBases(bool $apply=true)` (cron)
|
|
99
|
+
Keeps `Team.KnowledgeBases` current with **Bedrock as the source of truth** — the table only
|
|
100
|
+
had the KBs that existed as S3 folders at build time (`general`, `office-depot`), so the AI
|
|
101
|
+
classifier could never pick a KB (e.g. `elite`) that wasn't already a candidate. Lists all
|
|
102
|
+
Bedrock KBs (`listKnowledgeBases`, via helper `listAllKnowledgeBases()`), filters to the
|
|
103
|
+
`{kb_prefix}*` prefix (`development-team-`), derives the slug, and **upserts**: new rows insert
|
|
104
|
+
`isActive=1` (`isGeneral=1` only for `general`), name title-cased, `bedrockKbId` cached; existing
|
|
105
|
+
rows keep their name/flags and only refresh `bedrockKbId`. **Nothing is deleted or deactivated.**
|
|
106
|
+
CLI entry `bin/sync-knowledge-bases.php` (`--dry-run`). Cron: `Core.CronJobs` row action
|
|
107
|
+
`Team/Transcripts/SyncKnowledgeBases`, schedule `0 5 * * *` (daily 05:00 CT), `maxExecutionTime`
|
|
108
|
+
300 (dbchanges2 `Core/2026-07-02a`; depends on the 2026-05-14 `maxExecutionTime` migration).
|
|
109
|
+
|
|
80
110
|
Idempotency + status are tracked in `Team.TranscriptProcessing`
|
|
81
111
|
(`pending/cleaned/uploaded/archived/synced/failed`).
|
|
82
112
|
|
|
83
113
|
### `SyncKb(string $kbSlug)`
|
|
84
|
-
On-demand
|
|
114
|
+
On-demand re-sync used by the Tools delete/move/edit flows (see
|
|
85
115
|
[Talos KB Documents Admin](../../../1.0/apps/tools/features/talos-kb-documents-admin.md)).
|
|
116
|
+
`$kbSlug` now accepts a **comma-separated slug list** and syncs each KB **sequentially** with
|
|
117
|
+
`KB_SYNC_DELAY_SECONDS` spacing. This exists because **Bedrock allows only one in-flight
|
|
118
|
+
ingestion per KB** — a move that fired two independent parallel `SyncKb` workers raced and one
|
|
119
|
+
was dropped (only the destination synced). The Tools move now enqueues a **single**
|
|
120
|
+
`SyncKb("source,dest")` job so both KBs sync in one job, in order.
|
|
86
121
|
|
|
87
122
|
### `bin/reprocess-transcripts.php` (CLI backlog reprocessor)
|
|
88
123
|
Standalone CLI that bootstraps `_underscore`, lists existing raw transcripts under the
|
|
@@ -109,7 +144,10 @@ PKs `INT UNSIGNED` to match the existing Talos Team-table family.
|
|
|
109
144
|
- **`Team.TranscriptPromptTemplate`** — `name, bodyText` (w/ placeholders), `instruction, model,
|
|
110
145
|
temperature, maxTokens, timeoutSeconds, isActive`. One active row seeded from the `.ini` system prompt.
|
|
111
146
|
- **`Team.KnowledgeBases`** — `slug, name, isGeneral, bedrockKbId, isActive`. Seeded `general` +
|
|
112
|
-
`office-depot
|
|
147
|
+
`office-depot`, but now **auto-kept-current from Bedrock** by the daily `SyncKnowledgeBases`
|
|
148
|
+
action (Bedrock is the source of truth; the table is a cache/allowlist of classifier
|
|
149
|
+
candidates). NOTE: the Tools *browse* UI still derives its live KB list from S3 folders, not
|
|
150
|
+
this table.
|
|
113
151
|
- **`Team.TranscriptProcessing`** — `sourceKey UNIQUE(255), kbSlug, approvedKey, archiveKey,
|
|
114
152
|
status` ENUM (`pending/cleaned/uploaded/archived/synced/failed`), `aiModel, failureReason,
|
|
115
153
|
dtProcessed`. Pipeline ledger + idempotency.
|
|
@@ -122,10 +160,22 @@ PKs `INT UNSIGNED` to match the existing Talos Team-table family.
|
|
|
122
160
|
`worker2/Config/production.ini` `[talos]`: `s3_bucket=togaiq`, `s3_region=us-east-1`,
|
|
123
161
|
`bedrock_region=us-east-1`, `kb_prefix=development-team-`, `s3_root_path=development-team/`.
|
|
124
162
|
|
|
163
|
+
**Two S3 credential sets — one per AWS account (see the cross-account gotcha):**
|
|
164
|
+
- `[teams]` `s3_access_key_id` / `s3_secret_access_key` — the **toga-private** key (raw
|
|
165
|
+
transcripts, us-west-2). Read by `s3Client()` / `getS3Client()`.
|
|
166
|
+
- `[talos]` `s3_access_key_id` / `s3_secret_access_key` — the **togaiq** key (approved/archive
|
|
167
|
+
KB bucket, us-east-1). Read by `getTalosS3Client()` and `getBedrockClient()`.
|
|
168
|
+
|
|
169
|
+
Credential **values live only in the deployed `production.ini`** — documented here by section
|
|
170
|
+
and account, never by value. Pre-existing plaintext keys should be rotated (separate track).
|
|
171
|
+
|
|
125
172
|
## Gotchas / known issues
|
|
126
173
|
|
|
127
|
-
- **Cross-
|
|
128
|
-
|
|
174
|
+
- **Cross-ACCOUNT S3 (root cause of repeated PutObject/CopyObject `AccessDenied`).** `toga-private`
|
|
175
|
+
(raw, us-west-2) and `togaiq` (KB bucket, us-east-1) live in **different AWS accounts** — **no
|
|
176
|
+
single IAM key can write to both.** Credentials are therefore **per-bucket**: `[teams]` key for
|
|
177
|
+
toga-private, `[talos]` key for togaiq. Because it spans accounts, **`CopyObject` is impossible**;
|
|
178
|
+
the archive step is a **get(raw) + put(talos)**, not a server-side copy.
|
|
129
179
|
- **`[talos]` keys were added to `production.ini` only** — the beta/development configs still
|
|
130
180
|
need them before the pipeline runs in those environments.
|
|
131
181
|
- **`general` KB fans out.** A `Process()` classified as `general` (or a `general` sync) triggers
|
|
@@ -143,6 +193,15 @@ PKs `INT UNSIGNED` to match the existing Talos Team-table family.
|
|
|
143
193
|
|
|
144
194
|
## Change history
|
|
145
195
|
|
|
196
|
+
- 2026-07-02 — Fixed cross-**account** S3 creds: `toga-private` and `togaiq` are separate AWS
|
|
197
|
+
accounts, so credentials are now per-bucket (`[teams]` vs `[talos]` in `production.ini`) and the
|
|
198
|
+
archive step is get+put instead of the impossible cross-account `CopyObject`. Built
|
|
199
|
+
`SyncKnowledgeBases` (Bedrock→`Team.KnowledgeBases` upsert) + `bin/sync-knowledge-bases.php` and a
|
|
200
|
+
daily `0 5 * * *` cron so the classifier's candidate KB list stays current. `SyncKb` now takes a
|
|
201
|
+
comma-separated slug list and syncs sequentially (Bedrock allows one in-flight ingestion per KB;
|
|
202
|
+
parallel syncs raced). Classification: meeting title now passed to and prioritized by the AI, and
|
|
203
|
+
customer-over-company (a customer meeting stays with the customer even when TOGA staff attend).
|
|
204
|
+
Prompt template surgically updated to exclude personal/social content ("Personal Notes"). (jcardinal)
|
|
146
205
|
- 2026-06-30 — Built the automated cron-driven ingestion pipeline (`Scan`/`Process`/`SyncKb`) +
|
|
147
206
|
the `bin/reprocess-transcripts.php` backlog CLI, porting the manual `kb_processor.php`. Added
|
|
148
207
|
the `Team.*` config/ledger schema (DB-editable replacements, prompt terms, prompt template, KB
|
package/knowledge/INDEX.md
CHANGED
|
@@ -16,7 +16,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
16
16
|
|
|
17
17
|
## 2.0 framework
|
|
18
18
|
|
|
19
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
19
|
+
- **_underscore** (_Underscore) _(framework core)_ — 22 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
20
20
|
- **worker2** (Worker) — 25 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
21
21
|
- **api2** (API) — 7 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
22
22
|
- **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
package/package.json
CHANGED