toga-ai 1.0.296 → 1.0.298

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.
@@ -8,5 +8,5 @@
8
8
  | [Ticket Email Notifications (notifications table)](features/notifications.md) | Which TOGa Desk emails fire for a given client is driven **entirely by data**, not code: the `TOGaDeskSupport.notifications` table holds one row per `(clientid, | desk/includes/classes/class.notification.php, crons/tickets.php, crons/tickets_prod.php |
9
9
  | [REST API (RPC-over-POST) & API-Key Auth](features/rest-api.md) | TOGa Desk exposes a programmatic API at `desk/api/`. | desk/api/index.php, desk/api/resources/tickets.php, desk/api/resources/assets.php, desk/api/resources/authenticate.php, desk/includes/classes/class.apikey.php, desk/includes/functions.php |
10
10
  | [SMB Contract Editing & the clientMspId Corruption Trap](features/smb-contract-editing.md) | The SMB contracts page (`/desk/?route=toga/smbcontracts&togaClientId=<id>`) edits `TOGA_*.SMBContracts` rows via a modal. | desk/template/modals/toga/smbcontracts/smbContract.php, desk/includes/controllers/modals/toga/smbcontracts/smbContract.php, desk/includes/controllers/actions/toga/smbcontracts/smbContract.php, desk/includes/controllers/actions/toga/smbcontracts/edit.php |
11
- | [Ticket Lifecycle (class.ticket.php)](features/ticket-lifecycle.md) | All TOGa Desk ticket creation and reply handling funnels through `Ticket` in `desk/includes/classes/class.ticket.php`. | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/api/resources/tickets.php, crons/tickets.php, desk/includes/controllers/actions/tickets/merge.php |
11
+ | [Ticket Lifecycle (class.ticket.php)](features/ticket-lifecycle.md) | All TOGa Desk ticket creation and reply handling funnels through `Ticket` in `desk/includes/classes/class.ticket.php`. | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/includes/controllers/actions/tickets/addReply.php, desk/api/resources/tickets.php, desk/api/resources/ticket_replies.php, crons/tickets.php, crons/pipe.php, desk/includes/controllers/actions/tickets/merge.php |
12
12
  | [Standalone PHP Test Script Bootstrap (TOGa Desk)](workflows/standalone-test-scripts.md) | How to write a standalone CLI PHP script that bootstraps the TOGa Desk framework for read-only testing of desk classes (e.g. | |
@@ -6,8 +6,8 @@ project: TOGa Desk
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-15
10
- owners: ["jcardinal"]
9
+ updated: 2026-07-09
10
+ owners: ["jcardinal", "dfranks"]
11
11
  files:
12
12
  - crons/tickets.php
13
13
  - crons/tickets_prod.php
@@ -61,6 +61,14 @@ Writes `tickets`, `tickets_replies`, attachment `files`; reads config from the `
61
61
  - **Auto-reply filters** are partly hardcoded (e.g. ignore `TechHub@compass-usa.com`
62
62
  "Automatic reply:" messages).
63
63
  - Attachments are deleted locally immediately after the S3 upload with **no retry** if S3 fails.
64
+ - **`adminid` clobber on reply routing:** after `emailToTicket()` sets `$data['adminid']` from
65
+ the FROM address (~line 1407), an asset lookup (~line 1437-1442) overwrites it with the
66
+ replying user's asset's assigned admin. On a matched-ticket reply this makes a user's email
67
+ look like a staff reply in `addReply()` (`isAdminReply=true`), so `Awaiting User` etc. never
68
+ transition. The asset-derived adminid is for NEW-ticket auto-assign in `add()`, not reply
69
+ classification. See the ticket-lifecycle doc.
64
70
 
65
71
  ## Change history
72
+ - 2026-07-09 — documented the `emailToTicket()` adminid-overwrite reply misclassification
73
+ (TRUE-80114 planning investigation) (dfranks)
66
74
  - 2026-06-15 — documented from a source read of `crons/tickets*.php` and `emailToTicket()` (jcardinal)
@@ -6,13 +6,16 @@ project: TOGa Desk
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-12
10
- owners: ["mhammontree"]
9
+ updated: 2026-07-09
10
+ owners: ["mhammontree", "dfranks"]
11
11
  files:
12
12
  - desk/includes/classes/class.ticket.php
13
13
  - desk/includes/controllers/actions.php
14
+ - desk/includes/controllers/actions/tickets/addReply.php
14
15
  - desk/api/resources/tickets.php
16
+ - desk/api/resources/ticket_replies.php
15
17
  - crons/tickets.php
18
+ - crons/pipe.php
16
19
  - desk/includes/controllers/actions/tickets/merge.php
17
20
  related:
18
21
  - 1.0/apps/togadesk/features/smb-contract-editing.md
@@ -40,6 +43,26 @@ Creation paths — all funnel through `Ticket::add($data)`:
40
43
  - Ticket merge (`actions/tickets/merge.php`) inserts a master ticket directly — it copies
41
44
  fields from the child, including `customerid` (since June 2026).
42
45
 
46
+ ### Reply entry points (FOUR doors — only two transition status)
47
+ There are **four** inbound reply paths, but the status-transition switch lives ONLY inside
48
+ `Ticket::addReply()`. Any door that does not route through `addReply()` will not change status:
49
+ - **IMAP-poll cron** — `crons/tickets.php` → `emailToTicket()` → `addReply()`. ✅ transitions.
50
+ - **Mail pipe** — `crons/pipe.php:113` → `emailToTicket()` (legacy 6-arg call) → `addReply()`.
51
+ ✅ transitions.
52
+ - **Desk REST API** — `desk/api/resources/ticket_replies.php` `'add'` → `addReply($data)`
53
+ directly. ✅ transitions.
54
+ - **togaview customer portal** — the portal reply form (`action="/ticket"` + `newReplyBtn`,
55
+ from the generic `common/togaview/ticket.php` and client variants e.g.
56
+ `common/newcenturyholdingsllc/ticket.php`, `common/towfoundation/ticket.php`) POSTs to
57
+ `togaview/mvc/ticket/post.php` `newReplyBtn` handler (~line 388). This handler inserts the
58
+ reply row **directly** via `App_Model_TogaDesk_TicketReply` and **bypasses the `Ticket` class
59
+ entirely** — it never updates `tickets.status`, `tickets_replies.newStatus`, or
60
+ `tickets_history`. ❌ **no status transition.** This is the gap behind "a reply from outside
61
+ Desk doesn't move the ticket."
62
+
63
+ The in-Desk agent reply path is `desk/includes/controllers/actions/tickets/addReply.php` →
64
+ `Ticket::addReply($_POST)`.
65
+
43
66
  ## How it works
44
67
 
45
68
  ### `Ticket::deriveCustomerId(int, string): ?int` (added June 2026)
@@ -64,6 +87,11 @@ means the reply lands but the ticket status silently never changes (no history e
64
87
  re-entry). That was the On Hold SLA bug; `case 'On Hold'` (admin ⇒ stays On Hold, client ⇒
65
88
  Open) was added June 2026.
66
89
 
90
+ - **`origin` is the reliable "reply came from outside Desk" signal.** The in-app agent reply
91
+ path (`addReply.php` → `addReply($_POST)`) carries **no `origin` key**, whereas
92
+ `emailToTicket()` sets `tickets.origin = 'EMAIL'` and togaview sets `'TOGAVIEW'` on new
93
+ tickets. Absence of `origin` on a reply therefore identifies an in-app agent reply; presence
94
+ identifies an external reply. This is the safe hook for any "external reply → status" logic.
67
95
  - Special case: an admin replying to their OWN ticket is treated as a user reply.
68
96
  - Auto-assign on first staff reply requires the role perm `allowAutoAssign` via
69
97
  profiles/profile_departments.
@@ -88,11 +116,29 @@ backfill (e.g. craftex:
88
116
  column (close time would have to come from `tickets_replies.newStatus='Closed'` or
89
117
  `tickets_history`). Implementing it is a product decision: what happens to a lapsed reply
90
118
  (attach-but-closed / new ticket / bounce)?
119
+ - **`emailToTicket()` clobbers `adminid`, misclassifying user email replies as staff replies.**
120
+ After matching the sender and setting `$data['adminid']` from the FROM address (~line 1407),
121
+ an asset lookup (~line 1437-1442) **overwrites** `$data['adminid']` with the replying user's
122
+ asset's assigned admin. When the matched ticket goes to `addReply()`, the user's reply is
123
+ read as an admin/staff reply (`isAdminReply=true`), so states like `Awaiting User` stay put
124
+ instead of transitioning to `Open`. The asset-derived adminid is meant for auto-assigning
125
+ NEW tickets in `add()`, not for classifying replies — `adminid` serves two conflicting
126
+ purposes here.
127
+ - **Cross-app DB coupling:** togadesk and togaview share the `TOGaDeskSupport` database
128
+ (`tickets`, `tickets_replies`, `tickets_history`, `people`), so a reply/status behavior
129
+ change on either side is visible to both immediately. togaview cannot load Desk's
130
+ `class.ticket.php`, so its writes go through the `App_Model_TogaDesk_*` models
131
+ (`TicketReply`, `TicketsHistory`, `Ticket`) — any shared status logic must account for that
132
+ the portal path never instantiates `Ticket`.
91
133
  - Legacy `togadesk/includes/class.ticket.php` has active email-reopen logic; the live
92
134
  `desk/includes` version has it commented out — email replies never set status to "Reopened"
93
135
  via `emailToTicket()`.
94
136
 
95
137
  ## Change history
138
+ - 2026-07-09 — mapped the four reply entry points (togaview portal bypasses `addReply()` so
139
+ never transitions status); documented `emailToTicket()` adminid-overwrite reply
140
+ misclassification, the `origin`-absence agent-reply signal, and togadesk↔togaview shared-DB
141
+ coupling — from TRUE-80114 planning source investigation (dfranks)
96
142
  - 2026-06-12 — documented from craftex MSP portal debugging session (mhammontree)
97
143
  - 2026-06 — added `deriveCustomerId()`; added `On Hold` case to addReply() switch; merge
98
144
  copies `customerid` (mhammontree)
@@ -21,3 +21,4 @@
21
21
  | [Surface Resolver (_Model_Core_Surface::resolve — replaces Page::meta)](features/surface-resolver.md) | The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a cached resolver, `_Model_Core_Surface::resolve(&$api, string | _underscore/Model/Core/Surface.php, _underscore/Model/Client/AclRecordScript.php, _underscore/Model/Core/RecordScript.php, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Quad/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Client_CompassCanada/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, _underscore/Model/Core/SurfaceElement.php, _underscore/Model/Core/Action.php, _underscore/Model/Core/Vocabulary.php, _underscore/Model/Core/VocabularyTerm.php, _underscore/Model/Core/Message.php, _underscore/Model/Client/SurfaceOverride.php, _underscore/Model/Client/MessageTranslation.php, _underscore/Model/Client/ThemeToken.php, _underscore/Model/Core/Page.php |
22
22
  | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19b - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql |
23
23
  | [Units for Items for Purchase Orders — Data Structure](features/units-for-items-for-purchase-orders.md) | Describes how unit (serialized inventory) data is linked to sales-order and purchase-order line items behind the `units-for-items-for-purchase-orders` TableView | |
24
+ | [Running a 2.0 App Locally (browser, end-to-end via api2)](workflows/running-a-2.0-app-locally.md) | The full dependency chain required to run a 2.0 client app **through the browser**, end-to-end, against a **local `api2`** (e.g. | _underscore/Environment.php, _underscore/Config.php, _underscore/Database.php, _underscore/Route.php, api2/Component/Api/V2/V2.php, api2/index.php, api2/.htaccess |
@@ -6,8 +6,8 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-18
10
- owners: ["dfranks", "jcardinal"]
9
+ updated: 2026-07-09
10
+ owners: ["dfranks", "jcardinal", "mhammontree"]
11
11
  files:
12
12
  - _underscore/Database.php
13
13
  - _underscore/ApiRequest.php
@@ -93,6 +93,7 @@ registration is region-aware in `api2/Controller/Index.php`; the alias const is
93
93
 
94
94
  ## Change history
95
95
 
96
+ - 2026-07-09 — Linked the new [Running a 2.0 app locally](../workflows/running-a-2.0-app-locally.md) runbook, which frames these three connections as one requirement of a full local browser run. (mhammontree)
96
97
  - 2026-07-07 — Noted the new shared **Cache** cluster (`Databases` id 145, alias `DB_CACHE`,
97
98
  region-aware) added for the api2 cross-client retrieval engine. (jcardinal)
98
99
  - 2026-06-18 — Documented after the `Logs_Growrk`/`Logs_Aig` local 500s while testing the
@@ -101,4 +102,5 @@ registration is region-aware in `api2/Controller/Index.php`; the alias const is
101
102
  ## Related docs
102
103
 
103
104
  - [_underscore architecture](../architecture.md)
105
+ - [Running a 2.0 app locally (browser end-to-end)](../workflows/running-a-2.0-app-locally.md) — the full local-stack runbook these three connections are one requirement of.
104
106
  - [NetSuite → TOGa Supply per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md) — the work that surfaced this.
@@ -0,0 +1,124 @@
1
+ ---
2
+ title: Running a 2.0 App Locally (browser, end-to-end via api2)
3
+ framework: "2.0"
4
+ repo: _underscore
5
+ project: _Underscore
6
+ client: shared
7
+ type: workflow
8
+ status: active
9
+ updated: 2026-07-09
10
+ owners: ["mhammontree"]
11
+ files:
12
+ - _underscore/Environment.php
13
+ - _underscore/Config.php
14
+ - _underscore/Database.php
15
+ - _underscore/Route.php
16
+ - api2/Component/Api/V2/V2.php
17
+ - api2/index.php
18
+ - api2/.htaccess
19
+ related:
20
+ - ../features/per-client-database-connections.md
21
+ - ../../dbchanges2/workflows/client-onboarding.md
22
+ - ../../dbchanges2/architecture.md
23
+ ---
24
+
25
+ ## Summary
26
+
27
+ The full dependency chain required to run a 2.0 client app **through the browser**,
28
+ end-to-end, against a **local `api2`** (e.g. pointing the `toga2-view` frontend at a laptop
29
+ `api2` instead of beta/prod). This is separate from CLI/script backend testing — it exercises
30
+ the web SAPI, the Apache-supplied environment, Core-driven client-DB registration, and the
31
+ `/v2/auth/login` browser auth gate, none of which the CLI path touches. It cost hours to
32
+ reverse-engineer; the notes below are platform-general (any 2.0 client app), with Rate used
33
+ only as the running example.
34
+
35
+ **Bottom line:** most local failures here are a **provisioning gap in the local stack**
36
+ (missing/stale DB, unregistered origin, unseeded Core), *not* an app bug. The confusing
37
+ `Route.php:525` "failed to determine how to render view" and stale-Core "missing model file"
38
+ fatals below both mean "the local stack isn't fully stood up."
39
+
40
+ > Cost/benefit note from the originating session: because standing the full local backend up
41
+ > is this involved, the team's chosen shortcut for front-end work was to **deploy api2 to beta
42
+ > and point the local frontend at beta** rather than rebuild the entire local backend stack.
43
+
44
+ ## Steps / requirements
45
+
46
+ ### 1. ENVIRONMENT comes from Apache for the web app (not the CLI)
47
+
48
+ - `_Environment::initialize()` reads `getenv('ENVIRONMENT')` (`_underscore/Environment.php:11`)
49
+ and **throws if unset**. `_Config` then loads `Config/<ENVIRONMENT>.ini` (`Config.php:38`).
50
+ - The **web app** gets `ENVIRONMENT` from **Apache** — a `SetEnv ENVIRONMENT <name>` in the
51
+ api2 vhost — *not* from your shell/CLI environment. Dev convention:
52
+ `ENVIRONMENT=dev-<name>-laptop` → loads `Config/dev-<name>-laptop.ini`.
53
+ - To see what the **web** app actually resolves (the frontend URL/console won't show it — api2
54
+ is a separate host), temporarily add to the top of `api2/index.php`:
55
+ `if (isset($_GET['__env'])) die(getenv('ENVIRONMENT'));` and hit `http://api2/?__env=1`.
56
+ Remove it afterward.
57
+
58
+ ### 2. api2 collapses the environment name to a slug
59
+
60
+ - `V2.php:146`: `$this->environment = (substr($name,0,4)=='dev-') ? 'dev' : $name`. So **any**
61
+ `dev-*` INI resolves to the slug **`dev`** for downstream lookups (notably Core DB host
62
+ selection, below).
63
+
64
+ ### 3. Client DB connections are Core-driven, not from the INI
65
+
66
+ - `_Database::registerClientDatabases($environment, $clientId)` (`_underscore/Database.php:56`)
67
+ SELECTs from **Core**, joining `Clients → Databases → DatabaseHosts → Environments(slug=<env>)`
68
+ to resolve the client's **client, log, AND archive** databases (all three INNER-JOINed — a
69
+ missing row for any one drops the whole registration).
70
+ - For local, Core must have an `Environments.slug='dev'` row plus `DatabaseHosts` rows pointing
71
+ all three DBs at `localhost`. A prod Core backup **already carries a `dev`→localhost row by
72
+ design** — do not assume it's missing.
73
+ - There are **two** log connections: `DB_LOGS` (base framework `Logs`) and `DB_CLIENT_LOGS`
74
+ (per-tenant `Logs_<tenant>`). See
75
+ [per-client database connections](../features/per-client-database-connections.md) for the
76
+ three-alias mechanism and the logs-DB write trap.
77
+
78
+ ### 4. Browser login `POST /v2/auth/login` — requirements in the order code checks them
79
+
80
+ Checked in `V2.php` (~lines 485–800):
81
+
82
+ 1. **POST with an `Origin` header.** A browser address-bar **GET** has neither → silent
83
+ fallthrough (no auth, no useful error). Requests must come from the frontend (fetch/XHR),
84
+ not a typed URL.
85
+ 2. **`Core.Domains` row for the port-stripped origin.** `V2.php:98-103` strips the port, so
86
+ `http://rate.togaview:5173` matches a `Core.Domains` row for `http://rate.togaview`. That
87
+ row's `clientId`/`appId` must resolve to the local client/app.
88
+ 3. **Active login user** must exist in `Client_<tenant>.Users`.
89
+ 4. **All four databases must exist WITH SCHEMAS:** `Client_<tenant>`, `Logs_<tenant>`,
90
+ `Archive_<tenant>`, and base `Logs`. An **empty** database is not enough — every request
91
+ writes a transaction log via `$log->save()` at `execute()` finalization (`V2.php:2138`) into
92
+ `Logs_<tenant>.Api` / `Logs.Api`. Structure-only imports of the log/archive DBs are fine.
93
+ 5. **`Core.Apis` must be seeded** — API auth uses `Apis.uuid`/`Apis.secret`. `Apis` is
94
+ deliberately **excluded from blanks/backups**, so a freshly restored Core often has it empty.
95
+
96
+ ## Gotchas / known issues
97
+
98
+ - **The silent `Route.php:525` error** — `"Failed to determine how to render view for route
99
+ '/v2/...'"` (thrown at `_underscore/Route.php:525`) means the API handler returned **no**
100
+ response, so `_Route` fell through to view rendering. It is thrown during bootstrap
101
+ **outside** api2's JSON error handler, so the browser shows raw HTML and often **nothing** is
102
+ written to the PHP error log. The real cause is almost always a **local DB/registration gap**
103
+ (missing DB or schema, unresolved client-DB registration, or the auth gate being skipped per
104
+ step 4.1) — treat it as "the local stack isn't fully provisioned," not a request/app defect.
105
+
106
+ - **A STALE local Core silently 500s every api2 request.** api2's V2 loops **every**
107
+ `Core.Records` model at request time (`$thisModelName::TABLE`, `V2.php:3442-3446`). If local
108
+ Core is missing recent migrations it still references removed models and **fatals on a missing
109
+ model file** — a confusing error that looks unrelated to the DB and breaks *all* requests.
110
+ Concrete example: `dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql` is **non-idempotent**
111
+ (fixed record ids 317–322, RecordFields 2171–2200 — cannot be re-run) and its Section 9
112
+ **DELETES** Core `Records` row 41 (`ItemFulfillmentPackages`, model
113
+ `\_Model_Client_ItemFulfillmentPackage`), consolidating it into
114
+ `ItemFulfillments_TrackingNumbers` (record 317). A local Core missing this migration still has
115
+ record 41, so V2 fatals on the removed `_Model_Client_ItemFulfillmentPackage` file.
116
+ **Fix:** bring local Core current — apply the missing Core migrations in date order, or refresh
117
+ Core wholesale. Do not re-run a single non-idempotent migration in isolation.
118
+
119
+ ## Change history
120
+
121
+ - 2026-07-09 — Documented the full local browser-run dependency chain (Apache `ENVIRONMENT`,
122
+ `dev-*`→`dev` slug collapse, Core-driven client-DB registration, `/v2/auth/login` browser gate,
123
+ the `Route.php:525` silent-fallthrough gotcha, and the stale-Core "missing model file" fatal),
124
+ discovered while trying to run `toga2-view` against a local `api2`. (mhammontree)
@@ -17,7 +17,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
17
17
 
18
18
  ## 2.0 framework
19
19
 
20
- - **_underscore** (_Underscore) _(framework core)_ — 26 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
20
+ - **_underscore** (_Underscore) _(framework core)_ — 27 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
21
21
  - **worker2** (Worker) — 27 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
22
22
  - **api2** (API) — 10 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
23
23
  - **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.296",
3
+ "version": "1.0.298",
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",