toga-ai 1.0.301 → 1.0.302
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/features/talos-kb-documents-admin.md +15 -2
- package/knowledge/2.0/apps/_underscore/features/async-query-execution.md +22 -1
- package/knowledge/2.0/apps/worker2/INDEX.md +3 -3
- package/knowledge/2.0/apps/worker2/architecture.md +22 -1
- package/knowledge/2.0/apps/worker2/features/notification-email-template.md +26 -4
- package/knowledge/2.0/apps/worker2/features/talos-transcript-ingestion.md +153 -154
- package/knowledge/2.0/apps/worker2/features/teams-transcript-export.md +18 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: Tools
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- tools/mvc/talos/kb-documents/get.php
|
|
@@ -68,11 +68,18 @@ Classic view → Edit → Save per tab, with **full-set reconciliation** (transa
|
|
|
68
68
|
back on error), trash-icon row removal, and an auto-dismissing toast. Backs the `Team.*`
|
|
69
69
|
prompt/replacement tables that the worker2 pipeline reads at runtime.
|
|
70
70
|
|
|
71
|
+
**Encoding (fixed 2026-07-09):** terms were stored HTML-encoded and then re-escaped at render,
|
|
72
|
+
so they displayed **double-encoded** (e.g. "Grand & Toy"). `vocabulary/post.php` now
|
|
73
|
+
`html_entity_decode()`s values on save so the DB stores **raw** text; `vocabulary/get.php`
|
|
74
|
+
already escapes once at display. Do not re-add encoding on save.
|
|
75
|
+
|
|
71
76
|
## Helpers
|
|
72
77
|
|
|
73
78
|
- **`App_Talos_S3`** (`_/app/talos/s3.php`) — S3 client; list KB slugs; list/count approved
|
|
74
79
|
(UTC→Central for display); presigned URL; get/delete; `moveApprovedDocument`;
|
|
75
|
-
`updateApprovedDocument`; `sanitizeTitle
|
|
80
|
+
`updateApprovedDocument`; `sanitizeTitle` (**now converts `/` and `\` to hyphens** — it
|
|
81
|
+
previously converted them to spaces, which let a slashed title create an unintended S3
|
|
82
|
+
sub-folder; matches worker2's `filenameSafeTitle()`); `copyWithinBucket`; `putObjectBody`; slug
|
|
76
83
|
sanitize+lowercase; approved-prefix path guard. **`client()` credential resolution:** prefers a
|
|
77
84
|
dedicated **`[talos]`** config section (the **togaiq**-capable key), falling back to `[aws]`, then
|
|
78
85
|
the SDK default chain — because `App_Talos_S3` only ever talks to **togaiq**, so it needs the
|
|
@@ -114,6 +121,12 @@ prompt/replacement tables that the worker2 pipeline reads at runtime.
|
|
|
114
121
|
|
|
115
122
|
## Change history
|
|
116
123
|
|
|
124
|
+
- 2026-07-09 — Fixed `App_Talos_S3::sanitizeTitle()` to convert `/`,`\` to hyphens (was
|
|
125
|
+
spaces) so a slashed meeting title no longer creates a stray S3 sub-folder — matches
|
|
126
|
+
worker2's `filenameSafeTitle()`; `updateApprovedDocument()` already renames the S3 object and
|
|
127
|
+
updates the metadata-sidecar filename on a title edit. Fixed the vocabulary UI double-encoding
|
|
128
|
+
bug: `vocabulary/post.php` now `html_entity_decode()`s on save so the DB stores raw text
|
|
129
|
+
(`get.php` escapes once at display). (jcardinal)
|
|
117
130
|
- 2026-07-02 — Added **move** (re-file a doc between KBs) and **edit** (title + Find&Replace + body,
|
|
118
131
|
ported from the old `kb_processor.php`) to the doc modal, each via get+put (togaiq denies
|
|
119
132
|
`CopyObject`) and each enqueuing a single `SyncKb("source,dest")` re-sync. Fixed togaiq
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-09
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Query.php
|
|
@@ -74,6 +74,23 @@ and is a harmless no-op.
|
|
|
74
74
|
connection and started a local transaction it never committed. Fixed by moving the async
|
|
75
75
|
dispatch to the **top** of `execute()`, before any read/write connection or transaction setup.
|
|
76
76
|
|
|
77
|
+
## When to use async vs. synchronous (usage rule)
|
|
78
|
+
|
|
79
|
+
Async is a **workaround for a stale connection after a long HTTP call**, not a
|
|
80
|
+
general-purpose "make writes faster" switch. Use it **only** for writes that happen *after* a
|
|
81
|
+
long-running (e.g. up to 600s) external call has left the local connection idle enough to hit
|
|
82
|
+
"MySQL server has gone away" — for example the **post-AI `setStatus` writes** in the Talos
|
|
83
|
+
transcript pipeline.
|
|
84
|
+
|
|
85
|
+
**Do NOT use async for a write another poll/step must read back.** An async write is not
|
|
86
|
+
durable on the caller's connection when control returns — it commits later, on the worker's
|
|
87
|
+
connection. In the Talos pipeline the **pre-AI dedupe / discovery-ledger writes**
|
|
88
|
+
(`recordExport`, `loadOrCreateProcessing` INSERT) were originally async and the rows weren't
|
|
89
|
+
durable before the next poll, so dedupe and the watermark failed and meetings reprocessed.
|
|
90
|
+
Those writes were changed to **synchronous + `_Database::transactionCommit(...)`**. Rule of
|
|
91
|
+
thumb: **read-back-before-worker-runs → synchronous+committed; fire-and-forget-after-slow-call
|
|
92
|
+
→ async.** A stale read after such a write may also need `_Database::useQueryCache(false)`.
|
|
93
|
+
|
|
77
94
|
## Known open items
|
|
78
95
|
|
|
79
96
|
- **SECURITY (open, developer aware).** DB username/password are currently sent in **plaintext**
|
|
@@ -83,6 +100,10 @@ and is a harmless no-op.
|
|
|
83
100
|
|
|
84
101
|
## Change history
|
|
85
102
|
|
|
103
|
+
- 2026-07-09 — Added the sync-vs-async usage rule (async is only for fire-and-forget writes
|
|
104
|
+
after a long external call that staled the connection; writes another step must read back
|
|
105
|
+
must stay synchronous+committed). Prompted by the Talos pipeline, where async pre-AI dedupe
|
|
106
|
+
writes weren't durable before the next poll and caused reprocessing. (jcardinal)
|
|
86
107
|
- 2026-07-07 — Built writes-only async query execution (`_Query` `isAsync` → Worker
|
|
87
108
|
`Infrastructure/Database/Query` action); fixed four correctness bugs found in review
|
|
88
109
|
(constructor order, worker-side commit, double-dispatch sentinel, dangling local
|
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
| [NetSuite ↔ ClickUp / TOGA Opportunity Sync (API Message Queue + worker2 webhook)](features/netsuite-opportunity-sync.md) | Outbound sync from NetSuite to TOGA for the record types the Forecast2 importer pulls (opportunities first; sales/items/etc. | worker2/Worker/Netsuite.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Clickup.php, worker2/Worker/Clickup/Opportunity.php, worker2/Controller/Index.php, _underscore/Worker.php, test/@dave/NetSuite/api-message-queue/lib_amq_queue.js, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/ue_amq_drain.js, test/@dave/NetSuite/api-message-queue/ss_amq_drain.js, test/@dave/NetSuite/api-message-queue/DEPLOY_RUNBOOK.md, test/@dave/clickup/backfill_opportunity_numbers.php, test/@dave/clickup/probe_opportunity_fields.php, test/@dave/probe_clickup_desc_match.php, test/@dave/test_model_load_behavior.php, dbchanges2/Forecast/2026-06-25a - Add unique index on Opportunities netsuiteOpportunityInternalId.sql, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
19
19
|
| [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, test/@dave/probe_open_order_lines.php, test/@dave/check_so_status.php, test/@dave/check_so_history.php, test/@dave/probe_so_rest_lines.php, test/@dave/probe_missing_oo_timing.php, test/@dave/probe_missing_oo_createdby.php, test/@dave/probe_drift_so_dates.php, test/@dave/probe_open_order_gating.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
20
20
|
| [NetSuite Supporting-Record Webhook Importer (the reusable recipe)](features/netsuite-supporting-record-webhook-importer.md) | A single **repeatable recipe** for porting a legacy daily-pull NetSuite *supporting-record* importer (the lookup/dimension tables behind Forecast2 — Employees, | worker2/Worker/Netsuite/Employee.php, worker2/Worker/Netsuite/Account.php, worker2/Worker/Netsuite/Classification.php, worker2/Worker/Netsuite/Customer.php, worker2/Worker/Netsuite/Item.php, worker2/Worker/Netsuite.php, _underscore/Model/Forecast/Employee.php, _underscore/Model/Forecast/Account.php, _underscore/Model/Forecast/Classification.php, _underscore/Component/Forecast/Db/Db.php, test/@dave/test_employee_lifecycle.php, test/@dave/test_account_lifecycle.php, test/@dave/test_classification_lifecycle.php, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, worker/crons/toga2/forecast2/import_supporting_records.php |
|
|
21
|
-
| [Background Email-Template Worker (_Worker_Notification_EmailTemplate)](features/notification-email-template.md) | `_Worker_Notification_EmailTemplate::Send(...)` dispatches a **stored, client-defined `EmailTemplates` row off-thread** as a background WorkerJob. | worker2/Worker/Notification/EmailTemplate.php, _underscore/Model/Client/EmailTemplate.php |
|
|
21
|
+
| [Background Email-Template Worker (_Worker_Notification_EmailTemplate)](features/notification-email-template.md) | `_Worker_Notification_EmailTemplate::Send(...)` dispatches a **stored, client-defined `EmailTemplates` row off-thread** as a background WorkerJob. | worker2/Worker/Notification/EmailTemplate.php, worker2/Worker/Client/True.php, _underscore/Model/Client/EmailTemplate.php |
|
|
22
22
|
| [DB-Driven Notification (Internal) Email](features/notification-email.md) | Internal/notification emails (merge-conflict alerts, ops notices — anything system-generated, not client-facing transactional mail) are sent through one worker | worker2/Worker/Notification/Email.php, _underscore/Model/Client/EmailTemplate.php, dbchanges2/Client/2026-06-23a - EmailTemplateWrapper.sql, dbchanges2/Client_True/2026-06-23a - EmailTemplateWrapper.sql |
|
|
23
23
|
| [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 |
|
|
24
24
|
| [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 |
|
|
25
25
|
| [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 |
|
|
26
|
-
| [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | `_Worker_Team_Transcripts`
|
|
26
|
+
| [Talos Transcript Ingestion Pipeline (worker2 → AWS Bedrock KBs)](features/talos-transcript-ingestion.md) | `_Worker_Team_Transcripts` runs a fully automated, cron-driven pipeline that ingests raw Teams transcripts into the **Talos / TOGa IQ** AWS Bedrock knowledge ba | worker2/Worker/Team/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, dbchanges2/Team/2026-07-08a, dbchanges2/Team/2026-07-09a |
|
|
27
27
|
| [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 |
|
|
28
|
-
| [Teams Meeting Transcript Export](features/teams-transcript-export.md) |
|
|
28
|
+
| [Teams Meeting Transcript Export](features/teams-transcript-export.md) | > **SUPERSEDED (2026-07-09) — the S3-staging model below is history.** `Export` is now a thin > **GRAPH-DIRECT** cron poller: it no longer archives raw VTT to ` | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Core/2026-06-18a - Teams Transcript Export schedule.sql |
|
|
29
29
|
| [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 |
|
|
30
30
|
| [WJE Freshservice Sync (worker2)](features/wje-freshservice-sync.md) | WJE ("WJE IT", helpdesk `wje.freshservice.com`) is a **Freshservice**-based help-desk client whose tickets, contacts, assets, groups, categories, and canned res | worker2/Worker/Wje.php, _underscore/Component/Api/Wje/Wje.php, _underscore/Model/Wje/Ticket.php, _underscore/Model/Wje/TicketNote.php, _underscore/Model/Wje/Contact.php, _underscore/Model/Wje/Unit.php, _underscore/Model/Wje/TicketTeam.php, _underscore/Model/Wje/TicketCategory.php, _underscore/Model/Wje/AssetType.php, _underscore/Model/Wje/PredefinedReply.php, library/app/api/wje.php, worker/crons/toga2/wje/import_supporting_records.php, worker/crons/toga2/wje/sync_togasupply_wje.php, worker/crons/notifications/reports/wje/wje_common.php, library/app/systemmonitor/wje.php, dbchanges2/Client_Wje/2024-10-04 - WjeOnboarding.sql |
|
|
31
31
|
| [Ticket → ClickUp Pseudocode Planning (Talos-grounded)](workflows/ticket-to-pseudocode-planning.md) | A repeatable procedure for turning a ClickUp ticket into a reviewed, formatted implementation plan posted back to the ticket's `📝 Pseudocode` custom field. | test/@dave/clickup_md2delta.js |
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Controller/Index.php
|
|
@@ -154,6 +154,23 @@ worker tier creates the row itself on pickup.) Reason: the caller's MySQL transa
|
|
|
154
154
|
open when SQS delivers; the worker reads in a separate connection and, if uncommitted, its
|
|
155
155
|
SELECT returns nothing and the guard fires ("already processed or not found — skipping").
|
|
156
156
|
|
|
157
|
+
## Long-running AI actions — sync vs. async writes
|
|
158
|
+
|
|
159
|
+
A worker action that makes a long (up to 600s) AI/HTTP call leaves its **local MySQL
|
|
160
|
+
connection idle** long enough to hit "MySQL server has gone away" on the *next* write. The
|
|
161
|
+
pattern that works (proven in the Talos transcript pipeline):
|
|
162
|
+
|
|
163
|
+
- **Writes that a later poll/step must read back go BEFORE the AI call, synchronous +
|
|
164
|
+
committed.** e.g. the transcript dedupe/discovery-ledger inserts. If these are dispatched
|
|
165
|
+
async (via `_Query` writes-only async → `Infrastructure/Database/Query`) the row is **not
|
|
166
|
+
durable** when control returns, so the next cron poll re-selects and reprocesses the same
|
|
167
|
+
item. A stale read immediately after may also need `_Database::useQueryCache(false)`.
|
|
168
|
+
- **Writes AFTER the AI call (e.g. `setStatus`) go async** — the connection is already stale,
|
|
169
|
+
and these are fire-and-forget status updates nobody reads back synchronously.
|
|
170
|
+
|
|
171
|
+
See [async query execution](../_underscore/features/async-query-execution.md) for the `_Query`
|
|
172
|
+
`isAsync` mechanism and its correctness gotchas.
|
|
173
|
+
|
|
157
174
|
## Key design decisions
|
|
158
175
|
|
|
159
176
|
MySQL-first for the **Lambda/webhook/cron ingestion paths** (SQS on success) prevents lost
|
|
@@ -194,6 +211,10 @@ the MySQL-first design was departed from **on purpose** here.
|
|
|
194
211
|
|
|
195
212
|
## Change history
|
|
196
213
|
|
|
214
|
+
- 2026-07-09 — Added the "long-running AI actions — sync vs. async writes" rule: reads-back
|
|
215
|
+
writes go before the AI call, synchronous+committed; post-AI status writes go async.
|
|
216
|
+
Distilled from the Talos Graph-direct pipeline (async pre-AI dedupe writes weren't durable
|
|
217
|
+
before the next poll → reprocessing). (jcardinal)
|
|
197
218
|
- 2026-06-08 — Initial worker2 architecture doc. (jcardinal)
|
|
198
219
|
- 2026-07-07 — `_Worker::runTask()` moved to SQS-first (caller does no MySQL; worker tier
|
|
199
220
|
creates/tracks the row on pickup); the former debug-only direct-payload path in
|
|
@@ -6,10 +6,11 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["mhammontree"]
|
|
9
|
+
updated: 2026-07-09
|
|
10
|
+
owners: ["mhammontree", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Notification/EmailTemplate.php
|
|
13
|
+
- worker2/Worker/Client/True.php
|
|
13
14
|
- _underscore/Model/Client/EmailTemplate.php
|
|
14
15
|
related:
|
|
15
16
|
- ./notification-email.md
|
|
@@ -36,7 +37,8 @@ silently riding inside — the originating transaction.
|
|
|
36
37
|
|
|
37
38
|
- `worker2/Worker/Notification/EmailTemplate.php` —
|
|
38
39
|
`abstract class _Worker_Notification_EmailTemplate`. One method:
|
|
39
|
-
`public static Send(string $clientIdentifier, string $uuid, $to, $cc, $bcc, ...$args): string`.
|
|
40
|
+
`public static Send(string $clientIdentifier, string $uuid, string|array $to = [], string|array $cc = [], string|array $bcc = [], ...$args): string`.
|
|
41
|
+
`to`/`cc`/`bcc` accept a string or array and **default to `[]`** when omitted.
|
|
40
42
|
- Resolves the numeric `clientId` from `$clientIdentifier` via a Core query.
|
|
41
43
|
- Registers the client DB with
|
|
42
44
|
`_Database::registerClientDatabases($clientId, $environment)` (see the DB-registration
|
|
@@ -61,7 +63,21 @@ silently riding inside — the originating transaction.
|
|
|
61
63
|
pattern: `_Worker_Startech`). See
|
|
62
64
|
[`creating-worker-actions.md`](./creating-worker-actions.md).
|
|
63
65
|
3. **Dispatch the template.** `_Model_Client_EmailTemplate::send()` loads the `EmailTemplates`
|
|
64
|
-
row by UUID, substitutes `{placeholder}` vars from `...$args
|
|
66
|
+
row by UUID, substitutes `{placeholder}` vars from `...$args` into **both the stored subject
|
|
67
|
+
and body** (the body may contain HTML), and sends via `_Email`.
|
|
68
|
+
4. **Failure handling.** A failed send **THROWS** and is recorded as a failed `WorkerJobs` row
|
|
69
|
+
(`isSuccess=0`) with **no retry**; the failure never affects whatever enqueued it (the send
|
|
70
|
+
is fully decoupled from the originating transaction).
|
|
71
|
+
|
|
72
|
+
### TOGA Technology (`True`) template
|
|
73
|
+
|
|
74
|
+
The TOGA Technology template constant lives in worker2:
|
|
75
|
+
`_Worker_Client_True::EMAIL_TEMPLATE_UUID__TOGA_TECHNOLOGY = '232d4edb-c2fa-4a8b-b5b9-d5800c962e19'`
|
|
76
|
+
(`worker2/Worker/Client/True.php`); its `clientIdentifier` is `'True'`. The stored template
|
|
77
|
+
must define `{subject}` and `{body}` placeholders (the body may be HTML). This is the template
|
|
78
|
+
the **Talos transcript recap email** uses — see
|
|
79
|
+
[Talos Transcript Ingestion](./talos-transcript-ingestion.md). (The UUID is a template
|
|
80
|
+
identifier, not a secret.)
|
|
65
81
|
|
|
66
82
|
### Enqueuing it
|
|
67
83
|
|
|
@@ -86,6 +102,12 @@ flow into `...$args` as the template variables.
|
|
|
86
102
|
|
|
87
103
|
## Change history
|
|
88
104
|
|
|
105
|
+
- 2026-07-09 — Documented the full `Send` signature (`to`/`cc`/`bcc` default `[]`; extra
|
|
106
|
+
string-keyed params spread as named args into `...$args` → `{placeholder}` subs in subject
|
|
107
|
+
and body, body may be HTML) and the failure contract (throws → failed `WorkerJobs` row,
|
|
108
|
+
`isSuccess=0`, no retry, never affects the enqueuer). Added the TOGA Technology (`True`)
|
|
109
|
+
template constant `_Worker_Client_True::EMAIL_TEMPLATE_UUID__TOGA_TECHNOLOGY` — the template
|
|
110
|
+
the Talos recap email sends through. (jcardinal)
|
|
89
111
|
- 2026-06-30 — Built the generic background email-template worker
|
|
90
112
|
`_Worker_Notification_EmailTemplate::Send` (TRUE-79251): resolves the client, registers the
|
|
91
113
|
client DB via `registerClientDatabases`, and dispatches a stored `EmailTemplates` row by
|
|
@@ -6,11 +6,10 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-09
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Team/Transcripts.php
|
|
13
|
-
- worker2/bin/reprocess-transcripts.php
|
|
14
13
|
- worker2/bin/sync-knowledge-bases.php
|
|
15
14
|
- worker2/Config/production.ini
|
|
16
15
|
- dbchanges2/Team/2026-06-30a
|
|
@@ -21,197 +20,197 @@ files:
|
|
|
21
20
|
- dbchanges2/Core/2026-06-30a
|
|
22
21
|
- dbchanges2/Core/2026-07-02a
|
|
23
22
|
- dbchanges2/Team/2026-07-02a
|
|
23
|
+
- dbchanges2/Team/2026-07-08a
|
|
24
|
+
- dbchanges2/Team/2026-07-09a
|
|
24
25
|
related:
|
|
25
26
|
- ./teams-transcript-export.md
|
|
27
|
+
- ./notification-email-template.md
|
|
26
28
|
- ./creating-worker-actions.md
|
|
27
29
|
- ../architecture.md
|
|
30
|
+
- ../../_underscore/features/async-query-execution.md
|
|
28
31
|
- ../../../1.0/apps/tools/features/talos-kb-documents-admin.md
|
|
29
32
|
- ../../../1.0/apps/test/features/talos-kb-pipeline.md
|
|
30
33
|
---
|
|
31
34
|
|
|
32
35
|
## Summary
|
|
33
36
|
|
|
34
|
-
`_Worker_Team_Transcripts`
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
`_Worker_Team_Transcripts` runs a fully automated, cron-driven pipeline that ingests raw
|
|
38
|
+
Teams transcripts into the **Talos / TOGa IQ** AWS Bedrock knowledge bases and emails a
|
|
39
|
+
recap to the meeting organizer. **As of 2026-07-09 the pipeline is GRAPH-DIRECT**: there is
|
|
40
|
+
**no S3 staging bucket** in the loop anymore. `Export()` is a thin cron poller that pulls
|
|
41
|
+
transcript references from Microsoft Graph and enqueues one `Process` job each; `Process()`
|
|
42
|
+
downloads the VTT straight from Graph and does everything in-memory. The old
|
|
43
|
+
`s3://toga-private/transcripts/` staging bucket is **no longer used at all**, the `Scan()`
|
|
44
|
+
action was **removed**, and `bin/reprocess-transcripts.php` was **deleted** (the `Export`
|
|
45
|
+
cron replaces it).
|
|
46
|
+
|
|
47
|
+
Flow: `Export` (cron) polls Graph per configured organizer → records a discovery-ledger row
|
|
48
|
+
in `Team.TranscriptExports` → enqueues one `Process` job per transcript → `Process` downloads
|
|
49
|
+
the VTT from Graph, strips+groups it, AI clean+classify (pass 1), applies DB replacements,
|
|
50
|
+
uploads approved `.txt` + metadata sidecar to `togaiq`, archives the raw VTT to `togaiq` from
|
|
51
|
+
the in-memory copy → Bedrock `startIngestionJob` → a **second** AI pass returns structured
|
|
52
|
+
JSON that PHP renders to an inline-styled HTML recap email, enqueued to the organizer.
|
|
47
53
|
|
|
48
54
|
## Key files / entry points
|
|
49
55
|
|
|
50
|
-
- `Worker/Team/Transcripts.php` — actions `
|
|
51
|
-
`
|
|
52
|
-
(all alongside the existing `Export`).
|
|
53
|
-
- `bin/reprocess-transcripts.php` — standalone CLI backlog reprocessor.
|
|
56
|
+
- `Worker/Team/Transcripts.php` — actions `Export`, `Process`, `SyncKb`,
|
|
57
|
+
`SyncKnowledgeBases`. **`Scan` was removed.**
|
|
54
58
|
- `bin/sync-knowledge-bases.php` — CLI entry for the Bedrock→`Team.KnowledgeBases` registry
|
|
55
59
|
sync (`--dry-run` supported).
|
|
56
60
|
- `Config/production.ini` `[talos]` and `[teams]` sections — S3/Bedrock targets + the two
|
|
57
|
-
per-account S3 credential sets (see Configuration).
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
per-account S3 credential sets (see Configuration). Note the **toga-private raw read path
|
|
62
|
+
is gone**; the `[teams]` S3 key is no longer used by the ingestion loop.
|
|
63
|
+
- DB config + ledger tables in the `Team.*` schema (see Data model).
|
|
60
64
|
|
|
61
65
|
## How it works
|
|
62
66
|
|
|
63
|
-
### `
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
each
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
(`
|
|
67
|
+
### `Export(int $lookbackDays, ?int $limit = null, array $cc = [], array $bcc = [])` (cron)
|
|
68
|
+
Thin poller. The old `organizers` parameter was **REMOVED** — organizers are always resolved
|
|
69
|
+
from config now.
|
|
70
|
+
1. For each configured organizer, poll Microsoft Graph for transcripts in the lookback window.
|
|
71
|
+
2. Resolve the organizer email (see *Organizer email resolution*).
|
|
72
|
+
3. Record a discovery-ledger row in `Team.TranscriptExports` — **synchronously, then
|
|
73
|
+
committed** (see the durability gotcha).
|
|
74
|
+
4. Enqueue one `Process` job per transcript.
|
|
75
|
+
|
|
76
|
+
- `$limit` caps enqueues per run (mainly for testing; e.g. `1` = a single meeting).
|
|
77
|
+
- `cc` / `bcc` thread through each `Process` job into the recap email; validated in
|
|
78
|
+
`enqueueRecapEmail` (deduped, organizer excluded). **Fallback:** if the organizer email
|
|
79
|
+
can't be resolved but `cc` is present, the recap sends **TO** the cc addresses.
|
|
80
|
+
- A Graph `callTranscript` already recorded (`alreadyExported()`) is skipped. Export treats a
|
|
81
|
+
Duplicate-entry exception as **"skipped"**, not a hard error.
|
|
82
|
+
|
|
83
|
+
### `Process(int $transcriptId, string $graphUserId, string $meetingId, string $organizerEmail, string $subject, string $meetingStartIso, array $cc = [], array $bcc = [])`
|
|
84
|
+
1. Download the VTT **straight from Graph** (no S3 round trip).
|
|
85
|
+
2. `stripAndGroupVtt()` — strips WebVTT timestamps and **merges consecutive same-speaker cues
|
|
86
|
+
into one block** (~40% token reduction before the AI call).
|
|
87
|
+
3. **AI clean + classify (pass 1)** — same single-best-KB-or-`general` contract as before
|
|
88
|
+
(title prioritized, customer-over-company). System prompt assembled at runtime from the
|
|
89
|
+
`Team.*` prompt tables.
|
|
90
|
+
4. Apply DB replacements from `Team.TranscriptReplacements` via `preg_replace_callback`.
|
|
91
|
+
5. Build the approved name and upload the approved `.txt` + `{name}.txt.metadata.json` Bedrock
|
|
92
|
+
sidecar to `s3://togaiq/development-team/{kb_slug}/approved/` (us-east-1, togaiq account).
|
|
93
|
+
6. **Archive the raw VTT to `togaiq`** (`…/{kb_slug}/archive/{date}/`) **from the in-memory
|
|
94
|
+
Graph copy** — no `toga-private` round trip, no cross-account CopyObject.
|
|
95
|
+
7. Bedrock `startIngestionJob` on that KB's data source (retries on `ConflictException` /
|
|
96
|
+
throttling). `SyncKb` handles multi-slug lists; `syncBedrock()` now syncs a **single** KB
|
|
97
|
+
(the old "general fans out to all KBs" behavior was **intentionally removed**).
|
|
98
|
+
8. **Second AI pass** returns structured JSON, which **PHP renders to inline-styled HTML** (a
|
|
99
|
+
real `<table>` for Action Items) for a recap email. Subject is built in PHP:
|
|
100
|
+
`"Recap: {Meeting} - {Date}"`.
|
|
101
|
+
9. Enqueue `Notification/EmailTemplate/Send` to the organizer (see
|
|
102
|
+
[Background Email-Template Worker](./notification-email-template.md); the template is the
|
|
103
|
+
TOGA Technology `True`-client template).
|
|
104
|
+
|
|
105
|
+
### Organizer email resolution
|
|
106
|
+
- `resolveOrganizerEmail()` calls Graph `/users/{id}?$select=mail,userPrincipalName` — but
|
|
107
|
+
this needs the **`User.Read.All`** app permission, which the app registration **lacks**.
|
|
108
|
+
- **Reliable path = the FALLBACK:** `extractOrganizerUpn()` reads
|
|
109
|
+
`participants.organizer.upn` from the meeting. `getMeeting()` now requests
|
|
110
|
+
`$select=subject,startDateTime,participants`; the `OnlineMeetings` read scope already
|
|
111
|
+
exposes the organizer UPN, so **no extra permission** is needed.
|
|
112
|
+
|
|
113
|
+
### `SyncKnowledgeBases(bool $apply = true)` (cron)
|
|
114
|
+
Keeps `Team.KnowledgeBases` current with **Bedrock as source of truth**. Lists all Bedrock
|
|
115
|
+
KBs, filters to `{kb_prefix}*` (`development-team-`), derives the slug, upserts (new →
|
|
116
|
+
`isActive=1`; existing keep flags, refresh `bedrockKbId`); nothing is deleted. CLI:
|
|
117
|
+
`bin/sync-knowledge-bases.php --dry-run`. Cron `0 5 * * *` (daily 05:00 CT).
|
|
112
118
|
|
|
113
119
|
### `SyncKb(string $kbSlug)`
|
|
114
|
-
On-demand re-sync used by the Tools delete/move/edit flows
|
|
115
|
-
|
|
116
|
-
|
|
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.
|
|
121
|
-
|
|
122
|
-
### `bin/reprocess-transcripts.php` (CLI backlog reprocessor)
|
|
123
|
-
Standalone CLI that bootstraps `_underscore`, lists existing raw transcripts under the
|
|
124
|
-
`toga-private` `transcripts/` prefix, and runs each through `Process()`. Flags
|
|
125
|
-
`--prefix` / `--limit` / `--dry-run`. Tolerates **both** the old `{slug}/{date}/` layout and the
|
|
126
|
-
new `{date}/` layout. Requires the `ENVIRONMENT` env var, e.g.
|
|
127
|
-
`ENVIRONMENT=production php bin/reprocess-transcripts.php --dry-run`.
|
|
128
|
-
|
|
129
|
-
### Graph download hardening
|
|
130
|
-
The Graph transcript download is hardened against SSRF / token leak: `https`-only, a
|
|
131
|
-
`*.microsoft.com` host allowlist, and `FOLLOWLOCATION` off.
|
|
120
|
+
On-demand re-sync used by the Tools delete/move/edit flows. `$kbSlug` accepts a
|
|
121
|
+
**comma-separated slug list** and syncs each KB **sequentially** with `KB_SYNC_DELAY_SECONDS`
|
|
122
|
+
spacing (Bedrock allows only one in-flight ingestion per KB, so parallel syncs race).
|
|
132
123
|
|
|
133
124
|
## Data model (`Team.*` schema, DB alias core cluster)
|
|
134
125
|
|
|
135
|
-
Authored in dbchanges2 (`Team/2026-06-30a..e`, `Core/2026-06-30a`, all dated 2026-06-30). All
|
|
136
|
-
PKs `INT UNSIGNED` to match the existing Talos Team-table family.
|
|
137
|
-
|
|
138
126
|
- **`Team.TranscriptReplacements`** — `pattern, replacement, isCaseSensitive, isWholeWord, isActive`.
|
|
139
|
-
|
|
140
|
-
- **`Team.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
- **`Team.
|
|
147
|
-
`
|
|
148
|
-
|
|
149
|
-
candidates). NOTE: the Tools *browse* UI still derives its live KB list from S3 folders, not
|
|
150
|
-
this table.
|
|
151
|
-
- **`Team.TranscriptProcessing`** — `sourceKey UNIQUE(255), kbSlug, approvedKey, archiveKey,
|
|
152
|
-
status` ENUM (`pending/cleaned/uploaded/archived/synced/failed`), `aiModel, failureReason,
|
|
153
|
-
dtProcessed`. Pipeline ledger + idempotency.
|
|
154
|
-
- **`Core.CronJobs`** INSERT for action `Team/Transcripts/Scan` (weekday business hours);
|
|
155
|
-
includes `dtCreated = NOW()` since the column has no default, and depends on the
|
|
156
|
-
`maxExecutionTime` column migration.
|
|
127
|
+
- **`Team.TranscriptPromptTerms`** — `category` ENUM, `term, isActive, …`.
|
|
128
|
+
- **`Team.TranscriptPromptTemplate`** — `name, bodyText, instruction, model, temperature, …`.
|
|
129
|
+
- **`Team.KnowledgeBases`** — `slug, name, isGeneral, bedrockKbId, isActive`; auto-kept-current
|
|
130
|
+
from Bedrock by the daily `SyncKnowledgeBases`.
|
|
131
|
+
- **`Team.TranscriptProcessing`** — pipeline ledger + idempotency. `status` ENUM
|
|
132
|
+
(`pending/cleaned/uploaded/archived/synced/failed`). **`transcriptIdentifier` widened to
|
|
133
|
+
`VARCHAR(768)`** (dbchanges2 `Team/2026-07-09a`).
|
|
134
|
+
- **`Team.TranscriptExports`** — discovery ledger written by `Export`.
|
|
135
|
+
`transcriptIdentifier` **also widened to `VARCHAR(768)`**. Graph-direct columns +
|
|
136
|
+
`organizerEmail` added (dbchanges2 `Team/2026-07-08a`).
|
|
157
137
|
|
|
158
138
|
## Configuration
|
|
159
139
|
|
|
160
140
|
`worker2/Config/production.ini` `[talos]`: `s3_bucket=togaiq`, `s3_region=us-east-1`,
|
|
161
141
|
`bedrock_region=us-east-1`, `kb_prefix=development-team-`, `s3_root_path=development-team/`.
|
|
162
|
-
|
|
163
|
-
**
|
|
164
|
-
|
|
165
|
-
|
|
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).
|
|
142
|
+
The `[talos]` key (togaiq account, us-east-1) is used for approved/archive writes, Bedrock,
|
|
143
|
+
**and now the archive of the raw VTT**. The `[teams]`/`toga-private` S3 key is **no longer
|
|
144
|
+
part of the ingestion loop** (raw reads removed). Credential values live only in the deployed
|
|
145
|
+
`production.ini` — documented here by section/account, never by value.
|
|
171
146
|
|
|
172
147
|
## Gotchas / known issues
|
|
173
148
|
|
|
174
|
-
- **
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
the
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
149
|
+
- **Idempotency: 304-char Graph ids truncated at `VARCHAR(255)` (root cause of endless
|
|
150
|
+
reprocessing).** Graph `callTranscript` ids are ~304 chars; `transcriptIdentifier` columns
|
|
151
|
+
were `VARCHAR(255)`, so ids were silently truncated. `alreadyExported()` compared the full
|
|
152
|
+
(304) id against the stored (255) prefix and **never matched**, AND the INSERT collided on
|
|
153
|
+
the truncated prefix (`error 1062 Duplicate entry … uq_transcript`) — so the same meeting
|
|
154
|
+
reprocessed on every poll. **Fixed by widening `transcriptIdentifier` to `VARCHAR(768)`** on
|
|
155
|
+
**both** `TranscriptProcessing` and the legacy `TranscriptExports`. 768 is the max for a
|
|
156
|
+
utf8mb4 single-column UNIQUE index (3072 bytes / 4).
|
|
157
|
+
- **Durability: dedupe-ledger writes must be SYNCHRONOUS + committed.** `recordExport()` and
|
|
158
|
+
the `loadOrCreateProcessing` INSERT had been dispatched **async** (see
|
|
159
|
+
[async query execution](../../_underscore/features/async-query-execution.md)), so the row
|
|
160
|
+
wasn't durable before the next poll → dedupe/watermark failed and meetings re-ran. These
|
|
161
|
+
pre-AI writes must run **synchronously** and `_Database::transactionCommit(_underscore::DB_TEAM)`.
|
|
162
|
+
Conversely the **post-AI `setStatus` writes stay async** — those run after long (up to 600s)
|
|
163
|
+
AI HTTP calls, which is exactly when the local Core connection has gone stale ("MySQL server
|
|
164
|
+
has gone away"). `alreadyExported()` must also `_Database::useQueryCache(false)` so it does
|
|
165
|
+
not read a stale empty result.
|
|
166
|
+
- **S3 filename safety: a `/` in a meeting title created an unintended S3 sub-folder.**
|
|
167
|
+
`filenameSafeTitle()` replaces `/` and `\` with `-` when building the approved/archive/
|
|
168
|
+
metadata S3 key; the **email subject keeps the untouched original title**. (Tools-side
|
|
169
|
+
`App_Talos_S3::sanitizeTitle()` was fixed to match — see
|
|
170
|
+
[Talos KB Documents Admin](../../../1.0/apps/tools/features/talos-kb-documents-admin.md).)
|
|
171
|
+
- **KB registry split — a KB known to Bedrock/Client_True but not `Team.KnowledgeBases` is
|
|
172
|
+
silently rejected.** worker2's sync validates a KB slug against `Team.KnowledgeBases`
|
|
173
|
+
(`loadActiveKbSlugs`). A KB added only in Bedrock + `Client_True.VectorIndexes` (the registry
|
|
174
|
+
the newer Tools UI reads) is rejected by `SyncKb` as "unknown or inactive knowledge base" and
|
|
175
|
+
silently no-ops — the worker still returns HTTP-200, so the UI falsely reports "sync
|
|
176
|
+
initiated". **Fix:** run `Team/Transcripts/SyncKnowledgeBases` (or
|
|
177
|
+
`bin/sync-knowledge-bases.php`) to discover `development-team-*` KBs from Bedrock and upsert
|
|
178
|
+
them into `Team.KnowledgeBases`. **Open architectural point:** two competing registries —
|
|
179
|
+
`Team.KnowledgeBases` (worker2) vs `Client_True.VectorIndexes` (Tools UI).
|
|
180
|
+
- **Cross-ACCOUNT S3.** `togaiq` (us-east-1) approved/archive writes use the `[talos]` key;
|
|
181
|
+
`CopyObject` across accounts is impossible, so archive is get(in-memory)+put. (The old
|
|
182
|
+
toga-private cross-account read is no longer in the loop.)
|
|
193
183
|
|
|
194
184
|
## Change history
|
|
195
185
|
|
|
196
|
-
- 2026-07-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
`
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
the
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
186
|
+
- 2026-07-09 — **Re-architected to GRAPH-DIRECT.** `Export` is now a thin cron poller
|
|
187
|
+
(removed the `organizers` param; added `$limit`, `cc`/`bcc`); `Scan` and
|
|
188
|
+
`bin/reprocess-transcripts.php` deleted; the `toga-private` staging bucket dropped entirely.
|
|
189
|
+
`Process` downloads the VTT straight from Graph, `stripAndGroupVtt()` (~40% token cut), and a
|
|
190
|
+
**second AI pass** now produces a PHP-rendered HTML recap email to the organizer
|
|
191
|
+
(`Notification/EmailTemplate/Send`, `True` template). Organizer email resolved via the
|
|
192
|
+
meeting `participants.organizer.upn` fallback (no `User.Read.All`). Widened
|
|
193
|
+
`transcriptIdentifier` to `VARCHAR(768)` on both ledgers (304-char Graph ids were truncated
|
|
194
|
+
at 255 → dedupe never matched + 1062 collision → endless reprocessing). Made the pre-AI
|
|
195
|
+
dedupe/discovery writes synchronous+committed (they had been async and weren't durable before
|
|
196
|
+
the next poll); post-AI status writes stay async. `filenameSafeTitle()` converts `/`,`\` →
|
|
197
|
+
`-` in S3 keys. `syncBedrock()` no longer fans "general" out to all KBs. dbchanges2:
|
|
198
|
+
`Team/2026-07-08a`, `Team/2026-07-09a`. (jcardinal)
|
|
199
|
+
- 2026-07-02 — Fixed cross-**account** S3 creds (per-bucket `[teams]`/`[talos]`, archive via
|
|
200
|
+
get+put); built `SyncKnowledgeBases` + `bin/sync-knowledge-bases.php` + daily cron; `SyncKb`
|
|
201
|
+
now takes a comma-separated slug list synced sequentially. Classification: title prioritized,
|
|
202
|
+
customer-over-company. Prompt template excludes personal/social content. (jcardinal)
|
|
203
|
+
- 2026-06-30 — Built the automated cron-driven ingestion pipeline porting the manual
|
|
204
|
+
`kb_processor.php`; added the `Team.*` config/ledger schema; classification moved to AI. (jcardinal)
|
|
211
205
|
|
|
212
206
|
## Related docs
|
|
213
207
|
|
|
214
|
-
- [Teams Meeting Transcript Export](./teams-transcript-export.md) —
|
|
208
|
+
- [Teams Meeting Transcript Export](./teams-transcript-export.md) — now merged into this
|
|
209
|
+
Graph-direct `Export` poller.
|
|
210
|
+
- [Background Email-Template Worker](./notification-email-template.md) — the recap email path.
|
|
211
|
+
- [Async Query Execution](../../_underscore/features/async-query-execution.md) — the sync-vs-async write rule.
|
|
215
212
|
- [Talos KB Documents Admin](../../../1.0/apps/tools/features/talos-kb-documents-admin.md) — the Tools UI.
|
|
216
213
|
- [Talos Knowledge Base Pipeline](../../../1.0/apps/test/features/talos-kb-pipeline.md) — the manual script this ports.
|
|
217
214
|
- [Creating Worker Actions](./creating-worker-actions.md)
|
|
215
|
+
</content>
|
|
216
|
+
</invoke>
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-09
|
|
10
10
|
owners: ["ajean", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Team/Transcripts.php
|
|
@@ -21,6 +21,16 @@ related:
|
|
|
21
21
|
|
|
22
22
|
## Summary
|
|
23
23
|
|
|
24
|
+
> **SUPERSEDED (2026-07-09) — the S3-staging model below is history.** `Export` is now a thin
|
|
25
|
+
> **GRAPH-DIRECT** cron poller: it no longer archives raw VTT to `s3://toga-private/transcripts/`
|
|
26
|
+
> at all. It records a discovery-ledger row and enqueues one `Process` job per transcript, and
|
|
27
|
+
> `Process` downloads the VTT straight from Graph in-memory (raw is archived to `togaiq`, not
|
|
28
|
+
> toga-private). The `toga-private` staging bucket, the flat `transcripts/{date}/…` layout, and
|
|
29
|
+
> the `Backfill()` re-filer described here are no longer in the loop. See
|
|
30
|
+
> [Talos Transcript Ingestion](./talos-transcript-ingestion.md) for the current pipeline. The
|
|
31
|
+
> Graph organizer-resolution, GUID-vs-UPN, and Entra-permission material below is **still
|
|
32
|
+
> accurate** and remains the reference for how `Export` talks to Graph.
|
|
33
|
+
|
|
24
34
|
`_Worker_Team_Transcripts` (action `Team/Transcripts/Export`) polls Microsoft Graph for
|
|
25
35
|
Teams meeting transcripts produced by a set of organizers and archives the raw WebVTT to S3.
|
|
26
36
|
As of 2026-06-30 it **no longer classifies by client**: raw transcripts land **flat** at
|
|
@@ -171,6 +181,13 @@ Policy gap; a `400` only on a UPN means the id was never resolved to a GUID. (A
|
|
|
171
181
|
|
|
172
182
|
## Change history
|
|
173
183
|
|
|
184
|
+
- 2026-07-09 — **`Export` re-architected to GRAPH-DIRECT** (merged into the ingestion pipeline).
|
|
185
|
+
The `toga-private` staging bucket is dropped: `Export` is now a thin poller that records a
|
|
186
|
+
discovery row and enqueues one `Process` job per transcript; `Process` downloads the VTT
|
|
187
|
+
straight from Graph and archives the raw to `togaiq`. `Export` signature is now
|
|
188
|
+
`Export(int $lookbackDays, ?int $limit = null, array $cc = [], array $bcc = [])` (the
|
|
189
|
+
`organizers` param removed). The S3-layout / `Backfill()` sections above are historical. See
|
|
190
|
+
[Talos Transcript Ingestion](./talos-transcript-ingestion.md). (jcardinal)
|
|
174
191
|
- 2026-06-30 — **Removed title-based client classification and the client-folder S3 layout.**
|
|
175
192
|
Raw transcripts now land flat at `transcripts/{date}/…` with no client subfolder; deleted
|
|
176
193
|
`classify()`/`buildClientIndex()`/`loadClientNames()`/`loadClientAliases()` and the old
|
package/package.json
CHANGED