toga-ai 1.0.1206 → 1.0.1208

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.
@@ -50,6 +50,7 @@
50
50
  | [_Model::save() vs raw _Query — no atomic conditional update](features/model-save-vs-query-atomic-update.md) | `_Model::save()` can't do an atomic conditional update or a second write on the same instance; open before writing a row-claim/optimistic-concurrency guard, a r |
51
51
  | [NetSuite REST Client (_Component_Api_Netsuite) — record writes & SuiteQL](features/netsuite-rest-client.md) | Shared 2.0 `_underscore` NetSuite REST client (`_Component_Api_Netsuite`) — record GET/write, SuiteQL, auth, escaping, logging; open before any 2.0 NetSuite cal |
52
52
  | [NetSuite Sales Order sync — ship-to address, phone, and PO reference sourcing](features/netsuite-salesorder-address-phone-sync.md) | Where the shared NetSuite SO importer (`_Trait_Netsuite_SalesOrder`) sources SO#, PO#, and ship-to phone, and the traps in changing it; open before editing this |
53
+ | [Non-prod Aurora clusters (account 975050298201) — layout, settings, recovery, access limits](features/nonprod-aurora-clusters.md) | Which non-prod 2.0 Aurora MySQL clusters exist in account 975050298201 (us-east-1) after TRUE-83406, the settings every one copies, how to delete/restore a clus |
53
54
  | [Note records have NO author — every `*Notes` model's `contactId` is unwritten](features/note-record-authorship.md) | Every `*Notes` model declares a `contactId` FK but no code writes it — note authorship is unrecoverable from 2.0 data; open before building any note author disp |
54
55
  | [NOTIFICATION email templates — block JSON rendered server side (_Component_EmailBlocks + _Component_EmailBranding)](features/notification-email-blocks.md) | How client-built (NOTIFICATION) emails are stored as block JSON, rendered to safe HTML on the server, merged with `{{group.field}}` tokens and branded per clien |
55
56
  | [Notification engine (rule-driven in-app notifications from record change logs)](features/notification-engine.md) | Platform (all 2.0 apps) in-app notifications: data rules on Core turn a record's change log into Client.Notifications rows; open when adding a notification kind |
@@ -11,6 +11,7 @@ owners: ["rgirish"]
11
11
  files: []
12
12
  related:
13
13
  - ../architecture.md
14
+ - nonprod-aurora-clusters.md
14
15
  - ../../../../1.0/apps/worker1.5/features/database-backups.md
15
16
  ---
16
17
 
@@ -48,7 +49,7 @@ Live check on us-east-1 writers: `log_bin = ON`.
48
49
 
49
50
  ### Non-prod (account 975050298201, us-east-1)
50
51
 
51
- - 19 single-instance clusters (`sandbox-dev`, `sandbox-client`, `stage`, `demo`, `client-*`, `qa-*`, `qc-*`), engine `3.10.3`.
52
+ - Single-instance clusters, engine `3.10.3` — current list in [Non-prod Aurora clusters](nonprod-aurora-clusters.md).
52
53
  - All on `true-rds-cluster-parameters` with `binlog_format = OFF` (`log_bin = 0`). No Global Database.
53
54
 
54
55
  ### Who reads the binlog (2026-10-29: nobody)
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: Non-prod Aurora clusters (account 975050298201) — layout, settings, recovery, access limits
3
+ framework: "2.0"
4
+ repo: _underscore
5
+ project: _Underscore
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-10-29
10
+ owners: ["rgirish"]
11
+ files: []
12
+ related:
13
+ - ../architecture.md
14
+ - aurora-binlog-configuration.md
15
+ - ../../api2/workflows/environment-configuration-and-provisioning.md
16
+ - ../workflows/local-db-refresh-from-beta.md
17
+ ---
18
+
19
+ Which non-prod 2.0 Aurora MySQL clusters exist in account 975050298201 (us-east-1) after TRUE-83406, the settings every one copies, how to delete/restore a cluster safely, how to tell an empty cluster without DB access, and what the GoAgilant-Developers SSO role cannot do (KMS); open before creating, deleting or loading a non-prod DB cluster.
20
+
21
+ ## Summary
22
+
23
+ One **all-in-one** Aurora MySQL cluster per non-prod environment: `Core`, `Client_*`, `Logs`, `Logs_*`, `Archive_*` all in the same cluster. No environment shares a cluster. Production is separate: 4 per-family clusters in account `654654170868` (see [binlog doc](aurora-binlog-configuration.md)).
24
+
25
+ ## How it works
26
+
27
+ ### Clusters (as of 2026-10-29)
28
+
29
+ | Cluster | Databases |
30
+ |---|---|
31
+ | `sandbox-dev`, `sandbox-client` | full DB sets (unchanged) |
32
+ | `qa-alpha` | `Core`, `Logs`, `Client_True`, `Client_Elite`, `Logs_True`, `Logs_Elite`, `Archive_True` (restored from final snapshot) |
33
+ | `qc-security` | `Core`, `Logs`, `Client_Compass`, `Logs_Compass` (restored from final snapshot) |
34
+ | `dev`, `qa`, `stage`, `uat`, `hotfix`, `demo`, `sandbox` | **empty** — created 2026-10-29, no TOGA databases yet |
35
+
36
+ - The 7 new clusters match the environment list. AWS identifiers cannot start with `_`, so env `_qa` → cluster `qa`.
37
+ - Endpoint: `<name>.cluster-cb860gg2qw2n.us-east-1.rds.amazonaws.com`.
38
+ - Loading `Core` / `Client_*` / `Logs` into the new clusters is a follow-up ticket. Seed them per [environment provisioning](../../api2/workflows/environment-configuration-and-provisioning.md) (base `Logs` included).
39
+ - Prod data into non-prod: only `stage` and `demo`, masked (ticket rule A17). Prod is 4 clusters, so it is dump + load per database, not a snapshot copy.
40
+
41
+ ### Settings every non-prod cluster copies (from `sandbox-dev`)
42
+
43
+ - Engine `aurora-mysql` `8.0.mysql_aurora.3.10.3`.
44
+ - One writer `<name>-writer`, `db.t4g.medium`, `us-east-1a`.
45
+ - Subnet group `true-test-db-subnet-group`; security group `sg-075195ac9152313d3`.
46
+ - Cluster parameter group `true-rds-cluster-parameters`; instance parameter group `true-rds-parameters`.
47
+ - Encrypted with the account's RDS KMS key; not public; backup retention 1 day.
48
+ - Enhanced monitoring 60 s via `rds-monitoring-role`.
49
+ - Master user `admin`, plain `--master-user-password` (not Secrets Manager — see Gotchas).
50
+ - Tags `Environment=<name>`, `Ticket=<ticket>`.
51
+
52
+ ### Admin passwords — where they live (never copy values here)
53
+
54
+ - `dev`, `sandbox`: same as `api2/Config/sandbox-dev.ini` `[database]`.
55
+ - `qa`, `stage`, `uat`, `hotfix`, `demo`: same as `api2/Config/stage.ini` `[database]`.
56
+ - Developer plans to rotate them; after that, the ini files are the source.
57
+
58
+ ### Delete and restore a cluster
59
+
60
+ 1. Always delete with `--final-db-snapshot-identifier final-<name>-<date>`.
61
+ 2. To bring it back: `restore-db-cluster-from-snapshot` with the same cluster id → same endpoint, data, and admin password.
62
+ 3. Restore creates **no instance**. Run `create-db-instance` for a new writer (`<name>-writer`) or the cluster cannot serve.
63
+
64
+ ### Is a cluster empty? (no DB access needed)
65
+
66
+ - CloudWatch `AWS/RDS` `VolumeBytesUsed` ~140 MB = MySQL system files only, no TOGA databases.
67
+ - toga-db MCP `list_schemas` returns `[]` for clusters it cannot see, so an empty list alone is not proof.
68
+
69
+ ## Gotchas
70
+
71
+ - **No KMS rights for `GoAgilant-Developers`** in 975050298201 (`kms:DescribeKey` denied):
72
+ - `create-db-cluster --manage-master-user-password` fails `KMSKeyNotAccessibleFault ... KMS key [null]` (the Secrets Manager key). Use `--master-user-password` with the RDS key — that works.
73
+ - Restoring encrypted snapshots in the same account works.
74
+ - Copying prod snapshots across accounts needs KMS rights the role does not have.
75
+ - **us-west-1 RDS** is blocked for this role by an org SCP.
76
+ - **`api2/Config/stage.ini` is CRLF.** A script reading values from it must strip `\r`, or AWS rejects the password with "Input can't contain control characters".
77
+ - **RDS console "Databases" count** lists each cluster AND its writer instance as rows (19 clusters showed as "38").
78
+ - **Leftovers from the 2026-10-29 cleanup:** 15 deleted clusters (`qa-task`, `qa-hotfix`, `qa-beta`, `qa-gamma`, `qc-task`, `qc-hotfix`, `qc-alpha`, `qc-beta`, `qc-gamma`, `qc-performance`, `client-alpha`, `client-beta`, `client-gamma`, old `stage`, old `demo`) keep final snapshots `final-<name>-2026-10-29` (storage cost; cleanup is a separate task). Their Elastic Beanstalk envs (`api-*`, `worker-*`, `websocket-*`) still exist and point at missing databases — separate ticket.
79
+
80
+ ## Change history
81
+ - 2026-10-29 — First version: 7 new empty env clusters, 15 old clusters deleted, qa-alpha/qc-security restored, KMS limits, recovery steps (TRUE-83406) (rgirish)
@@ -6,8 +6,8 @@ project: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-10-15
10
- owners: [dfranks, jcardinal]
9
+ updated: 2026-10-29
10
+ owners: [dfranks, jcardinal, mhammontree]
11
11
  files: []
12
12
  related:
13
13
  - ../../../../standalone/apps/claude/workflows/clickup-ticket-workflow.md
@@ -35,9 +35,11 @@ How ClickUp surfaces branches/PRs/commits in a ticket's GitHub tab and the branc
35
35
  2. GitHub Settings → "Connect Repositories to Spaces": add each repo AND link it to the **Development** space. A repo not linked to that space never links its PRs.
36
36
  3. Repos added 2026-10-08: worker2, dbchanges2, toga25-desk, toga25-iq, toga25-blox, toga25-supply, toga2-commerce, toga2-supply, toga2-template, toga-blox-npm, saml, forward, apiproxy (`test` and `claude` skipped). A PR titled with the ticket id then linked automatically (verified).
37
37
  - **Settings:** leave "auto-generated branch name" as `:taskId:`. Do not turn on "Update Task Status from GitHub" — worker2 owns status automation.
38
+ - **A shared branch links its PRs to EVERY ticket it names.** One branch carrying several tickets (e.g. `TRUE-82776` for 82776/82778/82779) shows its PRs on all of them, even tickets whose work is not in it. Before closing a PR because "the ticket shows a PR", check the PR's head branch (2026-10-29, TRUE-82779).
38
39
 
39
40
  ## Change history
40
41
 
42
+ - 2026-10-29 — Gotcha: a shared branch's PRs show on every ticket it names; check the head branch (TRUE-82779). (mhammontree)
41
43
  - 2026-10-15 — PR base now follows the ticket's project (Desk/IQ → `toga25`); links to git-workflow.md instead of restating. (jcardinal)
42
44
  - 2026-10-08 — Added reconnect fix, Development-space repo linking, and settings to leave alone. (jcardinal)
43
45
  - 2026-10-08 — `/work-ticket` and `/plan-ticket` retired; `/finish` opens the PRs. PR title = ticket id first; base = repo default branch; git-workflow now uses bare ticket-id branches. (jcardinal)
@@ -578,6 +578,12 @@ instance: [Staples SFTP folder monitors](../../../clients/staples/features/oneup
578
578
  `loadImportConfigs()` + `connect()` (CronJobs config + Secrets Manager, one login per account) — see
579
579
  [NYCDOE stuck ASN files monitor](../../../clients/nycdoe/features/oneuptime-stuck-asn-files-monitor.md).
580
580
 
581
+ **Private-key login** (first in worker2, [NYCDOE EDI 850 queue monitor](../../../clients/nycdoe/features/oneuptime-edi-850-sftp-queue-monitor.md)):
582
+ `$key = PublicKeyLoader::load(_Component_Secrets::resolve($secretRef)); $sftp->login($username, $key);`
583
+ (`phpseclib3\Crypt\PublicKeyLoader`). Store the secret as the **raw PEM in Secrets Manager's Plaintext
584
+ tab** (BEGIN/END lines and line breaks kept) — the default key/value JSON (`{"":""}`) fails every run.
585
+ `_Worker_Sftp_Import::connect()` still has PRIVATE_KEY as a TODO; copy this.
586
+
581
587
  Two runtime facts about the installed phpseclib3 (v3.0.49) that bite silently — verified at runtime
582
588
  against the live SFTP:
583
589
 
@@ -693,6 +699,7 @@ production deploy.**
693
699
  - [OneUptime 1.0 worker uptime monitoring](../../../1.0/apps/worker/features/oneuptime-worker-uptime-monitoring.md) — the 1.0 push-heartbeat predecessor
694
700
 
695
701
  ## Change history
702
+ - 2026-10-29 — SFTP: first private-key login (raw PEM secret) via NYCDOE `EdiPurchaseOrderSftpQueue` (TRUE-82779). (mhammontree)
696
703
  - 2026-10-29 — Gotcha: never run a monitor from local worker2, the hardcoded push URL feeds local numbers into the prod monitor; `CompassSalesOrderTransmission` triage now in the MITS alerting doc (TRUE-83407). (bala)
697
704
  - 2026-10-29 — Every OneUptime push retries once (`setAutoRetry`, 1 retry, 2s; `PUSH_RETRY_*` consts on every monitor class); worker1.5 heartbeat too (TRUE-83398). (bala)
698
705
  - 2026-10-29 — Gotcha: "stopped reporting" incidents from failed OneUptime pushes (`OneUptime push FAILED.` + `alarm=OK` in `WorkerJobs.output`) and how to tell them from a real outage (TRUE-83380). (bala)
@@ -22,8 +22,8 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
22
22
 
23
23
  ## 2.0 framework
24
24
 
25
- - **_underscore** (_Underscore) _(framework core)_ — 118 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
26
- - **worker2** (Worker) — 102 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
25
+ - **_underscore** (_Underscore) _(framework core)_ — 119 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
26
+ - **worker2** (Worker) — 103 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
27
27
  - **api2** (API) — 29 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
28
28
  - **dbchanges2** (Database Changes) _(framework core)_ — 24 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
29
29
  - **toga2-supply** (TOGa Supply) — 9 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
@@ -5,6 +5,7 @@
5
5
  | [NYCDOE 2.0 ASN SFTP Ingestion + Parsing (Sftp/Import → Sftp/Parse → Case/ServiceRequest)](features/asn-sftp-ingestion-2-0.md) | 2.0 | 2.0 rewrite of DOE's vendor-SFTP ASN intake (TRUE-80519 Phases 1–2): per-manufacturer fetch to `Files`, header-validated parse into one Case per customer PO + o |
6
6
  | [NYCDOE EDI 850 Receive & 997 Acknowledgement](features/edi-850-receive-and-997-ack.md) | 1.0 | DOE's EDI 850-in → NetSuite SO + inline 997-out ack; open for the 997 build, its "always accept" stub, or a DOE ack complaint. |
7
7
  | [NYCDOE Ticket Hold-Status Sync (ServiceNow ⇄ TOGaDesk)](features/hold-status-sync.md) | 1.0 | Bidirectional DOE ticket hold-status sync (ServiceNow ⇄ TOGaDesk), the four defects that let holds silently revert or never re-send, the held→complete close clo |
8
+ | [NYCDOE: EDI 850 SFTP queue monitor (Monitor/Nycdoe/EdiPurchaseOrderSftpQueue)](features/oneuptime-edi-850-sftp-queue-monitor.md) | 2.0 | worker2 OneUptime monitor that alarms when a DOE EDI 850 sits 19h+ in `/outbound` on DOE's SFTP (= the 1.0 receiver failed, no SO, no 997); open when it fires o |
8
9
  | [NYCDOE: ServiceNow update-rejection monitor (Monitor/Nycdoe/ServiceNowUpdateRejections)](features/oneuptime-servicenow-update-rejections-monitor.md) | 2.0 | worker2 OneUptime monitor that alarms when ServiceNow refuses NYC DOE ticket updates (HTTP 200 with an empty body) in the 1.0 `Logs.API` log; open when it fires |
9
10
  | [NYCDOE: stuck ASN files monitor (Monitor/Nycdoe/StuckAsnFiles)](features/oneuptime-stuck-asn-files-monitor.md) | 2.0 | worker2 OneUptime monitor that alarms when any file sits 24h+ on a NYC DOE vendor ASN SFTP folder; open when it fires, when a DOE ASN file is stuck, or when cha |
10
11
  | [NYCDOE ServiceNow / ASN Integration](features/servicenow-integration.md) | 1.0 | Full DOE ServiceNow/ASN integration map — ticket mirror, ASN → NetSuite SO/TOGa Desk pipeline, outbound status/POD, EDI; open for any DOE cron/pipeline work or |
@@ -6,14 +6,16 @@ project: Worker
6
6
  client: nycdoe
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-09-16
9
+ updated: 2026-10-29
10
10
  owners: [mhammontree]
11
11
  files:
12
12
  - worker/crons/sync/nycdoe/receive_edi_purchase_orders.php
13
+ - worker/crons/sync/nycdoe/send_edi_open_invoices.php
13
14
  - worker/schedules/cron.worker.sync.json
14
15
  - library/app/edi.php
15
16
  related:
16
17
  - servicenow-integration.md
18
+ - oneuptime-edi-850-sftp-queue-monitor.md
17
19
  - ../profile.md
18
20
  - ../../../1.0/apps/worker/architecture.md
19
21
  - ../../../1.0/apps/library/architecture.md
@@ -23,7 +25,11 @@ DOE's EDI 850-in → NetSuite SO + inline 997-out ack; open for the 997 build, i
23
25
 
24
26
  ## Summary
25
27
 
26
- DOE sends purchase orders as **EDI 850** files over their SFTP; TOGA turns each into a NetSuite Sales Order and sends back an **EDI 997** functional acknowledgement. There is **no separate 997 cron** — the 997 is built and uploaded inline at the end of the 850 receiver, so anything that aborts the receiver means DOE never gets an ack. Broader DOE ticket + ASN pipeline: [servicenow-integration.md](servicenow-integration.md).
28
+ DOE sends purchase orders as **EDI 850** files over their SFTP; TOGA turns each into a NetSuite Sales Order and sends back an **EDI 997** functional acknowledgement. There is **no separate 997 cron** — the 997 is built and uploaded inline at the end of the 850 receiver, so anything that aborts the receiver means DOE never gets an ack. Broader DOE ticket + ASN pipeline: [servicenow-integration.md](servicenow-integration.md). A stuck 850 is caught by the 2.0 [EDI 850 SFTP queue monitor](oneuptime-edi-850-sftp-queue-monitor.md) (TRUE-82779).
29
+
30
+ **SFTP folder roles (named from DOE's side — easy to mix up):**
31
+ - `/outbound` = DOE drops 850s here **for us**; the receiver reads it (cron line ~33) and deletes each file after success.
32
+ - `/inbound` = **we** drop 997s and invoices (`send_edi_open_invoices.php`) here for DOE to pick up.
27
33
 
28
34
  ## How it works
29
35
 
@@ -63,6 +69,7 @@ Field mapping (verified against 850 FileLog id 880810):
63
69
  ## Related
64
70
 
65
71
  - [NYCDOE ServiceNow / ASN Integration](servicenow-integration.md)
72
+ - [EDI 850 SFTP queue monitor](oneuptime-edi-850-sftp-queue-monitor.md)
66
73
  - [NYC DOE client profile](../profile.md)
67
74
  - [Worker (1.0) architecture](../../../1.0/apps/worker/architecture.md)
68
75
  - [Library (1.0) architecture](../../../1.0/apps/library/architecture.md)
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: "NYCDOE: EDI 850 SFTP queue monitor (Monitor/Nycdoe/EdiPurchaseOrderSftpQueue)"
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: nycdoe
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-10-29
10
+ owners: ["mhammontree"]
11
+ files:
12
+ - worker2/Worker/Monitor/Nycdoe.php
13
+ - dbchanges2/Core/2026-10-28a - NYCDOE EDI 850 SFTP Queue Monitor CronJob.sql
14
+ related:
15
+ - ./edi-850-receive-and-997-ack.md
16
+ - ./oneuptime-stuck-asn-files-monitor.md
17
+ - ../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md
18
+ - ../profile.md
19
+ ---
20
+ worker2 OneUptime monitor that alarms when a DOE EDI 850 sits 19h+ in `/outbound` on DOE's SFTP (= the 1.0 receiver failed, no SO, no 997); open when it fires or when changing the DOE EDI SFTP login.
21
+
22
+ ## Summary
23
+ `Monitor/Nycdoe/EdiPurchaseOrderSftpQueue(string $host, string $username, string $secretRef, string $remoteDirectory, int $port = 22)` (`_Worker_Monitor_Nycdoe`, TRUE-82779). SFTP-backlog monitor; shared pattern in [OneUptime push-metric monitors](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md#sftp-monitoring-phpseclib3). Watches the folder the 1.0 [EDI 850 receiver](edi-850-receive-and-997-ack.md) empties.
24
+
25
+ - `Core.CronJobs` uuid `94f6d0ed-a67b-4862-b844-215d3ccc44cf`, `0 * * * *`, `maxExecutionTime` 300, ships `isActive = 0`. `parameters` `{host: transfer.schools.nyc, port: 22, username, secretRef, remoteDirectory: "/outbound"}` — config lives in the row, not PHP.
26
+ - Key: AWS Secrets Manager `sftp/nycdoe/edi` (the `doedimedu.pem` private key). Login: see [private-key SFTP login](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md#sftp-monitoring-phpseclib3).
27
+ - Push URL = in-method `$ONEUPTIME_URL` local (credential; never record the value).
28
+
29
+ ## How it works
30
+ - **Stale rule** (`findStaleFiles(entries, staleFileMinutes, nowEpoch)`, public pure filter for tests): any plain file, hidden included (only `.`/`..` and directories skipped — same as the 1.0 receiver), mtime >= `STALE_EDI_FILE_MINUTES` (1140 = 19h). Constant at the top of the class.
31
+ - **Why 19h:** receiver runs 9:30 and 15:30 and deletes each 850 after the SO + 997; a file landing just after 15:30 waits ~18h normally.
32
+ - **Retry:** 3 tries, 5s apart, per-step timeout min(15s, remaining / 4), floor 5s, 240s deadline. Login refused is not retried.
33
+ - **Body:** `status` (`reporting` / `error`), `alarm` (`HIGH` / `OK`), `staleFileCount`, `staleFileMinutes`, `staleFiles` (max 20: name, ageHours, oldest first), `checkedAtUtc`. Errors: `secret_load_failed`, `sftp_read_failed` (+ `attempts`). SFTP error text is kept only in the `WorkerJobs` return string, never sent to OneUptime.
34
+ - **OneUptime (set up by Mark):** Incoming Request. Offline (Match Any): body contains `"alarm":"HIGH"` OR `"status":"error"` OR no request in 120 min (add after first push). Online (Match All): `"alarm":"OK"` AND not `"status":"error"`.
35
+
36
+ ## Gotchas
37
+ - **When it fires:** the receiver failed for that file — DOE has no SO and no 997. Debug per [EDI 850 receive](edi-850-receive-and-997-ack.md).
38
+ - **`secret_load_failed` keeps only the exception class** — a key parse error can echo key material.
39
+ - **No SFTP host-key pinning** (same as Staples / `Sftp/Import`; CSO rated Low).
40
+ - **Do not run it locally** — no AWS role, so it pushes `status:"error"` and opens a real incident. Test the filter with `test/@Mark/TRUE-82779/test_find_stale_files.php`.
41
+ - **After deploy:** run the Core SQL, set `isActive = 1`, check the first `WorkerJobs` result.
42
+
43
+ ## Change history
44
+ - 2026-10-29 — Created; built, ships inactive (TRUE-82779). (mhammontree)
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: nycdoe
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-10-27
9
+ updated: 2026-10-29
10
10
  owners: ["mhammontree"]
11
11
  files:
12
12
  - worker2/Worker/Monitor/Nycdoe.php
@@ -43,6 +43,7 @@ worker2 OneUptime monitor that alarms when any file sits 24h+ on a NYC DOE vendo
43
43
  ### Decisions
44
44
  - **SFTP listing is the source of truth, not `Client_Nycdoe.Files`.** 2.0 deletes only after a good parse; `not_matched_kept` files have no `Files` row; a `failed` row goes stale when a same-name file later succeeds (seen: `DOE-CDW-ASN-10-20-2026.csv`). 1.0 `Logs.FileLog` logs each file once in gated mode, so it cannot show wait time.
45
45
  - Hourly — "we don't really know when customers plan to upload files" (Mark). Any file name counts (Mark).
46
+ - **TRUE-82778** (`Nycdoe/VendorAsnSftpQueue`, vendor ASN file age) is covered by this monitor — closed as duplicate, nothing built (Mark, 2026-10-27).
46
47
 
47
48
  ## Gotchas
48
49
  - **Known gap:** a same-name re-upload resets mtime, so a file the vendor keeps replacing never alarms.
@@ -51,4 +52,5 @@ worker2 OneUptime monitor that alarms when any file sits 24h+ on a NYC DOE vendo
51
52
  - **Do not run it locally once the real URL is in code** — no AWS role on a dev machine, so `_Component_Secrets::resolve()` fails (instance metadata `169.254.169.254`), pushes `status:"error"`, and opens a real incident. Local `Core_2` also has only 1 of 4 `Sftp/Import/Run` rows. Local test: `test/@Mark/TRUE-82941/run_stuck_asn_monitor.php` (read-only listing with the 1.0 library asnprocessor FTP password).
52
53
 
53
54
  ## Change history
55
+ - 2026-10-29 — TRUE-82778 closed as duplicate of this monitor. (mhammontree)
54
56
  - 2026-10-27 — Created; built, ships inactive. `Sftp/Import` `loadImportConfigs()` / `connect()` made public for reuse (TRUE-82941). (mhammontree)
@@ -14,7 +14,7 @@ project: Worker
14
14
  client: nycdoe
15
15
  type: profile
16
16
  status: active
17
- updated: 2026-10-27
17
+ updated: 2026-10-29
18
18
  owners: [mhammontree, sking, bala, rgirish]
19
19
  files: []
20
20
  related:
@@ -23,6 +23,7 @@ related:
23
23
  - features/asn-sftp-ingestion-2-0.md
24
24
  - features/oneuptime-servicenow-update-rejections-monitor.md
25
25
  - features/oneuptime-stuck-asn-files-monitor.md
26
+ - features/oneuptime-edi-850-sftp-queue-monitor.md
26
27
  ---
27
28
 
28
29
  NYC DOE client profile (1.0 worker tier live; 2.0 ASN rewrite in progress), ServiceNow + EDI; open for DOE's platform scope, endpoints, API clients, and where its ticket/ASN/EDI logic lives.
@@ -32,7 +33,7 @@ NYC DOE — TOGA client whose **live** integration runs in the **1.0 worker tier
32
33
 
33
34
  ## Platforms & data
34
35
  - **1.0 worker tier** — all live business logic. Local mirror tables in `db_common` (`NYCDOETickets`, `NYCDOELocations`, `AdvanceShippingNotice*`).
35
- - **2.0 (`worker2` + `_underscore` + `dbchanges2`, tenant `Client_Nycdoe`)** — (1) the OneUptime liveness probe `Monitor/Nycdoe/ServiceNowEndpointHealth` (`worker2/Worker/Monitor/Nycdoe.php`, TRUE-80587, live) the [ServiceNow update-rejection monitor](features/oneuptime-servicenow-update-rejections-monitor.md) (TRUE-82776, ships inactive), and the [stuck ASN files monitor](features/oneuptime-stuck-asn-files-monitor.md) (TRUE-82941, ships inactive); (2) the ASN rewrite `Sftp/Import` → `Sftp/Parse` → Case/ServiceRequest (Phases 1–2; gated delete live in prod since ~2026-10-11, scheduled crons still off) — see [2.0 ASN SFTP Ingestion](features/asn-sftp-ingestion-2-0.md). Entity map: `Case` = 1.0 managed_service_order, `ServiceRequest` = 1.0 repair_order; DOE does not use `Ticket`. Client models: `_underscore/Model/Nycdoe/`. 2.0 does **not** write to ServiceNow yet — the 1.0 RITM/POD crons keep sending until 2.0 takes over, then 1.0 is turned off.
36
+ - **2.0 (`worker2` + `_underscore` + `dbchanges2`, tenant `Client_Nycdoe`)** — (1) the OneUptime liveness probe `Monitor/Nycdoe/ServiceNowEndpointHealth` (`worker2/Worker/Monitor/Nycdoe.php`, TRUE-80587, live) the [ServiceNow update-rejection monitor](features/oneuptime-servicenow-update-rejections-monitor.md) (TRUE-82776, ships inactive), the [stuck ASN files monitor](features/oneuptime-stuck-asn-files-monitor.md) (TRUE-82941, ships inactive), and the [EDI 850 SFTP queue monitor](features/oneuptime-edi-850-sftp-queue-monitor.md) (TRUE-82779, ships inactive); (2) the ASN rewrite `Sftp/Import` → `Sftp/Parse` → Case/ServiceRequest (Phases 1–2; gated delete live in prod since ~2026-10-11, scheduled crons still off) — see [2.0 ASN SFTP Ingestion](features/asn-sftp-ingestion-2-0.md). Entity map: `Case` = 1.0 managed_service_order, `ServiceRequest` = 1.0 repair_order; DOE does not use `Ticket`. Client models: `_underscore/Model/Nycdoe/`. 2.0 does **not** write to ServiceNow yet — the 1.0 RITM/POD crons keep sending until 2.0 takes over, then 1.0 is turned off.
36
37
  - **TOGa Desk:** DOE is client id **16** (`db_togadesk`) — repair orders, managed service orders, the "#received / #ordered" Receiving Summary UI.
37
38
  - **NetSuite:** Sales Orders, PO reconciliation, item receipts, Item Fulfillments, invoices (SuiteTalk toolkit in `library`).
38
39
  - **`dbchanges` (1.0):** DOE schema + **data-repair** changes ship here (`dbchanges/Core/` targets the legacy/V1 `Core` schema). Stranded-ASN recoveries are data-only repairs — see the [1.0 dbchanges authoring workflow](../../1.0/apps/dbchanges/workflows/authoring-and-shipping-sql-files.md).
@@ -20,7 +20,7 @@ project: _Underscore
20
20
  client: nychh
21
21
  type: profile
22
22
  status: active
23
- updated: 2026-10-22
23
+ updated: 2026-10-29
24
24
  owners: [jcardinal, apeterson, bala, akhokhani, mhammontree, rgirish]
25
25
  files:
26
26
  - dbchanges2/Client_Nychh/2026-10-18a - NewYorkWarehouseLocation.sql
@@ -128,7 +128,7 @@ TOGA 2.0 client on `_underscore`, prod schema `Client_Nychh`. Supply-chain / ful
128
128
  - **PO links are UPSTREAM (`PurchaseOrders_SalesOrders`), not downstream.** The downstream `sales-order-purchase-orders` route returns an empty array for NYCHH by design, and the shared `_purchaseOrders` field (downstream-only) resolves to `null` for this tenant — open gap. See [PO Number direction](./features/po-number-upstream-direction.md).
129
129
 
130
130
  ## Notes
131
- - **1.0 cron "Send NYCHH Sales Orders" sends nothing (2026-10-15, TRUE-82761, parked).** `worker/crons/toga2/nychh/send_sales_order.php` (`*/5`) emails each `SalesOrders` row with `c_dtTransmittedToNYCHH IS NULL` as an xlsx to the warehouse. In prod all 125 rows have it NULL and none has `createdByUserId` (all NetSuite-imported), so its `INNER JOIN Users ON createdByUserId` drops every row. Supply orders are now `TransferOrders`. A monitor on that column would be always HIGH or always OK — don't build one. Open: ask Jelena if the transmission is still used.
131
+ - **1.0 cron "Send NYCHH Sales Orders" sends nothing (2026-10-15, TRUE-82761, parked).** `worker/crons/toga2/nychh/send_sales_order.php` (`*/5`) emails each `SalesOrders` row with `c_dtTransmittedToNYCHH IS NULL` as an xlsx to the warehouse. In prod all 125 rows have it NULL and none has `createdByUserId` (all NetSuite-imported), so its `INNER JOIN Users ON createdByUserId` drops every row. Supply orders are now `TransferOrders`. A monitor on that column would be always HIGH or always OK — don't build one. Open: is the transmission still used? Owner unknown — Jelena has left TOGA Technology (2026-10-29).
132
132
  - **`Items.title` is NULL on most NYCHH rows — `description` is the populated label field.** Confirmed 2026-09-03 on client-sandbox (a `transfer-order-items` line returned `"title": null` with a real `partNumber`). Every screen that labels an item for this tenant needs the `title || description` chain — the Transfer Order record modal and the Create modal's item picker both carry it. The real fix is populating `Items.title`.
133
133
  - **Role ids in `Client_Nychh` (verified 2026-09-01):** `1 = Base`, `2 = Developer`, `3 = API`. Role ids differ per client DB — resolve by **name** in migrations, never hard-code these.
134
134
  - **The Agilant API (`Client_Nychh.Apis` id 1) holds BOTH Base (1) and API (3) via `Apis_Roles` (2026-10-06).** Record ACL = union, so checking only the API role misleads. Base grants full CRUD on 28/312/313 (ItemFulfillments, TransferOrders, TransferOrderItems); API role has no DELETE on 312/313 and no row for 28. Interceptors: record 312 has POST/POST + POST/PUT only (none on DELETE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.1206",
3
+ "version": "1.0.1208",
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",