toga-ai 1.0.897 → 1.0.898

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.
@@ -0,0 +1,83 @@
1
+ ---
2
+ type: session
3
+ slug: netsuite-clickup-opportunity-sync
4
+ title: NetSuite <-> ClickUp opportunity sync BUILT (stages, close/reopen, notes + files) — live verification pending
5
+ author: jcardinal
6
+ repos: [worker2, _underscore, dbchanges2, test]
7
+ framework: "2.0"
8
+ client: shared
9
+ status: active
10
+ created: 2026-09-25
11
+ updated: 2026-09-25
12
+ ---
13
+
14
+ # Session: netsuite-clickup-opportunity-sync
15
+ **Date:** 2026-09-25
16
+ **Project/Repo:** worker2 + _underscore + dbchanges2 (2.0), test (NetSuite RESTlet)
17
+ **Task:** Implemented Aaron's NetSuite <-> ClickUp opportunity sync plan in worker2 (two-way stage sync with Likely to Close, close/reopen, NS notes -> CU comments, files both ways), deployed and turned on; first live run still to be verified. Continues Aaron's 2026-09-09 session (same slug).
18
+
19
+ > Full design + rules: KB `2.0/apps/worker2/features/netsuite-clickup-opportunity-bidirectional-design.md` and `netsuite-opportunity-documents-sync.md`. Read those first.
20
+
21
+ ---
22
+
23
+ ## What WORKED
24
+ - Business rules from Aaron + Eric (2026-09-24) implemented in ONE PHP map `worker2/Component/Opportunity/StageMap/StageMap.php` (`_Component_Opportunity_StageMap`). Each entry = target + "already matching" set. Offline test `Controller/OpportunityStageMapTest.php` run via CLI runner: all cases PASS, 1413 round trips, 0 would loop. Mutation check (broke BU Discovery entry) made the test fail as expected.
25
+ - Deployed to prod by jcardinal: SQL `Forecast/2026-09-24a`, `Forecast/2026-09-24b`, `Core/2026-09-24a`; `_underscore` Netsuite.php; worker2.
26
+ - NetSuite RESTlet deployed in PROD: script `customscript_rl_opportunity_documents` (internal 2831), deploy `customdeploy_rl_opportunity_documents` (deploy=1), Released, audience Administrator, param `custscript_opp_docs_upload_folder` = 2008026 (private folder "ClickUp Opportunity Uploads"). Integration "TOGa OAuth 2.0" (runs as Jeffrey Cardinal / Administrator) already had RESTlets scope. Host 1095849.restlets.api.netsuite.com matches what worker2 derives from production.ini.
27
+ - Live dry run proved end to end: `DRY RUN: NS->CU ...; CU->NS {"tasksChecked":2}` + 8 `would copy NS file ...` + `would copy NS note 48225 "Sync test" -> task 868k1ytc5`. So token, RESTlet, file search, note search, ClickUp team search all work.
28
+ - All flags then turned ON (jcardinal said "yes to both": copy all files incl. generated quote PDFs; stage sync in same deploy): `_Worker_Netsuite_OpportunityDocuments::SYNC_ENABLED = true`, `SYNC_START = '2026-09-24 00:00:00'`, `_Worker_Netsuite_Opportunity::SYNC_STAGE_TO_CLICKUP = true`, `_Worker_Clickup_Opportunity::SYNC_CLOSE_TO_NETSUITE = true`.
29
+ - Reviews (subagents): CTO (map as PHP not tables; no ApiRequest core edit), php-reviewer x2, sql-reviewer x2, cso x2 (final: SAFE TO SHIP), framework-pattern-checker (clean), JS reviewer. All findings fixed.
30
+
31
+ ## What did NOT work — DO NOT RETRY THESE
32
+ - **Testing worker2 via `?controller=OpportunityStageMapTest` URL** — blank page. worker2 routes EVERYTHING to `_Controller_Index::worker()`; no per-URL controllers. Prove pure logic with the CLI runner (stub `_Component`/`_Controller`, require file, call `run()`), or a worker action.
33
+ - **RESTlet search on `search.Type.OPPORTUNITY` with a `mainline` filter** — `SSS_INVALID_SRCH_FILTER: An nlobjSearchFilter contains invalid search criteria: mainline.` Use `search.Type.TRANSACTION` + `["type","anyof","Opprtnty"]` + mainline T.
34
+ - **`file.created` / `usernotes.notedate` filtered with `after` + a DATETIME string** — `UNEXPECTED_ERROR: An unexpected SuiteScript error has occurred`. Use `format.Type.DATE` with `onorafter` (day granularity; ledger drops repeats).
35
+ - **Creating User Notes via NS REST record API** — `Record type 'note' does not exist` (404). Must go through the RESTlet (`record.Type.NOTE`).
36
+ - **Reading note title/text/date via SuiteQL** — `NOT_EXPOSED - Not available for channel SEARCH`. SuiteQL `file` table IS exposed; notes are not.
37
+ - **Widening the shared NetSuite OAuth scope to add `restlets`** — not done on purpose: a token request for a scope the integration doesn't allow fails ALL NetSuite calls. sendRestlet() uses its own separate token.
38
+ - **First re-upload of the RESTlet file** didn't take — the error stack had identical line numbers to the old file. When a fix "doesn't work", compare stack line numbers before debugging.
39
+ - **Hand-escaped regex `[^\s@\\]` in a single-quoted PHP string** — PHP turned `\\` into `\`, escaping the `]` (preg compile error). Use `\x5C` for backslash.
40
+ - **`INSERT IGNORE` for the ledger reserve** — hides FK/other errors (silent skip). Replaced with plain INSERT + catch, treating only an existing row as "already done".
41
+
42
+ ## Not tried yet (candidates for next session)
43
+ - Verify the FIRST LIVE RUN (cron every 5 min): ClickUp task `868k1ytc5` has comment starting `NetSuite note from ...` with "Sync test"; task `868jh6x08` has attachment `TOGA_Technology.065763.v1.18.pdf`. Read `Core.WorkerJobs` (action `Netsuite/OpportunityDocuments/Sync`) and `Forecast.OpportunityDocuments` rows.
44
+ - Live stage test: change an opportunity's Sales Stage in NS -> task should move; output `stage set: X -> Y`. Close in ClickUp -> NS Likely to Close 124/14. Reopen -> NS 10% + ClickUp comment + NS User Note.
45
+ - Unverified live: NS workflows allowing entityStatus PATCH; RESTlet `addNote`/`addFile` (never run for real yet); ClickUp `date_updated` bumping when an attachment is added (if not, CU->NS files are never found); ClickUp attachment URLs being on `*.clickup-attachments.com`; TLS verify on EB hosts.
46
+ - Confirm BU Discovery interpretation with Aaron/Eric (see Decisions).
47
+ - Separate ticket: `_ApiRequest` core default does NOT verify TLS peers (ApiRequest.php:238) — affects every caller.
48
+ - Low: 3 NS files have no extension (e.g. `email_quote_excel_cpo_...`), so ClickUp may not preview them.
49
+
50
+ ## Current file state
51
+ All changes are on the developer's checked-out branches (worker2 `_production`; _underscore + dbchanges2 `toga25-desk`; test `master`). Deployed to prod by jcardinal. Check git for commit state — do not assume.
52
+
53
+ | File | Status | Notes |
54
+ |------|--------|-------|
55
+ | worker2/Component/Opportunity/StageMap/StageMap.php | new | Shared stage map (both directions), live option resolution |
56
+ | worker2/Controller/OpportunityStageMapTest.php | new | Pure test; run via CLI runner, not URL |
57
+ | worker2/Worker/Clickup.php | modified | CU->NS routed by Opportunities folder 90117060687, no early return |
58
+ | worker2/Worker/Clickup/Opportunity.php | modified | StageMap, close/reopen (SYNC_CLOSE_TO_NETSUITE=true), actor echo guard, Forecast link check, reopen comment + NS note, DB_LOGS commits, TLS |
59
+ | worker2/Worker/Netsuite/Opportunity.php | modified | Old map constants removed; claim split; syncStageToClickup (SYNC_STAGE_TO_CLICKUP=true); stale PENDING takeover + fenced endClaim; TLS |
60
+ | worker2/Worker/Netsuite/OpportunityDocuments.php | new | Notes/files sync cron action, ledger, SYNC_ENABLED=true, SYNC_START 2026-09-24 00:00:00 |
61
+ | _underscore/Component/Api/Netsuite/Netsuite.php | modified | Additive sendRestlet() with own restlets-scope token |
62
+ | dbchanges2/Forecast/2026-09-24a - Add dtClickupClaimed to Opportunities.sql | new, applied prod | Raw-SQL-only column, not on ORM models |
63
+ | dbchanges2/Forecast/2026-09-24b - Add OpportunityDocuments ledger.sql | new, applied prod | FK ON DELETE CASCADE (1.0 reconciler deletes Opportunities rows) |
64
+ | dbchanges2/Core/2026-09-24a - Insert Opportunity Documents Sync CronJob.sql | new, applied prod | */5 cron, uuid 961e7799-2b19-4b0c-add2-b551b6f82f6e |
65
+ | test/@jeff/netsuite/opportunity-documents/rl_opportunity_documents.js | new, deployed NS | RESTlet changes/getFile/addFile/addNote |
66
+
67
+ ## Decisions made
68
+ - **Stage map stays PHP, not Forecast tables** (CTO + jcardinal). Rows can't hold the "unless already X" rules; tables would add a cross-repo deploy for 8 rarely-changing options. Rejected: OpportunityStageMap/CloseStatusMap tables from Aaron's earlier design.
69
+ - **Likely to Close checked first, always wins** (Aaron/Eric). CU close writes Likely to Close only; Sales Stage never written for a close (Closed Won/Lost stages retired in NS).
70
+ - **Reopen in ClickUp** -> NS Likely to Close 10% + mapped stage, comment on BOTH ClickUp and NS User Note (Aaron/Eric: "A" + "C").
71
+ - **BU Discovery (my interpretation, CONFIRM):** matches NS Stage 2 and 3, so PQ -> BU -> Quote writes nothing; moving BACK into BU from a later stage writes Stage 3 (id 4) so NS doesn't drag the task forward again.
72
+ - **Notes/files: go-live forward only; ClickUp comments NOT sent to NS; copy all files including generated quote versions** (jcardinal "yes").
73
+ - **Security:** CU->NS only writes when Forecast.clickupTaskId == the task (Opportunity # is free text); RESTlet getFile only serves files attached to the given opportunity; attachment download only from https *.clickup-attachments.com; blocked script/web file types; RESTlet `changes` logged as counts only.
74
+ - **Claim:** guards creation only; stale PENDING (>30 min or NULL timestamp) taken over; final write fenced by our own claim timestamp so a slow job can't overwrite the new owner.
75
+
76
+ ## Blockers
77
+ none (pending live verification only). Still open from Aaron's 2026-09-09 session: 1.0 PRs library#880, worker#1718, tools#15 unmerged — nightly reconciler still runs without the delete guard.
78
+
79
+ ## Exact next step
80
+ > Check ClickUp task `868k1ytc5` for the "Sync test" comment and task `868jh6x08` for `TOGA_Technology.065763.v1.18.pdf`, then query prod `Forecast.OpportunityDocuments` (status per row) and `Core.WorkerJobs` for action `Netsuite/OpportunityDocuments/Sync` to confirm the first real runs succeeded. If anything is wrong, set that part's flag back to false and deploy.
81
+
82
+ ---
83
+ _Saved by /session-save on 2026-09-25_
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.897",
3
+ "version": "1.0.898",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",