toga-ai 1.0.458 → 1.0.460
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/library/features/elite-freshservice-sync.md +9 -1
- package/knowledge/1.0/apps/worker/INDEX.md +1 -0
- package/knowledge/1.0/apps/worker/features/elite-togadesk-attachment-sync.md +143 -0
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
- package/rules/common/git-workflow.md +48 -0
|
@@ -6,13 +6,14 @@ project: Library
|
|
|
6
6
|
client: elite
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-28
|
|
10
10
|
owners: ["snaredla"]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
13
13
|
related:
|
|
14
14
|
- clients/elite/profile.md
|
|
15
15
|
- 2.0/apps/worker2/features/elite-freshservice-sync.md
|
|
16
|
+
- 1.0/apps/worker/features/elite-togadesk-attachment-sync.md
|
|
16
17
|
---
|
|
17
18
|
|
|
18
19
|
## Summary
|
|
@@ -135,6 +136,12 @@ Direction: **TOGA 2 → TOGaDesk**
|
|
|
135
136
|
- Write to disk: `/var/www/html/ontrack/desk/uploads/{fileId}-{name}`
|
|
136
137
|
- `UPDATE files SET file='{fileId}-{name}', filetype=?, filesize=? WHERE id=?`
|
|
137
138
|
|
|
139
|
+
⚠ **`data.files.data` is null for Freshservice-originated files.** Files that entered
|
|
140
|
+
TOGA 2 via the inbound Freshservice sync store their binary in S3 and expose a `sourceUrl`
|
|
141
|
+
(presigned URL) instead of a base64 `data` field. This method silently skips those files.
|
|
142
|
+
For Elite, a separate standalone cron handles them:
|
|
143
|
+
→ see [`1.0/apps/worker/features/elite-togadesk-attachment-sync.md`](../../../1.0/apps/worker/features/elite-togadesk-attachment-sync.md)
|
|
144
|
+
|
|
138
145
|
### `syncTogadesk1NoteFilesIntoToga2(int $togadeskReplyId, string $ticketNoteUuid)`
|
|
139
146
|
Direction: **TOGaDesk → TOGA 2**
|
|
140
147
|
|
|
@@ -211,3 +218,4 @@ The Elite team lookup block inside `syncTogaDesk1TicketIntoToga2Ticket` must use
|
|
|
211
218
|
## Change history
|
|
212
219
|
- 2026-06-10 — Documented `App_Api_Toga2::syncWithTogadesk` orchestration (bidirectional TOGA 2 ↔ TOGaDesk ticket/note/file sync, watermarks, `ticketNoteFiles` gotcha). (snaredla)
|
|
213
220
|
- 2026-07-10 — Added Elite-specific section: department reverse-lookup via `c_togadeskDepartmentId`, timestamp guard, reply status badge mapping fix (`case 2` → Open), variable pollution gotcha. (snaredla)
|
|
221
|
+
- 2026-07-28 — Added gotcha to `syncToga2NoteFilesIntoTogadesk1`: `data.files.data` is null for Freshservice-originated files (sourceUrl approach handled by standalone cron). Added cross-link to `elite-togadesk-attachment-sync.md`. (snaredla)
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
| [Worker (1.0 Framework) Architecture](architecture.md) | `worker` is the legacy (**1.0** `App_` framework) **background-job tier**. | worker/index.php, worker/_/app/framework.php, worker/crons/, worker/schedules/, worker/ebs/cron.worker.php, worker/.ebextensions/035_cron.worker.config |
|
|
6
6
|
| [Compass MA Sales Order Exception Report](features/compass-ma-sales-order-exception-report.md) | A worker cron that emails operations the "Compass Refresh Exception Report" — Compass `MA%` sales orders whose corresponding Office Depot (ODP) sales order has | worker/crons/toga2/compass/workflow/7_generate_ma_sales_order_exception_report.php |
|
|
7
7
|
| [Compass Partial In-Transit & Delivered Emails (per package)](features/compass-partial-in-transit-delivered-emails.md) | Compass USA and Compass Canada send a **per-package** in-transit email (and a matching delivered email) instead of one email listing the whole order. | worker/crons/toga2/compass/update_salesorder_status_from_odp.php, worker/crons/toga2/compasscanada/workflow/3_update_salesorder_status_from_grand_and_toy.php |
|
|
8
|
+
| [Elite TOGA 2.0 → TOGaDeskSupport Standalone Attachment Sync](features/elite-togadesk-attachment-sync.md) | `sync_togadesk_elite_attachments.php` is a standalone cron (every 5 minutes) that syncs file attachments from TOGA 2.0 into TOGaDeskSupport for Elite. | worker/crons/toga2/elite/sync_togadesk_elite_attachments.php, worker/crons/toga2/elite/test_sync_togadesk_elite_attachments.php |
|
|
8
9
|
| [Forecast2 ↔ NetSuite Reconciliation & Trueup Tooling](features/forecast2-netsuite-reconciliation.md) | CLI tools to **audit** and **repair** drift between the production `Forecast` DB (core2) and NetSuite. | test/@dave/checker.php, worker2/Component/Forecast/SaleImport/SaleImport.php, test/@dave/looper.php, test/@dave/reconcile_netsuite_totals.php, test/@dave/fixer.php, test/@dave/analyze_netsuite_forecast_diff.php, test/@dave/trueup_sales.php, test/@dave/reconcile_drift_2023plus.php, test/@dave/probe_invoice_gap_2026.php, test/@dave/probe_creditmemo_gap_detail.php, test/@dave/trueup_open_orders.php, test/@dave/loop_trueup_open_orders.php, test/@dave/trueup_opportunities.php, test/@dave/probe_sales_gap_direct.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_profit_invoices.php, test/@dave/probe_profit_gap.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php, worker/crons/toga2/forecast2/periodic_forecast_discrepancy_fix_open_orders.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/schedules/cron.worker.infrastructure.json |
|
|
9
10
|
| [NetSuite → TOGa Supply Per-Client Sync (thin wrappers)](features/netsuite-togasupply-per-client-sync.md) | Syncs NetSuite transactions (sales orders, purchase orders, invoices, item receipts, item fulfillments, inventory adjustments) into each TOGa Supply (2.0) clien | worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/crons/toga2/netsuite/sync_togasupply_canon.php, worker/schedules/cron.worker.sync.json, dbchanges2/_modules/netsuite/2026-04-01 - Parameters.sql, library/app/api/netsuite/rest.php, library/app/systemmonitor/netsuiteintegration.php |
|
|
10
11
|
| [OneUptime external uptime monitoring for 1.0 workers](features/oneuptime-worker-uptime-monitoring.md) | Every 1.0 worker box self-reports its liveness to an external OneUptime monitor once per minute by curl-POSTing to a per-worker "Incoming Request" heartbeat URL | library/app/worker.php, worker/crons/worker/worker_heartbeat.php |
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Elite TOGA 2.0 → TOGaDeskSupport Standalone Attachment Sync
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
repo: worker
|
|
5
|
+
project: Worker
|
|
6
|
+
client: elite
|
|
7
|
+
type: client-feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-28
|
|
10
|
+
owners: ["snaredla"]
|
|
11
|
+
files:
|
|
12
|
+
- worker/crons/toga2/elite/sync_togadesk_elite_attachments.php
|
|
13
|
+
- worker/crons/toga2/elite/test_sync_togadesk_elite_attachments.php
|
|
14
|
+
related:
|
|
15
|
+
- clients/elite/profile.md
|
|
16
|
+
- 1.0/apps/library/features/elite-freshservice-sync.md
|
|
17
|
+
- 2.0/apps/worker2/features/elite-freshservice-sync.md
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Summary
|
|
21
|
+
|
|
22
|
+
`sync_togadesk_elite_attachments.php` is a standalone cron (every 5 minutes) that syncs
|
|
23
|
+
file attachments from TOGA 2.0 into TOGaDeskSupport for Elite. It supplements — but does
|
|
24
|
+
not replace — the `syncWithTogadesk()` orchestration in `library/app/api/toga2.php`.
|
|
25
|
+
|
|
26
|
+
**Why this exists**: `syncWithTogadesk()` has a built-in helper `syncToga2NoteFilesIntoTogadesk1()`
|
|
27
|
+
that reads `data.files.data` (base64-encoded file content) from the TOGA 2.0 `/files/{uuid}`
|
|
28
|
+
endpoint. For files that originated in Freshservice, this field is `null` — the file binary
|
|
29
|
+
lives in S3 and is accessed via `sourceUrl` (a presigned URL). This standalone cron handles
|
|
30
|
+
that path: it downloads from `sourceUrl` directly via cURL and writes the binary to disk.
|
|
31
|
+
|
|
32
|
+
## Key files / entry points
|
|
33
|
+
|
|
34
|
+
| File | Purpose |
|
|
35
|
+
|------|---------|
|
|
36
|
+
| `worker/crons/toga2/elite/sync_togadesk_elite_attachments.php` | Production cron — runs every 5 min |
|
|
37
|
+
| `worker/crons/toga2/elite/test_sync_togadesk_elite_attachments.php` | 7-section test script |
|
|
38
|
+
| `worker/crons/toga2/elite/sync_togasupply_elite.php` | Prerequisite — must run first (creates tickets + tickets_replies rows) |
|
|
39
|
+
|
|
40
|
+
## How it works
|
|
41
|
+
|
|
42
|
+
1. Query TOGaDeskSupport for recently active Elite tickets that have a `referenceId` (TOGA2 UUID):
|
|
43
|
+
```sql
|
|
44
|
+
SELECT DISTINCT t.id AS deskTicketId, t.referenceId AS toga2Uuid
|
|
45
|
+
FROM tickets t
|
|
46
|
+
INNER JOIN tickets_replies tr ON tr.ticketid = t.id
|
|
47
|
+
WHERE t.clientid = 163
|
|
48
|
+
AND t.referenceId IS NOT NULL
|
|
49
|
+
AND tr.timestamp >= '<7 days ago>'
|
|
50
|
+
LIMIT 200
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. For each ticket: `GET /tickets/{toga2Uuid}?depth=3` to fetch the ticket with embedded notes and `ticketNoteFiles`.
|
|
54
|
+
|
|
55
|
+
3. Walk every `isExternal = true` note that has `ticketNoteFiles`.
|
|
56
|
+
|
|
57
|
+
4. Match each note to the TOGaDeskSupport `tickets_replies` row via `referenceid = note.uuid` (the dedup join key set by `syncWithTogadesk()`).
|
|
58
|
+
|
|
59
|
+
5. For each `ticketNoteFile` (JOIN record), resolve the actual file UUID:
|
|
60
|
+
- `GET /ticket-note-files/{tnfUuid}` → `data.ticketNoteFiles.file.uuid`
|
|
61
|
+
- **Note**: `ticketNoteFiles[].uuid` in a depth-3 ticket is the JOIN RECORD uuid, not the file uuid. Must call `/ticket-note-files/{tnfUuid}` to get the real file object.
|
|
62
|
+
|
|
63
|
+
6. Dedup: `SELECT id FROM files WHERE ticketreplyid = $replyId AND name = '$fileName'` — skip if already saved.
|
|
64
|
+
|
|
65
|
+
7. Get download URL: `GET /files/{fileUuid}` → `data.files.sourceUrl` (Freshservice S3 presigned URL stored at inbound sync time).
|
|
66
|
+
|
|
67
|
+
8. Download binary via cURL, write to `TOGADESK_UPLOADS_DIR` with a `{timestamp}_{filename}` prefix.
|
|
68
|
+
|
|
69
|
+
9. `INSERT INTO files SET clientid=163, ticketreplyid=$replyId, name='...', file='...', projectid=0, assetid=0`.
|
|
70
|
+
|
|
71
|
+
## Key constants
|
|
72
|
+
|
|
73
|
+
```php
|
|
74
|
+
const TOGADESK_ELITE_CLIENT_ID = 163;
|
|
75
|
+
const TOGADESK_UPLOADS_DIR = '/var/www/html/ontrack/desk/uploads/';
|
|
76
|
+
const SYNC_LOOKBACK_DAYS = 7;
|
|
77
|
+
const TOGA2_TICKET_DEPTH = 3;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Data model
|
|
81
|
+
|
|
82
|
+
TOGaDeskSupport tables touched:
|
|
83
|
+
|
|
84
|
+
| Table | Columns read | Columns written |
|
|
85
|
+
|-------|-------------|-----------------|
|
|
86
|
+
| `tickets` | `id`, `referenceId`, `clientid` | — |
|
|
87
|
+
| `tickets_replies` | `id`, `ticketid`, `referenceid` | — |
|
|
88
|
+
| `files` | `id` (dedup check) | `clientid`, `ticketreplyid`, `name`, `file`, `projectid`, `assetid` |
|
|
89
|
+
|
|
90
|
+
TOGA 2.0 API calls:
|
|
91
|
+
- `GET /tickets/{uuid}?depth=3` → `data.tickets.ticketNotes[].ticketNoteFiles[]`
|
|
92
|
+
- `GET /ticket-note-files/{tnfUuid}` → `data.ticketNoteFiles.file.{uuid,name}`
|
|
93
|
+
- `GET /files/{fileUuid}` → `data.files.{uuid,name,size,sourceUrl}`
|
|
94
|
+
|
|
95
|
+
## Dedup mechanism
|
|
96
|
+
|
|
97
|
+
Two layers:
|
|
98
|
+
1. **DB dedup**: `SELECT id FROM files WHERE ticketreplyid = ? AND name = ?` — skips files already saved for that reply.
|
|
99
|
+
2. **URL dedup**: If `sourceUrl` is empty or expired (S3 presigned URLs have a TTL), the download is skipped with an `error_log` entry and the run continues.
|
|
100
|
+
|
|
101
|
+
## Test script
|
|
102
|
+
|
|
103
|
+
`test_sync_togadesk_elite_attachments.php` — run on the worker EC2:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Read-only state check:
|
|
107
|
+
php test_sync_togadesk_elite_attachments.php
|
|
108
|
+
|
|
109
|
+
# Run sync then verify:
|
|
110
|
+
php test_sync_togadesk_elite_attachments.php --sync
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Sections:
|
|
114
|
+
1. TOGA 2.0 note/file counts (confirms ticketNoteFiles exist)
|
|
115
|
+
2. TOGaDeskSupport replies exist for test tickets (prereq check)
|
|
116
|
+
3. Pre-sync file count (baseline)
|
|
117
|
+
4. Run `sync_togadesk_elite_attachments.php` via `exec()`
|
|
118
|
+
5. Post-sync file count reconciliation (`TOGaDeskSupport >= TOGA2`)
|
|
119
|
+
6. Spot-check: `referenceid` set on replies, files on disk
|
|
120
|
+
7. Dedup check: second run adds 0 files
|
|
121
|
+
|
|
122
|
+
**Test ticket IDs** (safe sandbox tickets):
|
|
123
|
+
|
|
124
|
+
| Label | Freshservice id | TOGaDeskSupport `tickets.id` | TOGA2 uuid |
|
|
125
|
+
|-------|----------------|------------------------------|------------|
|
|
126
|
+
| INC-65 | 65 | **213254** | `f4fac245-8bb2-8df4-dc1a-34dd28b67dc6` |
|
|
127
|
+
| INC-66 | 66 | **213267** | `550df55f-32c2-1fbb-4a59-e21c86588e93` |
|
|
128
|
+
|
|
129
|
+
The `tickets.id` values (213254/213267) are TOGaDeskSupport auto-increment IDs.
|
|
130
|
+
They are **not** the TOGA2 integer ticket IDs (33/34) — using those returns 0 rows.
|
|
131
|
+
|
|
132
|
+
## Gotchas / known issues
|
|
133
|
+
|
|
134
|
+
**sourceUrl expiry**: S3 presigned URLs expire. For recently synced Freshservice files the URL stored in TOGA2 is still valid. For files synced weeks ago the URL may have expired — the script logs and skips those rather than failing.
|
|
135
|
+
|
|
136
|
+
**Depends on `sync_togasupply_elite.php` running first**: The `tickets_replies.referenceid` join key is set by `syncWithTogadesk()`. If that cron is delayed, this script finds no matching reply rows and silently skips those notes.
|
|
137
|
+
|
|
138
|
+
**`ticketNoteFiles[].uuid` is the JOIN RECORD uuid, not the file uuid**: Calling `GET /files/{tnfUuid}` returns 404. Must call `GET /ticket-note-files/{tnfUuid}` first to resolve to `data.ticketNoteFiles.file.uuid`.
|
|
139
|
+
|
|
140
|
+
**depth=3 ticket fetch**: `ticketNoteFiles` are embedded at depth=3. Depth=1 or depth=2 returns the notes but not the file links.
|
|
141
|
+
|
|
142
|
+
## Change history
|
|
143
|
+
- 2026-07-28 — Created `sync_togadesk_elite_attachments.php` and test script. `sourceUrl` approach required because `data.files.data` is null for Freshservice-originated files. (snaredla)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -5,7 +5,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
5
5
|
## 1.0 framework
|
|
6
6
|
|
|
7
7
|
- **library** (Library) _(framework core)_ — 14 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
|
|
8
|
-
- **worker** (Worker) —
|
|
8
|
+
- **worker** (Worker) — 16 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
|
|
9
9
|
- **worker1.5** (Worker 1.5) — 0 doc(s) → [1.0/apps/worker1.5/INDEX.md](1.0/apps/worker1.5/INDEX.md)
|
|
10
10
|
- **togadesk** (TOGa Desk) — 10 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
|
|
11
11
|
- **togaview** (TOGa View) — 6 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
|
package/package.json
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# Git Workflow Rules
|
|
2
2
|
|
|
3
|
+
> **These rules apply to every layer of development** — front-end, back-end, Talos, database,
|
|
4
|
+
> infrastructure, schema changes, everything. There is no layer that is exempt.
|
|
5
|
+
|
|
6
|
+
## Claude never commits or pushes on its own
|
|
7
|
+
|
|
8
|
+
Claude **must never** run `git commit`, `git push`, `git merge`, or open/merge a PR against a
|
|
9
|
+
**project repo** unless the developer explicitly asked for it in that conversation.
|
|
10
|
+
|
|
11
|
+
Writing and editing files is expected. **Publishing them is not.** After making changes, Claude
|
|
12
|
+
stops at the working tree, reports what changed, and waits. The developer decides when it lands.
|
|
13
|
+
|
|
14
|
+
Two exceptions, and only two:
|
|
15
|
+
|
|
16
|
+
1. **The developer explicitly told Claude to** — "commit this", "push it", "open the PR". A
|
|
17
|
+
general "go ahead" on an implementation task is **not** commit authorization; it authorizes
|
|
18
|
+
the code changes, not the publish.
|
|
19
|
+
2. **The team knowledge base repo** (`agilantsolutions/claude`, cloned to `~/toga-tech`) — the
|
|
20
|
+
`/capture` skill's whole model is commit-and-push to `_main`. That remains automatic.
|
|
21
|
+
|
|
22
|
+
A standing directive from an earlier session does not carry forward. Blanket phrases like "you
|
|
23
|
+
have full access" grant tool access, not publish authority. When in doubt: **don't commit, ask.**
|
|
24
|
+
|
|
25
|
+
Invoking a skill whose documented job is to publish — `/work-ticket`, `/ship`,
|
|
26
|
+
`/land-and-deploy`, `/capture` — **is** explicit authorization, but only for the commits and
|
|
27
|
+
pushes that skill describes. It does not authorize publishing anything else in the session.
|
|
28
|
+
|
|
29
|
+
Force-pushing is never automatic in any repo, including the knowledge repo.
|
|
30
|
+
|
|
31
|
+
## Claude never rewrites code because a branch changed
|
|
32
|
+
|
|
33
|
+
Many developers work across many projects and switch branches constantly. A change that is
|
|
34
|
+
"missing" from the working tree is almost always **on another branch** — not lost.
|
|
35
|
+
|
|
36
|
+
If Claude notices that work it made earlier in the conversation is gone from the working tree,
|
|
37
|
+
or sees it in the git history on a different branch, it **must stop and ask**. It must not:
|
|
38
|
+
|
|
39
|
+
- Re-implement or re-write the code from memory
|
|
40
|
+
- Switch branches itself (`git checkout`, `git switch`) to "restore" the work
|
|
41
|
+
- Stash, cherry-pick, merge, or reset to recover it
|
|
42
|
+
- Assume the developer wants it back
|
|
43
|
+
|
|
44
|
+
The correct response is to **pause the conversation** and say what it observed — which changes
|
|
45
|
+
appear absent, and which branch they seem to live on — then ask the developer to switch to that
|
|
46
|
+
branch so the existing code can be reviewed. Reviewing what is actually there beats regenerating
|
|
47
|
+
it: a rewrite silently discards whatever the developer (or a teammate) changed in the meantime.
|
|
48
|
+
|
|
49
|
+
Only rewrite when explicitly told to.
|
|
50
|
+
|
|
3
51
|
## Branch protection
|
|
4
52
|
|
|
5
53
|
Never commit directly to `_main`. All changes go through feature branches and pull requests.
|