toga-ai 1.0.0

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.
Files changed (62) hide show
  1. package/.claude/settings.json +119 -0
  2. package/.claude-plugin/marketplace.json +87 -0
  3. package/.claude-plugin/plugin.json +22 -0
  4. package/CLAUDE.md +161 -0
  5. package/README.md +72 -0
  6. package/agents/framework-pattern-checker.md +67 -0
  7. package/agents/harness-optimizer.md +102 -0
  8. package/agents/knowledge-writer.md +62 -0
  9. package/agents/php-build-resolver.md +51 -0
  10. package/agents/php-reviewer.md +51 -0
  11. package/agents/planner.md +88 -0
  12. package/agents/session-capture.md +101 -0
  13. package/agents/sql-reviewer.md +67 -0
  14. package/contexts/dev.md +43 -0
  15. package/contexts/research.md +49 -0
  16. package/contexts/review.md +37 -0
  17. package/knowledge/1.0/apps/library/INDEX.md +5 -0
  18. package/knowledge/1.0/apps/library/architecture.md +105 -0
  19. package/knowledge/1.0/apps/worker/INDEX.md +5 -0
  20. package/knowledge/1.0/apps/worker/architecture.md +223 -0
  21. package/knowledge/1.0/standards/backend-php.md +450 -0
  22. package/knowledge/2.0/apps/_underscore/INDEX.md +6 -0
  23. package/knowledge/2.0/apps/_underscore/architecture.md +183 -0
  24. package/knowledge/2.0/apps/_underscore/features/recursive-item-fulfillments.md +111 -0
  25. package/knowledge/2.0/apps/api2/INDEX.md +5 -0
  26. package/knowledge/2.0/apps/api2/architecture.md +162 -0
  27. package/knowledge/2.0/apps/worker2/INDEX.md +6 -0
  28. package/knowledge/2.0/apps/worker2/architecture.md +127 -0
  29. package/knowledge/2.0/apps/worker2/features/creating-worker-actions.md +135 -0
  30. package/knowledge/2.0/standards/backend-php.md +710 -0
  31. package/knowledge/CONVENTIONS.md +117 -0
  32. package/knowledge/INDEX.md +19 -0
  33. package/knowledge/clients/.gitkeep +0 -0
  34. package/knowledge/registry.json +7 -0
  35. package/knowledge.js +384 -0
  36. package/mcp-configs/README.md +72 -0
  37. package/mcp-configs/mcp-servers.json +23 -0
  38. package/package.json +50 -0
  39. package/rules/README.md +53 -0
  40. package/rules/common/coding-style.md +123 -0
  41. package/rules/common/git-workflow.md +72 -0
  42. package/rules/common/security.md +118 -0
  43. package/rules/common/testing.md +74 -0
  44. package/rules/php/app-framework.md +104 -0
  45. package/rules/php/underscore-framework.md +111 -0
  46. package/scripts/harness.js +605 -0
  47. package/scripts/hooks/evaluate-session.js +55 -0
  48. package/scripts/hooks/post-edit-validate.js +102 -0
  49. package/scripts/hooks/session-end.js +13 -0
  50. package/scripts/hooks/session-start.js +57 -0
  51. package/scripts/install.js +611 -0
  52. package/scripts/pre-commit +46 -0
  53. package/skills/capture/SKILL.md +294 -0
  54. package/skills/code-review/SKILL.md +140 -0
  55. package/skills/create-elastic-beanstalk/SKILL.md +217 -0
  56. package/skills/harness-audit/SKILL.md +152 -0
  57. package/skills/kickoff/SKILL.md +151 -0
  58. package/skills/php-patterns/SKILL.md +296 -0
  59. package/skills/session-resume/SKILL.md +156 -0
  60. package/skills/session-save/SKILL.md +158 -0
  61. package/skills/sync-team-skills/SKILL.md +87 -0
  62. package/sync-skills.js +71 -0
@@ -0,0 +1,5 @@
1
+ # worker (Worker) — 1.0 knowledge
2
+
3
+ | Doc | Summary | Files |
4
+ |-----|---------|-------|
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 |
@@ -0,0 +1,223 @@
1
+ ---
2
+ title: Worker (1.0 Framework) Architecture
3
+ framework: "1.0"
4
+ repo: worker
5
+ project: Worker
6
+ client: shared
7
+ type: architecture
8
+ status: active
9
+ updated: 2026-06-08
10
+ owners: [jcardinal]
11
+ files:
12
+ - worker/index.php
13
+ - worker/_/app/framework.php
14
+ - worker/crons/
15
+ - worker/schedules/
16
+ - worker/ebs/cron.worker.php
17
+ - worker/.ebextensions/035_cron.worker.config
18
+ related:
19
+ - ../library/architecture.md
20
+ ---
21
+
22
+ ## Summary
23
+
24
+ `worker` is the legacy (**1.0** `App_` framework) **background-job tier**. It is a single
25
+ codebase of ~550 standalone PHP CRON scripts (under `crons/`) that handle everything the web
26
+ apps must not do inline: notifications/reports, database backups, third-party syncs (NetSuite,
27
+ EDI partners, NYC DOE, ManageEngine, Syncro…), cloud-infrastructure maintenance, and the
28
+ TOGa / TOGaDesk / TOGa-2 client integrations.
29
+
30
+ In production the tier is **one Elastic Beanstalk environment running 7 instances**, each of
31
+ which **self-elects a distinct role** (`notification`, `database`, `infrastructure`, `toga`,
32
+ `togadesk`, `sync`, `catalog`) and runs only that role's slice of the schedule. Platform:
33
+ **PHP 7.2 running on 64-bit Amazon Linux / 2.8.8**.
34
+
35
+ > This repo is the "legacy worker" referred to in the 1.0 back-end standard's
36
+ > *Asynchronous / Background Processing* note — long-running work is handed off here rather
37
+ > than run inside a web request. It builds on the `library` core (see
38
+ > [Library architecture](../library/architecture.md)).
39
+
40
+ ## How a job becomes a cron (the dispatch pipeline)
41
+
42
+ There is **no in-process scheduler**. Jobs are plain PHP files invoked by the OS `crontab`,
43
+ which is generated at deploy time:
44
+
45
+ 1. **`schedules/cron.<env>.json`** declares the jobs for an environment. Each entry is
46
+ `{ active, name, schedule (5-field cron expr), cron | command | url }`:
47
+ - `cron` → `php /var/www/html/crons/<path>` (the common case)
48
+ - `command` → run the raw shell command (e.g. `apachectl -k graceful`)
49
+ - `url` → `wget http://localhost<url>`
50
+ - `active: 0` disables the entry without deleting it.
51
+ 2. **`.ebextensions/035_cron.worker.config`** runs `ebs/cron.worker.php` as an EB
52
+ `container_command` on every deploy.
53
+ 3. **`ebs/cron.worker.php`** reads the `ENVIRONMENT` env var, loads `cron.<env>.json`, and for
54
+ the `worker` env *also* loads the **role-specific** `cron.worker.<role>.json` (see role
55
+ election below). It writes the assembled crontab into the EB appdeploy enact hook
56
+ `/opt/elasticbeanstalk/hooks/appdeploy/enact/01_cron.sh`, which installs it via
57
+ `crontab cronjobs`.
58
+ 4. The OS cron daemon then runs each `php /var/www/html/crons/<path>` on its schedule.
59
+
60
+ So **to add/modify a scheduled job you edit a `schedules/*.json` file and redeploy** — the cron
61
+ path is relative to `crons/`. Editing the `.php` alone does nothing until it is referenced by a
62
+ schedule entry.
63
+
64
+ ### Schedule files
65
+
66
+ | File | Purpose |
67
+ |---|---|
68
+ | `cron.worker.json` | **Base — runs on every production worker** regardless of role: `apachectl -k graceful` (closes DB connections) + `worker/worker_heartbeat.php` (every minute). |
69
+ | `cron.worker.<role>.json` | Per-role job set. The 7 roles: `notification` (~88), `sync` (~96), `toga` (8), `togadesk` (14), `infrastructure` (12), `database` (3), `catalog` (2). |
70
+ | `cron.{alpha,beta,demo,hotfix,sprint,stage,ui}.json` | Non-prod environments — each runs the *entire* job set on a single box (no role splitting). |
71
+
72
+ ## Production topology: 7 self-electing workers
73
+
74
+ The 7 production instances share one image; **role assignment is dynamic, not baked in.** This
75
+ is the most important and least obvious part of the architecture.
76
+
77
+ - The roster lives in DB table **`Workers`** (`workerInstanceId`, `workerName`, `dtHeartbeat`)
78
+ in the **`Vision_Log`** database (config group `db_log`).
79
+ - The canonical role list is **`App_Worker::$desiredWorkers`** (in `library`): `notification`,
80
+ `database`, `infrastructure`, `toga`, `togadesk`, `sync`, `catalog`. (`ebs/cron.worker.php`
81
+ carries its own near-duplicate copy of this list — keep the two in sync.)
82
+ - **Role election (at deploy, in `ebs/cron.worker.php`):** the script connects to `Vision_Log`,
83
+ reads `Workers`, computes which roles are not yet claimed, and assigns this instance (keyed by
84
+ its EC2 instance-id) the **first unclaimed role** — `INSERT`/`UPDATE`-ing its `Workers` row.
85
+ It then appends that role's `cron.worker.<role>.json` to the crontab.
86
+ - **Heartbeat / self-healing (`crons/worker/worker_heartbeat.php`, every minute on all 7):**
87
+ - Updates this instance's `dtHeartbeat = NOW()`.
88
+ - First time an instance is seen, tags the EC2 instance `Worker=<ROLE>-WORKER`, **associates
89
+ the role's dedicated Elastic IP** (`App_Worker::$associateWorkerWithElasticIpAllocationId`),
90
+ and emails ops that the role was instated.
91
+ - If any worker's heartbeat is stale (> **270 s** in the heartbeat; the deploy script uses a
92
+ separate **7-minute** cutoff), it **terminates that EC2 instance** via the AWS API and
93
+ deletes its `Workers` row. EB replaces the instance, which re-runs deploy, finds the now-open
94
+ role, and re-claims it → automatic self-recovery. Each instance staggers with a random
95
+ `sleep(rand(1,50))` to avoid races.
96
+ - **`App_Worker::getWorkerName()`** returns the current instance's role; helpers
97
+ `isPrimaryWorker()` / `isPrimaryDbWorker()` (currently commented out) gated "primary-only"
98
+ jobs in an older model.
99
+
100
+ > Net effect: roles are a **claim-the-first-free-slot pool**, and a dead worker's role is
101
+ > reclaimed by its replacement within minutes. There is no static instance→role mapping to edit.
102
+
103
+ ## Anatomy of a cron script
104
+
105
+ Every script is self-contained and follows this boilerplate:
106
+
107
+ ```php
108
+ <?php
109
+ require_once('_.php'); // library bootstrap (see below)
110
+ App_Framework_Worker::initialize(); // worker framework init
111
+ App_Framework::cronInitialization($timeLimitSeconds); // lock + logging + time limit
112
+
113
+ // ... the job's work, written against App_Model / App_Database / App_Api / App_Email ...
114
+
115
+ App_Framework::cronFinished(); // log completion
116
+ App_Database::closeConnections();
117
+ ```
118
+
119
+ - **`App_Framework_Worker::initialize()`** (`_/app/framework.php`) extends the library
120
+ `App_Framework`, disables sessions, registers the vendored libraries this tier needs
121
+ (PHPExcel reader/writer, NetSuite toolkit, phpseclib, php-imap2), and initializes **Sentry**
122
+ (project `worker1`, environment = the EB env).
123
+ - **`App_Framework::cronInitialization($timeLimitSeconds = 7200, $okayToPerformOnReadCluster =
124
+ false, $exitIfProcessAlreadyRunning = true)`** (in `library`):
125
+ - **Overlap guard** — `exitIfProcessRunning()` scans `ps -ef` for another PHP process running
126
+ the same script file and exits early if found (prevents a slow job from stacking on its next
127
+ tick). This is a process-table lock, not a file lock.
128
+ - `set_time_limit()`, `date_default_timezone_set('America/Chicago')`, optional read-cluster
129
+ routing.
130
+ - Logs a `CRON_START` row to the `Log` table (`db_log`) and a row to `CronJobExecutions`
131
+ (`db_common`) for monitoring.
132
+ - **`cronFinished()`** logs completion; **`App_Database::closeConnections()`** must be called so
133
+ the connection is released before the next tick.
134
+ - **Sentry cron monitors (newer jobs):** wrap the body with
135
+ `if (App_Framework::isProcessRunning()) exit;` then `App_Worker::checkIn('<monitor-slug>')`
136
+ … work … `App_Worker::checkOut()`. A missing `checkOut()` makes Sentry flag the run as missed.
137
+
138
+ ## Directory layout
139
+
140
+ ```
141
+ worker/
142
+ ├── index.php # thin web entry: require _.php; App_Framework_Worker::{initialize,renderIndex}
143
+ ├── _/app/ # APPROOT marker + app-level App_ overrides
144
+ │ ├── framework.php # App_Framework_Worker (extends library App_Framework)
145
+ │ └── frameworkindex.php # App_FrameworkIndex_Worker (renders the minimal web index)
146
+ ├── crons/ # ~553 standalone job scripts, grouped by domain (below)
147
+ ├── schedules/ # *.json crontab definitions (per env + per role)
148
+ ├── mvc/ # minimal web routes (get / login / logout / 404) — worker has a thin web face
149
+ ├── common/ # header.php / footer.php for the web face
150
+ ├── ebs/ # deploy-time provisioning scripts (run by .ebextensions)
151
+ ├── .ebextensions/ # EB platform config (apache, https, imap, s3fs, ldap, redis, composer, git, cron)
152
+ ├── config.<env>.ini # per-environment DB connections + settings (one per dev/stage/prod box)
153
+ ├── composer.json/vendor/ # Composer deps (Sentry, AWS SDK, …) — note: 1.0 apps normally vendor via library
154
+ └── assets/
155
+ ```
156
+
157
+ `_.php` itself is **not committed** — it is the `library` framework bootstrap, made available on
158
+ the box at deploy (library is git-cloned to `/var/www/library`; the `_/` folder is the
159
+ `__APPROOT__` marker the autoloader walks up to find). App-level classes named `App_*` resolve to
160
+ `_/app/...`, shadowing the library copies (standard 1.0 autoloader behavior).
161
+
162
+ ### `crons/` domains
163
+
164
+ | Folder | ~Files | What it does |
165
+ |---|---:|---|
166
+ | `notifications/` | 115 | Emailed reports, reminders, campaigns, CSAT surveys, daily/weekly/monthly client reports (Office Depot, Walmart, NYC DOE, Canon, Newegg, InComm…). Also `infrastructure/send_emails.php` outbound mail queue + code-commit digests. |
167
+ | `sync/` | 97 | Record syncing & reconciliation: NetSuite, EDI partners (Prudential, Canon, DOE), ManageEngine, Syncro, Active Directory, Staples, tracking-number reconciliation. |
168
+ | `toga2/` | 162 | **TOGa-2 (2.0) client integrations** — per-client subfolders (`compass`, `compasscanada`, `prudential`, `aig`, `rate`, `wje`, `elite`, `startech`, `nychh`, `northwell`, `sap_ariba`, `bankofamerica`, `managelife`, `quad`…), the NetSuite `togasupply` syncs, and `forecast2/` sales/order/opportunity imports. |
169
+ | `toga/` | 23 | Core TOGa (1.0) processors: order processor, Walmart e-comm/B2B, subscription billing, garbage collector, client sync. |
170
+ | `infrastructure/` | 18 | DB cluster failover, AWS GuardDuty auto-terminate, domain documenter, `system_monitors.php`, sprint open/close automation, forecast2 imports. |
171
+ | `vendorinvoiceretrieval/` | 18 | Automated vendor-invoice retrieval. |
172
+ | `catalog/` | 17 | Catalog DB reconciliation (Office Depot inventory imports). |
173
+ | `maintenance/` | 9 | Hourly/daily/weekly maintenance + TOGaDesk ticket archiving. |
174
+ | `togadesk/` | 8 | TOGaDesk helpdesk: inactive-people cleanup, UMA customer/ticket sync, task reminders, repair-order status. |
175
+ | `test_active_directory/` | 5 | AD integration test scripts. |
176
+ | `database/` | 4 | Backups, restores, restore scheduling. |
177
+ | `worker/` | 2 | `worker_heartbeat.php` (the self-healing heartbeat) + helper. |
178
+ | `DOA/` | 75 | **Dead-On-Arrival** — deprecated jobs kept for reference, **not scheduled**. Per the 1.0 standard, do not build on `DOA_`-prefixed code. |
179
+
180
+ ## Cross-repo bridges (cloned at deploy via `ebs/git.json`)
181
+
182
+ The worker box is assembled from several repos, not just this one. `ebs/git.json` clones:
183
+
184
+ | Repo | Deployed to | Why |
185
+ |---|---|---|
186
+ | `library` | `/var/www/library` | 1.0 framework core (`_.php`, all `App_*`). |
187
+ | `resources` | `…/resources` | Front-end assets (not used by crons). |
188
+ | `dbchanges` | `/var/www/dbchanges` | DB migration scripts. |
189
+ | `togadesk` | `…/ontrack` | TOGaDesk app — note `cron.worker.togadesk.json` runs `../ontrack/crons/tickets_prod.php` and `monitoring_prod.php` **out of the sibling repo**. |
190
+ | `_underscore` | `/var/www/_underscore` | **2.0 framework core** — present because the `toga2/` client integrations reach into 2.0 (TOGa-2 / TOGaSupply). This is a genuine 1.0↔2.0 bridge living inside a 1.0 app. |
191
+
192
+ ## Deploy-time provisioning (`.ebextensions/` → `ebs/`)
193
+
194
+ Container commands set up the box on each deploy: apache tuning, force HTTP→HTTPS, install IMAP,
195
+ **mount S3 via s3fs**, export/cache folders, LDAP, php.ini, Redis, Composer, clone the git
196
+ libraries above, permit library executables, **symbolic links** (`ebs/setup_symbolic_links.php`
197
+ links paths from `links.<env>.json`, e.g. `ontrack/desk/uploads` → an S3-backed folder), run
198
+ `dbchanges`, and finally generate the crontab. `index.php` exists only to give the box a trivial
199
+ web face (`mvc/` GET routes for login/logout/404); the tier's real work is the crons.
200
+
201
+ ## Conventions & gotchas
202
+
203
+ - **Schedules are the source of truth for what runs.** A `.php` under `crons/` that no schedule
204
+ references is dormant (and may be `DOA/`). Set `active: 0` to disable without deleting.
205
+ - **All cron paths are relative to `crons/`** except the `../ontrack/...` jobs that run from the
206
+ sibling TOGaDesk repo.
207
+ - **Always call `App_Database::closeConnections()`** at the end; rely on `cronInitialization`'s
208
+ overlap guard rather than rolling your own lock.
209
+ - **Timezone is `America/Chicago`** everywhere; cron expressions are in that zone, but several
210
+ `togadesk/` UMA jobs are commented with their UTC equivalents — read the `name` carefully.
211
+ - **Two stale-worker cutoffs exist** (270 s in the heartbeat, 7 min in the deploy script) and the
212
+ `$desiredWorkers` list is duplicated in `ebs/cron.worker.php` and `App_Worker` — keep them
213
+ aligned when changing roles.
214
+ - **Per-role Elastic IPs** mean a role's outbound IP is stable across instance replacement —
215
+ relevant for partner allow-lists (EDI/SFTP endpoints).
216
+
217
+ ## ⚠ Security note — committed secrets
218
+
219
+ Several files contain **live credentials checked into the repo**: AWS access keys
220
+ (`ebs/cron.worker.php`, and in a comment in `crons/worker/worker_heartbeat.php`), GitHub PATs
221
+ (`ebs/git.json`), and a Sentry DSN (`_/app/framework.php`). These should be rotated and moved to
222
+ environment variables / SSM Parameter Store rather than living in source. (Values are
223
+ deliberately not reproduced here.)